2006年06月07日

不知出处,也不知是哪位牛人分析整理的,不过倒是可以从中看出很多东西。推荐一下。

在asp.net中用excel导出
 在.net中excel导出有多种方法。比较常用的有一下几种:
1.用OWC(Office Web Components)
 首先要在项目->添加引用->COM中添加Microsoft Office Web Components ,然后定义一个OWC.SpreadsheetClass 如:Dim xlsheet As New OWC.SpreadsheetClass 然后就可以向sheet中添加数据了,使用xlsheet.Cells(2, 2) = "表格填充内容" 格式添加数据,使用xlsheet.Range("B3:F3") 设置单元格的样式。
 最后用xlsheet.ActiveSheet.Export(Path, OWC.SheetExportActionEnum.ssExportActionNone) 的方式将excel文件保存在硬盘上。
 这种方法优点是无需系统权限分配,缺点是对于单元格的格式属性设置不够丰富,某些属性无法设置或者是设置比较麻烦。
2.用Excel Application
 用这种方法之前首先要给aspnet用户设置权限,否则程序会不能执行的。设置权限的过程如下:
 在运行处运行“dcomcnfg.exe”启动“分布式COM配置属性”
 在应用程序列表框中双击“Microsoft Excel 应用程序”
 选择“安全性”标签
 选择“使用自定义访问权限”单选框,点击“编辑”按钮
 添加本地“ASPNET”用户,权限为“允许访问”
 在“安全性”标签中选择“使用自定义启动权限”单选框,点击“编辑”按钮
 添加本地“ASPNET”用户,权限为“允许访问”
 以上工作在需要执行excel导出的服务器上进行一次即可。
 以后在每个工程中要选择“项目->添加引用中选择“COM”标签,添加“Microsoft Excel x.x Object Library”到 项目中,以后就可以执行excel导出操作了。
 在需要导出的时候首先定义一下excel application、excel Workbooks、excel Worksheets变量:
Dim excelApp As Excel.Application
Dim excelBook As Excel.Workbook
Dim excelSheet As Excel.Worksheet
excelApp = New Excel.Application
excelBook = excelApp.Workbooks.Add
excelSheet = excelBook.Worksheets(1)
之后就可以将数据放入excel Sheet中了,而且可以方便的设置单元格的各个属性参数。比如,设置行高可以用excelSheet.Rows(intRowIndex).RowHeight = dblRowHeight
用这种方法可以实现几乎全部的excel功能,如果对于某个功能不知道用excel application如何实现,可以在excel中通过“记录宏-执行操作-查看宏代码”的方法得到实现该功能所用到的属性和方法。
最后要将数据保存到硬盘上,只需用excelBook.SaveAs(FileName)方法即可。
这种方法的优点是功能强大,可以实现几乎所有的excel功能。缺点是需要权限配置,而且运行时间比较长,占用资源比较高,结束后可能在系统中有残留的EXCEL进程。
3.直接存为文本文件
直接将数据按照html表格的格式存为文本文件,扩展名改成xls就可以用excel打开,或者按照Office XP之后支持的xml格式,这种格式所能实现的功能同样强大,缺点就是需要知道各种功能或者excel单元格格式的xml表达方法,工作量比较大。
这种方法优点是不需要权限配置(只需能够向硬盘写文件的权限就可以),执行速度快。缺点是需要知道大量的excel格式的xml或者html表达方式,前期准备工作量大。

作者:郭斌 时间:2003年10月
作者手机:13571878390
作者项目:Syn-Data
负责人名称: 韦敏宗
负责人Email: hansmail@sina.com

Syn-Data基于Internet数据协同会议系统是利用最新流媒体通信技术实现的一个具有实时多点语音、视频、数据通讯综合应用能力的系统。它不仅实现基于Internet的具有多人音频、多人视频、共享电子白板、共享文档等交流手段的实时数据协同、实时讨论的功能,而且实现单机的课件录制,全程录制回放等功能,具有对带宽要求低,高质量音频视频,与Web无缝集成,强的抗网络拥塞丢包能力和支持防火墙和应用代理等能力。它使用户可以利用普通的PC机、标准的视频采集设备、耳机和麦克风,实现基于Internet的实时数据协同、实时讨论。Syn-Data致力于有效地节约时间和经费,提高了用户的工作效率,使任何人不受时间、地点的限制,可充分享受网络时代给大家带来的实时交互式多媒体通信服务。使用Syn-Data,终端用户可以实现共享演示文档、应用程序、网页及桌面等,同时提供音、视频的无缝嵌入。

BlastTeam:该文为Blast Tean技术原创文章之一。因为是一系列文章,故文章描述是以章节来描述的。BlastTeam最终将把所有的技术原创文章汇编成册。
概述
在Web程序开发中,图形和图表是很好的数据表现形式。往往是表格中的大量数据让用户产生无法处理,无从获取所需要的信息。而以图表方式提供数据的话就可以达到简单清爽,简单易懂并且一目了然的效果。利用图表,管理人员可以方便的掌握销售与生产数据,从而做出相应的判断。
在ASP.NET Web程序中,生成图表有以下的选择:
● ASP.NET内建的图形库(GDI+,位于System.Drawing 命名空间)
使用简单的图形,GDI+能够创建饼图、柱状图、线性图。但是这种方法过于底层,不适于创建复杂的图形。
● 各种ASP.NET图表控件
网上有一些ASP.NET的图表控件,但许多控件非常昂贵,而且与最常用的图表应用Excel差别很大。
● Office Web Components
Microsoft Office Web Components 包含在 Microsoft Office 2000 中。它们是用于向 Web 页添加电子表格、图表和数据处理功能的 ActiveX 控件的集合。可以直接在浏览器中处理数据,并以图表的形式显示数据。由于其基于Office的强大功能,以及与Office相同的用户界面,无疑是ASP.NET中图表生成的明智选择。
本章主要包括以下几个方面内容:
● 基于GDI+的图表生成。
● 基于Office Web Components的图表生成。
● 基于Office Web Components的报表生成。
经过本章的学习,将掌握ASP.NET中统计图表的实现,利用图表将网站的数据完美呈现给用户。
方案设计
本节分别介绍GDI+与Office Web Components的具体使用,为后面的章节打下基础。
使用GDI+
GDI+ 是 GDI(Windows XP之前版本提供的图形设备接口)的后续版本。GDI+ 是一种应用程序编程接口 (API),负责在屏幕和打印机上显示信息。
在ASP.NET中,利用GDI+可以创建简单的柱状图和饼图。
ASP.NET页面GDI+图像使用
在完整的生成柱状图和饼图之前,先从简单的ASP.NET页面图像使用开始。
在System.Draw命名空间中包含了创建、编辑图像的所有类。创建图像时,主要使用Bitmap类和Graphics类。为了便于理解,可以把Bitmap类想象成画板,Graphics类想象成画笔。Bitmap类主要用于创建画板以及在完成后图像的保存。Graphics类主要用来绘制图像、图形和线条。
首先使用下面的代码创建画板。(本小节所有代码需要添加System.Drawing命名空间与System.Drawing.Image命名空间)
Bitmap myPalette = new Bitmap(600, 400); //创建600*400的画板
有了画板,还需要创建Graphics类的实例来创建画笔,指定画板。代码如下
Graphics myGraphics = Graphics.FromImage(myPalette);
有了画笔和画板,只要使用Graphics类中的各种方法在画板上绘制图像、图形和线条就可以了。
Graphics类中的方法分为两类:绘制方法与填充方法。例如,DrawRectangle方法与FillRectangle方法。绘制方法仅仅绘制出图形的轮廓,而填充方法绘制出图形的轮廓同时填充图形的内部。
下面的代码绘制了简单的图形:
int width=150,height=50;
// 创建黑色背景椭圆
myGraphics.FillEllipse(new SolidBrush(Color.Black), 300, 150, width, height);
// 创建蓝色背景椭圆
myGraphics.FillEllipse(new SolidBrush(Color.LightBlue), 300, 150, width – 10, height – 10);
// 创建输出文本
string textOut = "ASP.NET";
//指定字体
Font fontOut = new Font("Times New Roman", 16, FontStyle.Bold|FontStyle.Italic);
//指定文本居中
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
// 绘制文本
myGraphics.DrawString(textOut,fontOut,new SolidBrush(Color.Black),new Rectangle(0,0,width,height),stringFormat);
图已经画好了,只剩下将图保存下来。要将保存的图在网页中显示,可以在下面两种方式中任选其一:
● 将图像保存在服务器的文件系统中,使用HTML的<img>标记来显示。
● 直接将图像的二进制流输出到Response对象的输出流。
两种方式各有千秋。第一种方式适合于创建不需要变动的图片(例如网站广告图片),能够创建一次,满足今后的所有要求。但会在服务器端保留下临时文件。第二种方式适用于动态的创建图片供页面显示,不能满足今后的需要,却也不会留下临时文件。
下面分别写出这两种方式的代码:
● 保存文件方式代码:
保存文件方式首先需要在Aspx文件中添加<Img>标记
<!– 指定显示文件为tmpFile.jpg –>
<img src = “tmpFile.jpg”>
然后在代码中保存文件即可:
//将文件保存为当前页面所在目录下的tmpFile.jpg
myPalette.Save(Server.MapPath("")+@"\tmpFile.jpg",ImageFormat.Jpeg);
● 直接输出方式代码:
//直接将图片以二进制流的方式输出到Response对象的输出流。
myPalette.Save(Response.OutputStream, ImageFormat.Jpeg);
将代码加入页面的PageLoad事件中即可得到如图18-1效果。
      
       图18-1 GDI+图像使用效果
创建柱状图
在知道了如何使用GDI+创建简单图像并显示到页面后,创建复杂一些的柱状图就非常自然了。
使用简单的FillRectangle,DrawRectangle,DrawString方法,即可实现基本的柱状图。代码如下:
//初始化Bitmap类实例与Graphics类实例准备画图
const int width = 600, height = 400;
Bitmap myPalette = new Bitmap(width,height);
Graphics myGraphics = Graphics.FromImage(myPalette);
// 绘制白色背景
myGraphics.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height);
// 设定显示数据
string [] DataName = {"Jan","Feb","Mar","Apr","May","Jun"};
int [] Data = {100,20,50,60,240,20};
//设定显示颜色
Color [] myColors = {
Color.Blue,
Color.Red,
Color.Yellow,
Color.Purple,
Color.Orange,
Color.Brown
};
//绘制柱图
for(int i = 0;i<DataName.Length;i++)
{
//填充柱图
myGraphics.FillRectangle(new SolidBrush(myColors[i]),(i*40)+30,300 – Data[i],20,Data[i]+5);
//绘制柱图边界
myGraphics.DrawRectangle(new Pen(Color.Black),(i*40)+30,300 – Data[i],20,Data[i]+5);
//绘制柱图上方数据
myGraphics.DrawString(Data[i].ToString(), new Font("宋体", 9), Brushes.Black, new PointF((i*40)+30,300-Data[i]-20));
//绘制柱图下标
myGraphics.DrawString(DataName[i], new Font("宋体", 9), Brushes.Black, new PointF((i*40)+30,320));
}
//输出图像
myPalette.Save(Response.OutputStream, ImageFormat.Jpeg);
// 清除所用绘图对象
myGraphics.Dispose();
myPalette.Dispose();
将上述代码写入页面CodeBehind文件的PageLoad事件中即可看到如图18-2效果:
       
          图18-2 GDI+柱状图效果
使用Office Web Components
Office Web Components是用于向 Web 页添加电子表格、图表和数据处理功能的 ActiveX控件的集合。
利用Office Web Components可以方便的来绘制简单的柱状图与饼图。不必像在GDI+中需要考虑画图的细节。
使用Office Web Components要求
使用Office Web Components需要系统中装有Office2000以上版本。在工程的引用中添加Office Web Components即可。
具体步骤如下:
在解决方案资源管理器的引用文件夹上单击鼠标右键选择添加引用,在出现的添加引用对话框中选择COM标签,选择Microsoft Office Web Components,双击选中,点击确定即可。图18-3、18-4显示了该过程。
       
            图18-3 添加引用           图18-4 添加Office Web Components引用

使用Office Web Components绘制柱状图
使用Office Web Components绘制图表不同于直接使用GDI+,主要的工作从绘图的细节转移到对于图表的设置。步骤如下:
● Step1 创建ChartSpace对象来放置图表
ChartSpace是用来放置图表的类,图表完成后用它来输出。
OWC.ChartSpace objCSpace = new OWC.ChartSpaceClass ();
● Step2 使用ChartSpace对象的Add方法创建图表
ChartSpace的Add方法创建图表,参数表示所创建图表的索引。
OWC.WCChart objChart = objCSpace.Charts.Add (0);
● Step3 指定图表的类型
通过设定Chart类对象的Type属性来指定图表的类型。
objChart.Type = OWC.ChartChartTypeEnum.chChartTypeColumnClustered;
本例创建柱状图,选择了chChartTypeColunmClustered类型。
其他的类型包括:chChartTypeArea 面积图、chChartTypeBarClustered 条形图、chChartTypePie 饼图、chChartType RadarLine 雷达线图、chChartTypeSmoothLine 平滑曲线图、chChartTypeDoughnut 环形图等等。
图18-5至18-10显示了部分图表的简单实例。
       
         图18-5 曲线图          图18-6 条形图          图18-7 面积图
       
         图18-8 环形图           图18-9 饼图          图18-10 雷达图
● Step4 设定图示说明
图示说明主要包括图例(用颜色表示数据类型)、图题(图表的标题)、XY轴的数据说明(一般用来说明各轴上的数据单位)。设定代码如下:
//指定图表是否需要图例
objChart.HasLegend = true;

//给定标题
objChart.HasTitle = true;
objChart.Title.Caption= "上半年月收入图";

//给定x,y轴的图示说明
objChart.Axes[0].HasTitle = true;
objChart.Axes[0].Title.Caption = "万元";
objChart.Axes[1].HasTitle = true;
objChart.Axes[1].Title.Caption = "月份";
● Step5 添加数据
添加数据主要设定Chart类对象的SeriesCollection属性。首先使用SeriesCollection的Add方法创建一组数据。然后使用SetData方法具体添加数据。代码如下:
(注意:数据的格式是以’\t’间隔的字符串)
//添加一组图表数据
objChart.SeriesCollection.Add(0);
//给定该组数据的名字
objChart.SeriesCollection[0].SetData (OWC.ChartDimensionsEnum.chDimSeriesNames,
+ (int)OWC.ChartSpecialDataSourcesEnum.chDataLiteral,”上半年收入”);
//给定数据分类
objChart.SeriesCollection[0].SetData (OWC.ChartDimensionsEnum.chDimCategories,
+ (int)OWC.ChartSpecialDataSourcesEnum.chDataLiteral,
"Jan"+’\t’+"Feb"+’\t’+"Mar"+’\t’+"Apr"+’\t’+"May"+’\t’+"Jun"+’\t’ );
//给定数据值
objChart.SeriesCollection[0].SetData (OWC.ChartDimensionsEnum.chDimValues,
(int)OWC.ChartSpecialDataSourcesEnum.chDataLiteral,
"100"+’\t’+"20"+’\t’+"50"+’\t’+"60"+’\t’+"240"+’\t’+"20"+’\t’);
● Step6 显示数据
显示数据是使用Chart类对象的ExportPicture方法将生成的图表创建为图片,然后显示的。代码如下:
//输出成GIF文件,参数为文件名、格式、图片大小
objCSpace.ExportPicture(Server.MapPath("")+@"\tmpFile.gif", "GIF", 400, 300);
//从生成的图片创建Bitmap对象,输出到Response输出流
Bitmap myPalette = new Bitmap(Server.MapPath("")+@"\tmpFile.gif",true);
myPalette.Save(Response.OutputStream,System.Drawing.Imaging.ImageFormat.Gif);
经过了以上的步骤,将代码输入页面的PageLoad事件代码段中,运行程序就可以得到如图18-11效果。
        
        图18-11 Office Web Components柱状图示例
使用Office Web Components绘制饼图
绘制饼图与绘制柱状图的区别不是很大,需要注意的是饼图没有XY轴,所以不能设置XY轴的图示说明。在绘制柱状图的代码上去掉关于XY轴图示说明的代码即可。
完整代码如下:
//初始化图表数据
string [] DataName = {"Jan","Feb","Mar","Apr","May","Jun"};
int [] Data = {100,20,50,60,240,20};
//声明存储数据分类和数据值的字符串
string strDataName = "";
string strData = "";
//循环按格式生成存储数据分类与数据值的字符串
for(int i=0;i< Data.Length;i++)
{
strCategory += DataName[i]+’\t’;
strValue += Data[i].ToString()+’\t’;
}
//创建ChartSpace对象来放置图表
OWC.ChartSpace objCSpace = new OWC.ChartSpaceClass ();

//在ChartSpace对象中添加图表,Add方法返回chart对象
OWC.WCChart objChart = objCSpace.Charts.Add (0);

//指定图表的类型为饼图
objChart.Type = OWC.ChartChartTypeEnum.chChartTypePie;
//指定图表是否需要图例
objChart.HasLegend = true;
//给定标题
objChart.HasTitle = true;
objChart.Title.Caption= "上半年月收入图";

//添加一个series
objChart.SeriesCollection.Add(0);

//给定数据分类
objChart.SeriesCollection[0].SetData (OWC.ChartDimensionsEnum.chDimCategories,
+ (int)OWC.ChartSpecialDataSourcesEnum.chDataLiteral,strDataName);

//给定数据值
objChart.SeriesCollection[0].SetData (OWC.ChartDimensionsEnum.chDimValues,
(int)OWC.ChartSpecialDataSourcesEnum.chDataLiteral,strData);
//输出成GIF文件.
objCSpace.ExportPicture(Server.MapPath("")+@"\tmpFile.gif", "GIF", 400, 300);
Bitmap myPalette = new Bitmap(Server.MapPath("")+@"\tmpFile.gif",true);
myPalette.Save(Response.OutputStream,System.Drawing.Imaging.ImageFormat.Gif);
将上述代码写入页面文件的PageLoad事件处理代码中,运行得到如图18-12效果。
         
        图18-12 Office Web Components饼图示例
公司盈利状况统计
前面介绍了Office Web Components的情况,本节将通过公司盈利状况统计来介绍Office Web Components柱状图在具体实例中的使用。
数据库设计
为了实现公司盈利状况统计。首先,需要设计相关的数据库表。在实际情况中,公司的盈利就是收入的总和与支出的总和之差。而每笔收入与支出都是在一次交易中完成的。基于以上考虑,同时为了实现简单。设计数据库表单如下:
表名 T_Deal 别名 交易表
表项      说明   类型     是否可空
DealTime   交易时间  DateTime     否
DealIncome  交易金额  int       否
DealContent  交易内容  Varchar(50)   否
DealObject  交易对象  Varchar(20)   是
Comment    备注    Varchar(50)   是
注意:为了简单起见,用交易金额统一表示收入与支出。用正数的交易金额表示收入,负数的交易金额表示支出。这样计算盈利时只需将交易金额求和即可。
按照上表的格式在SqlServer中创建表。添加样例数据供程序使用。
然后创建下面的存储过程来实现公司盈利状况的统计。
CREATE PROCEDURE sp_SelectIncome
@year int
AS

Select sum(DealCount),Datepart(mm,DealTime) From T_Deal
where DATEPART(yy,DealTime)=@year
Group By Datepart(mm,DealTime)
GO
该存储过程选择交易时间的年份与输入参数@year相等的数据,并将选择得到的数据按交易时间的月份分组。返回每组交易金额的总和以及该组的交易月份。
界面设计
新建Web工程WebChart。将WebForm1.aspx改名为OWCChart.aspx。同时,将其代码文件中的类名改为OWCChart。
界面如图18-13所示:
      
          图18-13 公司盈利统计界面
该界面主要包括两部分:一个PlaceHolder控件用来放置生成的图表构成显示图表部分。一个用来选择统计年份的DropDownList控件,一个用来确定生成图表的Button控件,一个用来显示错误信息的Label控件构成了图表的生成部分。
同样为了界面整洁使用Table将所用控件放在表格中。按照图18-13安排好界面后就可以为控件设置属性了,主要包括以下几个方面的设置:
● PlaceHolder控件的设置
PlaceHolder控件在本程序中用于放置生成的图表,只需设置其Id属性为ChartPlaceHolder即可。
● DropDownList控件的设置
DropDownList控件在本程序中用于选择统计年份,设置其Id属性为ddlYear。并为其添加ListItem 2000,2001,2002,2003。
<asp:ListItem Value="2000">2000</asp:ListItem>
<asp:ListItem Value="2001">2001</asp:ListItem>
<asp:ListItem Value="2002">2002</asp:ListItem>
<asp:ListItem Value="2003">2003</asp:ListItem>
● Button控件的设置
Button控件设置Id属性为btnSumbit,Text属性为“确定”即可。
● Label控件的设置
Label控件设置Id属性为Info即可。
代码实现
在完成了上面的界面设计后,就要进入后台代码的编写了。下面就将分模块介绍各部分功能的代码实现。
数据读入
为了显示公司盈利状况统计,首先需要将统计的结果从数据库中读入。读入数据的代码包括以下两部分。
● 创建数据库联接
为了方便程序的配置,将数据库联接字段保存在Web.Config文件中。如此一来,在需要修改数据库联接字段时只需要修改Web.Config文件即可,不需要修改程序代码,重新编译。
在Web.Config文件中的<configuration>字段中加入如下代码:
<appSettings>
<add key="ConnectStr"
value="server=localhost;uid=yourid;pwd=yourpas;database=Info;"/>
</appSettings>
其中localhost要改为自己的数据库服务器名,uid,pwd分别为数据库用户id和密码,database为数据库名。
设置好联接字段后就可在代码中对其进行引用了。
主要使用AppSettingReader对象的GetValue方法获取”ConnectStr”字段信息,该方法需要两个参数,第一个指定要引用的字段,第二个参数指定需要的类型。返回一个Object类型的变量。需要使用Convert的ToString方法将其转换为字符串类型
引用代码如下:
//全局数据库联接
System.Data.SqlClient.SqlConnection MyConnection;
private void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
if(Page.IsPostBack==false) // 页面首次加载时
{

//创建Web.Config文件应用程序设置字段读取对象
System.Configuration.AppSettingsReader ConnectionString = new System.Configuration.AppSettingsReader();
//创建数据库联接
MyConnection = new System.Data.SqlClient.SqlConnection();
//设置数据库联接的连接字段
MyConnection.ConnectionString = Convert.ToString(ConnectionString.GetValue("ConnectStr",System.Type.GetType("System.String")));
}
}
● 利用数据库联接读取数据
有了前面创建的数据库联接就可以从数据库中读取数据了。
读取数据代码如下:
//读取数据函数,参数为需要统计的年份,返回DataSet
DataSet ReadData(string Year)
{
//创建DataSet
DataSet dsIncome = new DataSet();
//设置存储过程名
string SpName = "sp_SelectIncome";
//创建Sql命令
SqlCommand IncomeCmd = new SqlCommand(SpName,MyConnection);
//设定数据库命令类型为存储过程
IncomeCmd.CommandType = CommandType.StoredProcedure;
//打开数据库联接
MyConnection.Open();
//创建并设定存储过程参数
SqlParameter IncomeYear = new SqlParameter("@year",SqlDbType.Int,4);
IncomeYear.Value=Convert.ToInt16(Year);
//为Sql命令添加参数
IncomeCmd.Parameters.Add(IncomeYear);
//创建Sql数据桥接器
SqlDataAdapter adapter = new SqlDataAdapter(SpName,MyConnection);
//指定桥接器Sql命令
adapter.SelectCommand = IncomeCmd;
//填充DataSet
adapter.Fill(dsIncome,"Income");
//关闭数据库联接
MyConnection.Close();
}
数据显示
使用ReadData方法读出数据,接下来就是显示数据了。显示数据同样需要两个步骤。
● 处理数据库读出的数据
由于从数据库读出的数据不能完全满足显示的要求,有可能某个月份没有交易,那么返回的DataSet中就不会包含该月的数据。但显示时需要所有月份的数据。所以直接显示数据之前先要对数据进行加工。代码如下:
//创建存储数据的数组,MyMonthIncome存放每月数据,MyMonthName存放月份缩写
int[] MyMonthIncome = new int[12];
string[] MyMonthName = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

//声明存放显示用字符串的变量,strMonthName存放月份信息,strMonthIncome存放数据
string strMonthName = "";
string strMonthIncome = "";
//对存在数据的月份将数据保存在MyMonthName
for(int i=0;i< dsIncome.Tables["Income"].Rows.Count;i++)
{
MyMonthIncome[Convert.ToInt16(dsIncome.Tables["Income"].Rows[i][1])-1] = Convert.ToInt16(dsIncome.Tables["Income"].Rows[i][0]);
}
//用已有的数据来生成图表显示所需的字符串
for(int i=0;i< 12;i++)
{
strMonthName += MyMonthName[i]+’\t’;
strMonthIncome += MyMonthIncome[i].ToString()+’\t’;
}
● 使用Office Web Components显示数据
经过上一步骤,图表显示所需的字符串已经放入了strMonthName和strMonthIncome中,下面使用18.2节中关于Office Web Components的知识就可以大功告成了。需要注意的是在生成图片后使用了PlaceHolder的.Controls属性的Add方法将动态生成的<img>标签放入页面。为了方便后面的使用,将显示数据的代码写为函数。代码如下:
private void MakeLineChart(string Year)
{
//使用ReadData函数读出数据
DataSet dsIncome = ReadData(Year);
//以下插入处理数据库读出的数据部分的代码,不再重复
//….上一步骤中处理数据库读出数据代码

//创建ChartSpace对象来放置图表
OWC.ChartSpace mySpace = new OWC.ChartSpaceClass ();
//在ChartSpace对象中添加图表,Add方法返回chart对象
OWC.WCChart myChart = mySpace.Charts.Add (0);

//指定图表的类型为线性图
myChart.Type = OWC.ChartChartTypeEnum.chChartTypeLine;
//指定图表是否需要图例
myChart.HasLegend = true;
//给定标题
myChart.HasTitle = true;
myChart.Title.Caption= ddlYear.SelectedItem.Text + "月收入图";
//给定x,y轴的图示说明
myChart.Axes[0].HasTitle = true;
myChart.Axes[0].Title.Caption = "万元";
myChart.Axes[1].HasTitle = true;
myChart.Axes[1].Title.Caption = "月份";
//添加一个series
myChart.SeriesCollection.Add(0);
//给定series的名字
myChart.SeriesCollection[0].SetData (OWC.ChartDimensionsEnum.chDimSeriesNames,
+ (int)OWC.ChartSpecialDataSourcesEnum.chDataLiteral, strSeriesName);
//给定分类
myChart.SeriesCollection[0].SetData (OWC.ChartDimensionsEnum.chDimCategories,
+ (int)OWC.ChartSpecialDataSourcesEnum.chDataLiteral, strCategory);
//给定值
myChart.SeriesCollection[0].SetData (OWC.ChartDimensionsEnum.chDimValues,
(int)OWC.ChartSpecialDataSourcesEnum.chDataLiteral, strValue);
//输出成GIF文件.
string strAbsolutePath = (Server.MapPath(".")) + @"\images\tempFile.gif";
mySpace.ExportPicture(strAbsolutePath, "GIF", 700, 350);
//创建GIF文件的相对路径.
string strRelativePath = "./images/tempFile.gif”
//生成显示图片的<img>标签
string strImageTag = "<IMG SRC=’" + strRelativePath + "’/>";
//把图片添加到placeholder.
ChartPlaceHolder.Controls.Add(new LiteralControl(strImageTag));
18.3.3.3 按钮点击事件
有了前面的MakeLineChart函数,在aspx页面的设计模式下双击btnSumbit创建按钮点击事件。修改代码如下:
private void btnSubmit_Click(object sender, System.EventArgs e)
{
MakeLineChart(ddlYear.SelectedItem.Text);
}
至此,公司盈利状况统计功能完全实现,运行结果如图18-16:
        
         图18-16 公司盈利状况统计效果
公司收入分块图
上一节介绍了Office Web Components的柱状图的使用,实现了对公司盈利状况统计。本节将通过公司收入分块图的实现来介绍Office Web Components中饼装图的使用。
数据库设计
为了实现公司收入分块图,为T_Deal表增添交易类别字段。扩充后的T_Deal表如下:
表名 T_Deal 别名 交易表
表项      说明   类型     是否可空
DealTime   交易时间  DateTime     否
DealIncome  交易金额  int       否
DealContent  交易内容  Varchar(50)   否
DealObject  交易对象  Varchar(20)   是
DealCategory 交易类别  Varchar(20)   否
Comment     备注   Varchar(50)   是
按照新表的格式重新在SqlServer中创建表。添加样例数据供程序使用。并且创建如下存储过程实现公司收入分块统计。
CREATE PROCEDURE sp_SelectCategory
@year int
AS

Select sum(DealCount),DealCategory From T_Deal
where DATEPART(yy,DealTime)=@year
Group By DealCategory
GO
该存储过程选择交易时间的年份与输入参数@year相等的数据,并将选择得到的数据按交易类别分组。返回每组交易金额的总和以及该组的交易类别。
界面设计
为了简便同时利用已有成果,在现有的基础上修改无疑是最好的选择。因此,本节的程序直接在上一节的程序上修改。在原有界面上添加一个DropDownList来选择不同的图表内容。新的界面如图18-17:
      
         图18-17 公司收入分布界面
新增的DropDownList属性设置为Id=ddlChartType,并为其添加Item
<asp:ListItem Value="Income">公司盈利状况</asp:ListItem>
<asp:ListItem Value="Category">公司收入分块图</asp:ListItem>
代码实现
在完成了上面的界面设计后,就要进入后台代码的编写了。由于是在上一节的基础上,所以只需增加新功能。
数据读入
由于已经有了创建数据库联接部分,下面只需要读出数据即可。数据读入部分代码与上一节的代码相似,不同之处只是所调用的存储过程不同,为了简化代码,修改ReadData函数,为其增加参数SpName 指定调用的存储过程。将原有程序中声明SpName的语句删除即可。
//读取数据函数,参数Yesr为需要统计的年份SpName为调用存储过程名,返回DataSet
DataSet ReadData(string Year,string SpName)
{
//创建DataSet
DataSet dsIncome = new DataSet();
//设置存储过程名,通过新增参数完成,将此句去除
//string SpName = "sp_SelectIncome";
//以下部分不做修改,不再重复

}
注意:修改ReadData函数后,在上一节MakeLineChart函数中调用ReadData的代码需要为其增加参数。
private void MakeLineChart(string Year)
{
//使用ReadData函数读出数据
DataSet dsIncome = ReadData(Year,“sp_SelectIncome”);
//以下部分不做修改

}
数据显示
使用ReadData方法读出数据,接下来就是显示数据了。显示数据的代码也只需对上一节代码做部分修改即可。声明绘制分类收入的函数,将MakeLineChart函数内容复制并修改如下:
private void MakePieChart(string Year)
{
//使用ReadData函数读出数据
DataSet dsCategory = ReadData(Year);
//创建存储数据的数组,MyCategory存放类别数据,MyMonthName存放类别名称
int[] MyCategory = new int[dsIncome.Tables["Income"].Rows.Count];
string[] MyCategoryName = new string[dsIncome.Tables["Income"].Rows.Count];

//声明存放显示用字符串的变量,strMonthName存放月份信息,strMonthIncome存放数据
string strCategoryName = "";
string strCategory = "";
//声明总收入,用来计算各分类收入百分比
int IncomeSum = 0;
//将数据放入数组,同时计算总收入
for(int i=0;i< dsIncome.Tables["Income"].Rows.Count;i++)
{
MyCategory[i] = Convert.ToInt16(dsIncome.Tables["Income"].Rows[i][0]);
MyCategoryName[i] = Convert.ToString(dsIncome.Tables["Income"].Rows[i][1]);
IncomeSum += MyCategory[i];
}
//用已有的数据来生成图表显示所需的字符串
for(int i=0;i< dsIncome.Tables["Income"].Rows.Count;i++)
{
//计算出各分类所占百分比
int PercentCategory= 100*MyCategory[i]/IncomeSum;
strCategory += MyCategoryName[i]+" "+PercentCategory.ToString()+ "%"+’\t’;
strValue += MyCategory[i].ToString()+’\t’;
}
//下面代码与上一节基本相同不再重复
//仅给出需要修改的部分

//指定图表的类型为线性图 改为饼图
myChart.Type = OWC.ChartChartTypeEnum.chChartTypePie;
//指定图表是否需要图例
myChart.HasLegend = true;
//给定标题
myChart.HasTitle = true;
myChart.Title.Caption= ddlYear.SelectedItem.Text + "收入图";
//给定x,y轴的图示说明 ,去掉XY轴图示说明部分
/*
myChart.Axes[0].HasTitle = true;
myChart.Axes[0].Title.Caption = "万元";
myChart.Axes[1].HasTitle = true;
myChart.Axes[1].Title.Caption = "月份";
*/

}
18.3.3.3 按钮点击事件
由于增加了新功能,按钮点击的代码修改如下:
private void btnSubmit_Click(object sender, System.EventArgs e)
{
//根据ddlChartType的选项判断执行的内容
if(ddlChartType.SelectedItem.Value=="Income")
MakeLineChart(ddlYear.SelectedItem.Text); //显示公司盈利统计
else if(ddlChartType.SelectedItem.Value=="Category")
MakePieChart(ddlYear.SelectedItem.Text);//显示收入分布
}
至此,公司盈利状况统计功能完全实现,运行后选择收入分布点击确定按钮。结果如图18-18:
       
        图18-18 公司收入分块图效果
报表产出
前面的几个小节完成了对数据的统计并以图表的形式呈现给用户,使用户能够一目了然的获取所需信息。这一节将把统计结果以Excel报表的形式输出,以免用户提交报表时手工输入的麻烦,提高工作效率。
报表产出主要使用了Office Web Components的Spreadsheet控件。利用该控件创建并编辑数据表格,并将最终结果输出到Excel报表。步骤如下:
● Step1创建SpreadsheetClass对象,用来放置数据表格
类似于Chart控件中的ChartSpace,Spread控件中也需要创建一个放置SpreadsheetClass的对象作为单个表的容器。
SpreadsheetClass exl = new SpreadsheetClass();
● Step2利用SpreadsheetClass的ActiveSheet获得当前处于活动状态的表格
一个SpreadsheetClass对象就相当于一个运行中的Excel,通过其ActiveSheet属性能够得到当前处于活动状态的表格。一个Worksheet对象就相当于Excel中的一张表格。将SpreadsheetClass对象的ActiveSheet属性赋值给Worksheet对象。就可以在后面的代码中对表格进行操作了。
OWC.Worksheet ws = exl.ActiveSheet;
● Step3 编辑表格
编辑表格使用的是Worksheet对象的Cells属性。Cells属性就代表着表格中的每个单元格。使用Cells[row,col]的格式引用,row表示行号,col表示列号。需要注意的是行号和列号都是从1开始的,而不是像C#的数组是从0开始。例如Cells[2,2]就表示表格的第二行,第二列的单元格。下面是产生盈利状况统计报表的编辑表格代码(该段代码添加在MakePieChart函数末尾,其中的变量延用之前的声明):
//为表格设定标题
ws.Cells[1, 1] = "收入(万元)";
ws.Cells[1,2] = "类别";
ws.Cells[1,3] = "所占比重";
//将数据写入表格
for(int i=0;i<dsIncome.Tables[0].Rows.Count;i++)
{
ws.Cells[i+2,1] = MyCategory[i].ToString();
ws.Cells[i+2,2] = MyCategoryName[i];
int Percent = MyMonthIncome[i]*100/IncomeSum;
ws.Cells[i+2,3] = Percent.ToString() + "%";
}
//将总收入写入表格
ws.Cells[dsIncome.Tables[0].Rows.Count + 2,1] = IncomeSum.ToString();
ws.Cells[dsIncome.Tables[0].Rows.Count + 2,2] = "总和";
ws.Cells[dsIncome.Tables[0].Rows.Count + 2,3] = "100%";
Step4 导出表格
导出表格使用Worksheet对象的Export方法,将表格导出为Excel文件即可。考虑到可能出现异常,使用了try,catch来捕获可能出现的异常,输出错误信息。
try
{
//输出Excel报表到服务器文件系统
ws.Export(Server.MapPath(" ") + @"\Excel\report.xls", OWC.SheetExportActionEnum.ssExportActionNone);
}
catch
{
//错误时输出错误提示
Info.Text = "保存报表错误,请与管理员联系";
}
将上述代码添加到MakePieChart函数的末尾,重新运行,在工程所在文件夹下的子Excel目录中就会有report.xls文件。其结果如图18-19。
       
        图18-19 生成报表结果图
小结
这一章先对ASP.NET中生成统计图表进行了介绍,然后对GDI+与Office Web Components的使用进行了详细说明,为后面做好了知识储备。随后利用公司盈利状况统计和公司收入分块图两个实例详细的介绍了如何利用Office Web Components与数据库结合绘制统计图表以及生成报表。
通过本章的学习,读者对于.NET中统计报表的生成有了深刻的理解,相信精美、清晰的报表一定会为成为Web程序的亮点。






 

2006年06月02日

This page contains sample ADO connection strings for ODBC DSN / DSN-Less,
OLE DB Providers, Remote Data Services (RDS), MS Remote, MS DataShape.

Also included are ADO.NET connection strings for each .NET Managed Provider
(SQLClient, OLEDB, and ODBC).

These sample connection strings are compiled
by Carl Prothman, a Microsoft Visual Basic MVP
Enjoy!

 
Table of Contents
ODBC DSN Connections DSN
File DSN


ODBC DSN-Less Connections  ODBC Driver for AS/400
ODBC Driver for Access
ODBC Driver for dBASE
ODBC Driver for Excel
ODBC Driver for MySQL
ODBC Driver for Oracle
ODBC Driver for Paradox
ODBC Driver for SQL Server
ODBC Driver for Sybase
ODBC Driver for Sybase SQL Anywhere
ODBC Driver for Text
ODBC Driver for Teradata
ODBC Driver for Visual FoxPro


OLE DB Data Link Connections Data Link File (UDL)


OLE DB Data Provider Connections  OLE DB Provider for AS/400
OLE DB Provider for Active Directory Service
OLE DB Provider for DB2
OLD DB Provider for Internet Publishing
OLE DB Provider for Index Server
OLE DB Provider for Microsoft Jet
OLE DB Provider for ODBC Databases
OLE DB Provider for Oracle (From Microsoft)
OLE DB Provider for Oracle (From Oracle)
OLE DB Provider for Simple Provider
OLE DB Provider for SQL Server


Remote Data Service (RDS) Connections RDS Data Control – Connect Property
RDS Data Control – URL Property


ADO URL Connections ADO Recordset


MS Remote Provider Connections MS Remote – Access (Jet)
MS Remote – SQL Server


Data Shape Provider Connections  MS DataShape – SQL Server


.NET Managed Provider Connections SQL Client .NET Managed Provider (System.Data.SqlClient)
OLE DB .NET Managed Provider (System.Data.OleDb)
ODBC .NET Managed Provider (System.Data.ODBC)


 


ODBC DSN Connections
Using an ODBC DSN (Data Source Name) is a two step process.

1) You must first create the DSN via the "ODBC Data Source Administrator" program
found in your computer’s Control Panel (or Administrative Tools menu in Windows 2000).
Make sure to create a SYSTEM DSN (not a USER DSN) when using ASP.
Note: You can also create the DSN via VB code.

2) Then use the following connection string – with your own DSN name of course.  ;-)

ODBC – DSN
 
oConn.Open "DSN=AdvWorks;" & _
          "Uid=Admin;" & _
          "Pwd=;


You can also create and use a File DSN. Then use the following ADO Connection string:

ODBC – File DSN
 
oConn.Open "FILEDSN=c:\somepath\mydb.dsn;" & _
          "Uid=Admin;" & _
          "Pwd=;"

For more information, see: About ODBC data sources and
How to Use File DSNs and DSN-less Connections

Note: The problem with DSN is that Users can (and will) modify them (or delete by mistake),
then your program won’t work so well… So it’s better to use a DSN-Less or OLE DB Provider
connection string with a Trusted Connection if possible!



ODBC DSN-Less Connections
ODBC Driver for AS/400
 
oConn.Open "Driver={Client Access ODBC Driver (32-bit)};" & _
          "System=myAS400;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

For more information, see:  A Fast Path to AS/400 Client/Server



ODBC Driver for Access
 
For Standard Security:

oConn.Open "Driver={Microsoft Access Driver (*.mdb)};" & _
          "Dbq=c:\somepath\mydb.mdb;" & _
          "Uid=Admin;" & _
          "Pwd=;"

If you are using a Workgroup (System database):

oConn.Open "Driver={Microsoft Access Driver (*.mdb)};" & _
          "Dbq=c:\somepath\mydb.mdb;" & _
          "SystemDB=c:\somepath\mydb.mdw;", _
          "admin", ""

If MDB is located on a network share:

oConn.Open "Driver={Microsoft Access Driver (*.mdb)};" & _
          "Dbq=\\myServer\myShare\myPath\myDb.mdb;"

For more information, see: Microsoft Access Driver Programming Considerations



ODBC Driver for dBASE
 
oConn.Open "Driver={Microsoft dBASE Driver (*.dbf)};" & _
         "DriverID=277;" & _
         "Dbq=c:\somepath;"

Note: Specify the filename in the SQL statement. For example:
     oRs.Open "Select * From user.dbf", oConn, , ,adCmdText

Note: MDAC 2.1 (or greater) requires the Borland Database Engine (BDE) to update dBase DBF files. (Q238431).

For more information, see: dBASE Driver Programming Considerations



ODBC Driver for Excel
 
oConn.Open "Driver={Microsoft Excel Driver (*.xls)};" & _
          "DriverId=790;" & _
          "Dbq=c:\somepath\mySpreadsheet.xls;" & _
          "DefaultDir=c:\somepath;"

For more information, see: Microsoft Excel Driver Programming Considerations



ODBC Driver for MySQL (via MyODBC)
 
To connect to a local database

oConn.Open "Driver={mySQL};" & _
          "Server=MyServerName;" & _
          "Option=16834;" & _
          "Database=mydb;"

To connect to a remote database

oConn.Open "Driver={mySQL};" & _
          "Server=db1.database.com;" & _
          "Port=3306;" & _
          "Option=131072;" & _
          "Stmt=;" & _
          "Database=mydb;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

For more information, see: Programs Known to Work with MyODBC



ODBC Driver for Oracle
 
For the current Oracle ODBC Driver from Microsoft:

oConn.Open "Driver={Microsoft ODBC for Oracle};" & _
          "Server=OracleServer.world;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

For the older Oracle ODBC Driver from Microsoft:

oConn.Open "Driver={Microsoft ODBC Driver for Oracle};" & _
          "ConnectString=OracleServer.world;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

For more information, see: Connection String Format and Attributes



ODBC Driver for Paradox
 
oConn.Open "Driver={Microsoft Paradox Driver (*.db)};" & _
          "DriverID=538;" & _
          "Fil=Paradox 5.X;" & _
          "DefaultDir=c:\dbpath\;" & _
          "Dbq=c:\dbpath\;" & _
          "CollatingSequence=ASCII;"

Note: MDAC 2.1 (or greater) requires the Borland Database Engine (BDE) to update Paradox ISAM fDBF files. (Q230126).

For more information, see: Paradox Driver Programming Considerations



ODBC Driver for SQL Server
 
For Standard Security:

oConn.Open "Driver={SQL Server};" & _
          "Server=MyServerName;" & _
         "Database=myDatabaseName;" & _
         "Uid=myUsername;" & _
          "Pwd=myPassword;"

For Trusted Connection security:

oConn.Open "Driver={SQL Server};" & _
          "Server=MyServerName;" & _
          "Database=myDatabaseName;" & _
          "Uid=;" & _
          "Pwd=;"

‘ or

oConn.Open "Driver={SQL Server};" & _
          "Server=MyServerName;" & _
          "Database=myDatabaseName;" & _
          "Trusted_Connection=yes;"

To Prompt user for username and password

oConn.Properties("Prompt") = adPromptAlways
oConn.Open "Driver={SQL Server};" & _
          "Server=MyServerName;" & _
          "DataBase=myDatabaseName;"
  

For more information, see: SQLDriverConnect (ODBC)



ODBC Driver for Sybase
 
If using the Sybase System 11 ODBC Driver:

oConn.Open "Driver={SYBASE SYSTEM 11};" & _
          "Srvr=myServerName;" & _
         "Uid=myUsername;" & _
          "Pwd=myPassword;"

If using the Intersolv 3.10 Sybase ODBC Driver:

oConn.Open "Driver={INTERSOLV 3.10 32-BIT Sybase};" & _
          "Srvr=myServerName;" & _
         "Uid=myUsername;" & _
          "Pwd=myPassword;"

For more information, see: Sybase System 10 ODBC Driver Reference Guide



ODBC Driver for Sybase SQL Anywhere
 
oConn.Open "ODBC; Driver=Sybase SQL Anywhere 5.0;" & _
          "DefaultDir=c:\dbpath\;" & _
          "Dbf=c:\sqlany50\mydb.db;" & _
         "Uid=myUsername;" & _
          "Pwd=myPassword;"
          "Dsn="""";"

Note: Including the DSN tag with a null string is absolutely critical or else you get the dreaded -7778 error.

For more information, see: Sybase SQL Anywhere User Guide



ODBC Driver for Teradata
 
oConn.Open "Provider=Teradata;" & _
          "DBCName=MyDbcName;" & _
          "Database=MyDatabaseName;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

For more information, see Teradata ODBC Driver



ODBC Driver for Text
 
oConn.Open "Driver={Microsoft Text Driver (*.txt; *.csv)};" & _
          "Dbq=c:\somepath\;" & _
          "Extensions=asc,csv,tab,txt;" & _
          "Persist Security Info=False"

Note: Specify the filename in the SQL statement. For example:

oRs.Open "Select * From customer.csv", _
        oConn, adOpenStatic, adLockReadOnly, adCmdText


For more information, see: Text File Driver Programming Considerations



ODBC Driver for Visual FoxPro
 
With a database container:

oConn.Open "Driver={Microsoft Visual FoxPro Driver};" & _
          "SourceType=DBC;" & _
          "SourceDB=c:\somepath\mySourceDb.dbc;" & _
          "Exclusive=No;"

Without a database container (Free Table Directory):

oConn.Open "Driver={Microsoft Visual FoxPro Driver};" & _
          "SourceType=DBF;" & _
          "SourceDB=c:\somepath\mySourceDbFolder;" & _
          "Exclusive=No;"


For more information, see: Visual FoxPro ODBC Driver and Q165492




OLE DB Data Link Connections
Data Link File (UDL)
 
For Absolute Path:

oConn.Open "File Name=c:\somepath\myDatabaseName.udl;"

For Relative Path:

oConn.Open "File Name=myDatabaseName.udl;"
 

For more information, see: HOWTO: Use Data Link Files with ADO

Note: Windows 2000 no longer contains the "New | Microsoft Data Link" menu 
anymore. You can add the Data Link menu back in the menu list by running the
"C:\Program Files\Common Files\System\Ole DB\newudl.reg" reg file,
then right-click on the desktop and select "New | Microsoft Data Link" menu. 
Or you can also create a Data Link file by creating a text file and change it’s
file extension to ".udl", then double-click the file.




OLE DB Provider Connections
OLE DB Provider for AS/400
 
oConn.Open "Provider=IBMDA400;" & _
          "Data source=myAS400;"
          "User Id=myUsername;" & _
          "Password=myPassword;"

For more information, see:  A Fast Path to AS/400 Client/Server



OLE DB Provider for Active Directory Service
 
oConn.Open "Provider=ADSDSOObject;" & _
          "User Id=myUsername;" & _
          "Password=myPassword;"
 

For more information, see: Microsoft OLE DB Provider for Microsoft Active Directory Service



OLE DB Provider for DB2
 
oConn.Open = "Provider=DB2OLEDB;" &
           "Network Transport Library=TCPIP;" &
           "Network Address=MyServer;" & _
           "Package Collection=MyPackage;" &
           "Host CCSID=1142"
           "Initial Catalog=MyDB;" &
           "User ID=MyUsername;" & _
           "Password=MyPassword;"

For more information, see: OLE DB Provider for DB2
and INF: Configuring Data Sources for the Microsoft OLE DB Provider for DB2



OLE DB Provider for Index Server
 
oConn.Open "Provider=msidxs;" & _
          "Data source=MyCatalog;"
 

For more information, see: Microsoft OLE DB Provider for Microsoft Indexing Service



OLE DB Provider for Internet Publishing
 
oConn.Open "Provider=MSDAIPP.DSO;" & _
      "Data Source=http://mywebsite/myDir;" & _
      "User Id=myUsername;" & _
      "Password=myPassword;"
 

For more information, see: Microsoft OLE DB Provider for Internet Publishing and Q245359



OLE DB Provider for Microsoft Jet
 
For standard security:

oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
          "Data Source=c:\somepath\myDb.mdb;" & _
          "User Id=admin;" & _
          "Password=;"

If using a Workgroup (System Database):

oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
          "Data Source=c:\somepath\mydb.mdb;" & _
          "Jet OLEDB:System Database=MySystem.mdw;", _
          "admin", ""

Note, remember to convert both the MDB and the MDW to the 4.0 database format when using the 4.0 OLE DB Provider.

If MDB has a database password:

oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
          "Data Source=c:\somepath\mydb.mdb;" & _
          "Jet OLEDB:Database Password=MyDbPassword;", _
          "admin", ""

If MDB is located on a network share:

oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
          "Data Source=\\myServer\myShare\myPath\myDb.mdb;

If want to open up the Access database exclusively:

oConn.Mode = adModeShareExclusive
oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
          "Data Source=c:\somepath\myDb.mdb;" & _
          "User Id=admin;" & _
          "Password=;"


For more information, see: OLE DB Provider for Microsoft Jet, Q191754, Q225048, Q239114, and Q271908



You can also open an Excel Spreadsheet using the "OLE DB Provider for Microsoft Jet"

oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
          "Data Source=c:\somepath\myExcelSpreadsheet.xls;" & _
          "Extended Properties=""Excel 8.0;HDR=Yes;"";"

Where "HDR=Yes" means that there is a header row in the cell range
(or named range), so the provider will not include the first row of the
selection into the recordset. If "HDR=No", then the provider will include
the first row of the cell range (or named ranged) into the recordset.

For more information, see: Q278973


You can also open a Text File using the "OLE DB Provider for Microsoft Jet"

oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
          "Data Source=c:\somepath\;" & _
          "Extended Properties=""text;HDR=Yes;FMT=Delimited;"";"

‘ Then open a recordset based on a select on the actual file
oRs.Open "Select * From MyTextFile.txt", oConn, adOpenStatic, adLockReadOnly, adCmdText

For more information, see: Q262537



OLE DB Provider for ODBC Databases
 
For Access (Jet):

oConn.Open "Provider=MSDASQL;" & _
          "Driver={Microsoft Access Driver (*.mdb)};" & _
          "Dbq=c:\somepath\mydb.mdb;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"
For SQL Server:

oConn.Open "Provider=MSDASQL;" & _
          "Driver={SQL Server};" & _
          "Server=myServerName;" & _
          "Database=myDatabaseName;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

For more information, see: Microsoft OLE DB Provider for ODBC



OLE DB Provider for Oracle (from Microsoft)
 
oConn.Open "Provider=msdaora;" & _
          "Data Source=MyOracleDB;" & _
          "User Id=myUsername;" & _
          "Password=myPassword;"

For more information, see: Microsoft OLE DB Provider for Oracle



OLE DB Provider for Oracle (from Oracle)
 
For Standard Security:

oConn.Open "Provider=OraOLEDB.Oracle;" & _
          "Data Source=MyOracleDB;" & _
          "User Id=myUsername;" & _
          "Password=myPassword;"

For a Trusted Connection:

oConn.Open "Provider=OraOLEDB.Oracle;" & _
          "Data Source=MyOracleDB;" & _
          "User Id=/;" & _
          "Password=;"
‘ Or

oConn.Open "Provider=OraOLEDB.Oracle;" & _
          "Data Source=MyOracleDB;" & _
          "OSAuthent=1;"
 

Note: "Data Source=" must be set to the appropriate Net8 name which is known to the naming method in use. For example, for Local Naming, it is the alias in the tnsnames.ora file; for Oracle Names, it is the Net8 Service Name.

For more information, see: Connecting to an Oracle Database
(Note, if you get a Logon dialog, then click Cancel, then perform a one-time free signup with Oracle’s TechNet system)



OLE DB Provider for Simple Provider
 
The Microsoft OLE DB Simple Provider (OSP) allows ADO to access any data for which a provider has
been written using the OLE DB Simple Provider Toolkit. Simple providers are intended to access data
sources that require only fundamental OLE DB support, such as in-memory arrays or XML documents.

OSP in MDAC 2.6 has been enhanced to support opening hierarchical ADO Recordsets over arbitrary
XML files. These XML files may contain the ADO XML persistence schema, but it is not required. This
has been implemented by connecting the OSP to the MSXML2.DLL, therefore MSXML2.DLL or newer is
required.

oConn.Open "Provider=MSDAOSP;" & _
          "Data Source=MSXML2.DSOControl.2.6;"

oRS.Open "http://WebServer/VirtualRoot/MyXMLFile.xml", oConn


For more information, see: Microsoft OLE DB Simple Provider



OLE DB Provider for SQL Server
 
For Standard Security:

oConn.Open "Provider=sqloledb;" & _
          "Data Source=myServerName;" & _
          "Initial Catalog=myDatabaseName;" & _
          "User Id=myUsername;" & _
          "Password=myPassword;"

For a Trusted Connection:

oConn.Open "Provider=sqloledb;" & _
          "Data Source=myServerName;" & _
          "Initial Catalog=myDatabaseName;" & _
          "Integrated Security=SSPI;"

To connect to a "Named Instance" (SQL Server 2000)

oConn.Open "Provider=sqloledb;" & _
          "Data Source=myServerName\Inst2;" & _
          "Initial Catalog=myDatabaseName;" & _
          "User Id=myUsername;" & _
          "Password=myPassword;"

To Prompt user for username and password:

oConn.Provider = "sqloledb"
oConn.Properties("Prompt") = adPromptAlways
oConn.Open "Data Source=myServerName;" & _
          "Initial Catalog=myDatabaseName;"

To connect via an IP address:

oConn.Open "Provider=sqloledb;" & _
          "Data Source=xxx.xxx.xxx.xxx,1433;" & _
          "Network Library=DBMSSOCN;" & _
          "Initial Catalog=myDatabaseName;" & _
          "User ID=myUsername;" & _
          "Password=myPassword;"

Note:
- xxx.xxx.xxx.xxx is an IP address
- "Network Library=DBMSSOCN" tells OLE DB to use TCP/IP rather than Named Pipes (Q238949)
- 1433 is the default port number for SQL Server
- You can also add "Encrypt=yes" for encryption

For more information, see: Microsoft OLE DB Provider for SQL Server




Remote Data Service (RDS) Connections
The following examples show how to connect to a remote database using the RDS Data Control.
When using the RDS DataControl’s Server/SQL/Connect properties, the RDS DataControl uses the
RDS DataFactory on the remote server. If you use the RDS DataControl’s URL property,
then the RDS DataFactory is not used at all.

WARNING: The RDS DataFactory can be a major security hole if not setup and configured correctly!
For more information, see RDS FAQ #24

RDS DataControl – Connect Property
 
With the RDS default handler disabled (not recommend due to security risks):

With oRdc
  .Server = "http://carl2"
  .Sql = "Select * From Authors Where State = ‘CA’"
  .Connect = "Provider=sqloledb;" & _
           "Data Source=(local);" & _
           "Initial Catalog=pubs;" & _
           "User Id=sa;" & _
           "Password=;"
  .Refresh
End With

With the RDS default handler enabled (recommend):

With oRdc
  .Server = "http://carl2"
  .Handler = "MSDFMAP.Handler"
  .Connect = "Data Source=MyConnectTag;"
  .Sql = "MySQLTag(""CA"")"
  .Refresh
End With

The corresponding CONNECT and SQL sections in the default handler \WINNT\MSDFMAP.INI file would be:

[connect MyConnectTag]
Access = ReadWrite
Connect = "Provider=sqloledb;Data Source=(local);Initial Catalog=pubs;User Id=sa;Password=;"

[sql MySQLTag]
Sql = "Select * From Authors Where State = ‘?’"

For more information about the RDS Default Handler, see:
Q243245, Q230680, and RDS Customization Handler Microsoft articles


RDS DataControl – URL Property
 
To get records from a remote database:

With oRdc
  .URL = "http://carlp0/Authors_GetByState.asp?state=CA"
  .Refresh
End With

To save, set the URL property to an ASP web page:

With oRdc
  .URL = "http://carlp0/rdsdatacontrol/Authors_Save.asp"
  .SubmitChanges
End With

For more information, see: RDS URL Property




ADO URL Connections
ADO 2.5+ allows you to open up a Recordset based on XML returned from an ASP file over HTTP. 
This feature doesn’t use RDS at all.

ADO Recordset
 
To get records from a remote database:

oRs.Open "http://carlp0/Authors_GetByState.asp?state=CA", , _
                      adOpenStatic, adLockBatchOptimistic

To save changes, you must use the MSXML’s XMLHTTP object to POST back the updated XML. 
The Recordset’s Update and UpdateBatch methods will not work in this case.

‘ Save Recordset into Stream
Set oStm = New ADODB.Stream
oRs.Save oStm, adPersistXML

‘ Use MSXML’s XMLHTTP object to open ASP and post a XML stream
Set oXMLHTTP = New MSXML2.XMLHTTP30
oXMLHTTP.Open "POST", "http://carlp0/Authors_Save.asp", False
oXMLHTTP.Send oStm.ReadText

‘ If an error occurred
If oXMLHTTP.Status = 500 Then
  Debug.Print oXMLHTTP.statusText
End If

For more information, see: ADO Recordset’s Open Method




MS Remote Provider Connections
The following connections strings use Microsoft’s remote provider (MS Remote). The MS Remote
provider tells ADO to communicate with the remote server (via the RDS DataFactory) and to use
the remote provider that is installed on the remote server.

WARNING: The RDS DataFactory can be a major security hole if not setup and configured correctly! 
For more information, see RDS FAQ #24
  

MS Remote – Access (Jet)
 
If you want to use an ODBC DSN on the remote machine:

oConn.Open "Provider=MS Remote;" & _
          "Remote Server=http://myServerName;" & _
          "Remote Provider=MSDASQL;" & _
          "DSN=AdvWorks;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

If you want to use an OLE DB Provider on the remote machine:

oConn.Open "Provider=MS Remote;" & _
          "Remote Server=http://myServerName;" & _
          "Remote Provider=Microsoft.Jet.OLEDB.4.0;" & _
          "Data Source=c:\somepath\mydb.mdb;", _
          "admin", ""

If you want to use an OLE DB Provider on the remote machine (via RDS DataFactory Default Handler):

oConn.Open "Provider=MS Remote;" & _
          "Remote Server=http://myServerName;" & _
          "Handler=MSDFMAP.Handler;" & _
          "Data Source=MyAdvworksConn;"

The corresponding entry in the \winnt\Msdfmap.ini file would be:

[connect MyAdvworksConn]
Access = ReadWrite
Connect = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=mydb.mdb;" & _
        "User Id=admin;" & _
        "Password=;"
 

MS Remote – SQL Server
 
If you want to use an ODBC DSN on the remote machine:

oConn.Open "Provider=MS Remote;" & _
          "Remote Server=http://myServerName;" & _
          "Remote Provider=MSDASQL;" & _
          "DSN=myDatabaseName;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

If you want to use an OLE DB Provider on the remote machine:

oConn.Open "Provider=MS Remote;" & _
          "Remote Server=http://myServerName;" & _
          "Remote Provider=SQLOLEDB;" & _
         "Data Source=myServerName;" & _
         "Initial Catalog=myDatabaseName;" & _
         "User ID=myUsername;" & _
          "Password=myPassword;"

If you want to use an OLE DB Provider on the remote machine (via RDS DataFactory Default Handler):

oConn.Open "Provider=MS Remote;" & _
          "Remote Server=http://myServerName;" & _
          "Handler=MSDFMAP.Handler;" & _
          "Data Source=MyPubsConn;"

The corresponding entry in the \winnt\Msdfmap.ini file would be:

[connect MyPubsConn]
Access = ReadWrite
Connect = "Provider=SQLOLEDB;" & _
        "Data Source=myServerName;" & _
        "Initial Catalog=myDatabaseName;" & _
        "User ID=myUsername;" & _
        "Password=myPassword;"

For more information, see: Microsoft OLE DB Remoting Provider  and Q240838




Data Shape Provider Connections
MS DataShape – SQL Server
 
oConn.Open "Provider=MSDataShape;" & _
          "Data Provider=SQLOLEDB;" & _
          "Data Source=mySQLServerName;" & _
          "Initial Catalog=myDatabase;" & _
          "User ID=myUsername;" & _
          "Password=myPassword;"

Then use a Shape command with SQL strings:
sSQL = "SHAPE {select * from authors} " & _
      "APPEND ({select * from titleauthor} AS chapter " & _
      "RELATE au_id TO au_id)"

Or use a Shape command that calls Stored Procedures:
sSQL = "SHAPE {exec spAuthors_LoadAll} " & _
      "APPEND ({exec spTitleAuthor_LoadAll} AS chapter " & _
      "RELATE au_id TO au_id)"

For more information, see: Microsoft Data Shaping Service for OLE DB and Q288409




.NET Managed Provider Connections
SQL Client .NET Managed Provider (System.Data.SqlClient)
 
The SQL Client .NET Managed Provide allows you to connect to a Microsoft SQL Server 7.0
or 2000 database. For Microsoft SQL Server 6.0 or earlier, use the OLE DB .NET Data Provider
with the "SQL Server OLE DB Provider" (SQLOLEDB).

Dim oSQLConnection As SqlClient.SqlConnection
Dim sConnString As String

sConnString = "Data Source=(local);" & _
           "Initial Catalog=NorthWind;" & _
           "Integrated Security=SSPI;" & _
           "Pooling=True;" & _
           "Min Pool Size=10;" & _
           "Max Pool Size=50;" & _
           "Connection Lifetime=30;" & _
           "Connection Reset=True;" & _
           "Enlist=True;"
oSQLConnection = New SqlClient.SqlConnection(sConnString)
oSQLConnection.Open()

For more information, see: System.Data.SQL Namespace and .NET Data Providers
Note: ‘SQL’ namespace got renamed to ‘SQLClient’



OLE DB .NET Managed Provider (System.Data.OleDb)
 
The OLE DB .NET Data Provider uses native OLE DB through COM interop to enable data access. 
To use the OLE DB .NET Data Provider, you must also use an OLE DB provider (e.g. SQLOLEDB,
MSDAORA, or Microsoft.JET.OLEDB.4.0).

For SQL Server OLE DB Provider (for SQL Server 6.0 or earlier)

Dim oOleDbConnection As OleDb.OleDbConnection
Dim sConnString As String

sConnString = "Provider=sqloledb;" & _
           "Data Source=myServerName;" & _
           "Initial Catalog=myDatabaseName;" & _
           "User Id=myUsername;" & _
          "Password=myPassword;"
oOleDbConnection = New OleDb.OleDbConnection(sConnString)
oOleDbConnection.Open()

For JET OLE DB Provider:

Dim oOleDbConnection As OleDb.OleDbConnection
Dim sConnString As String

sConnString = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
           "Data Source=C:\Work\nwind.mdb;" & _
           "User ID=Admin;" & _
           "Password="";"
oOleDbConnection = New OleDb.OleDbConnection(sConnString)
oOleDbConnection.Open()

For more information, see: System.Data.OleDb Namespace and .NET Data Providers
Note: ‘ADO’ namespace got renamed to ‘OleDb’



ODBC .NET Managed Provider (System.Data.ODBC)
 
The ODBC .NET Data Provider is an add-on component to the .NET Framework SDK Beta 2.
It provides access to native ODBC drivers the same way the OLE DB .NET Data Provider
provides access to native OLE DB providers.

For SQL Server ODBC Driver:

Dim oODBCConnection As Odbc.OdbcConnection
Dim sConnString As String

‘ Create and open a new ODBC Connection
sConnString = "Driver={SQL Server};" & _
           "Server=MySQLServerName;" & _
           "Database=MyDatabaseName;" & _
           "Uid=MyUsername;" & _
           "Pwd=MyPassword;"

oODBCConnection = New Odbc.OdbcConnection(sConnString)
oODBCConnection.Open()

For Oracle ODBC Driver:

Dim oODBCConnection As Odbc.OdbcConnection
Dim sConnString As String

‘ Create and open a new ODBC Connection
sConnString = "Driver={Microsoft ODBC for Oracle};" & _
          "Server=OracleServer.world;" & _
          "Uid=myUsername;" & _
          "Pwd=myPassword;"

oODBCConnection = New Odbc.OdbcConnection(sConnString)
oODBCConnection.Open()

For Access (JET) ODBC Driver:

Dim oODBCConnection As Odbc.OdbcConnection
Dim sConnString As String

‘ Create and open a new ODBC Connection
sConnString = "Driver={Microsoft Access Driver (*.mdb)};" & _
          "Dbq=c:\somepath\mydb.mdb;" & _
          "Uid=Admin;" & _
          "Pwd=;"

oODBCConnection = New Odbc.OdbcConnection(sConnString)
oODBCConnection.Open()

For all other ODBC Drivers:

Dim oODBCConnection As Odbc.OdbcConnection
Dim sConnString As String

‘ Create and open a new ODBC Connection
sConnString = "Dsn=myDsn;" & _
           "Uid=myUsername;" & _
           "Pwd=myPassword;"

oODBCConnection = New Odbc.OdbcConnection(sConnString)
oODBCConnection.Open()

2006年05月17日

查询表内容
SELECT
表名=case when a.colorder=1 then d.name else ” end,
表说明=case when a.colorder=1 then isnull(f.value,”) else ” end,
字段序号=a.colorder,
字段名=a.name,
标识=case when COLUMNPROPERTY( a.id,a.name,’IsIdentity’)=1 then ‘√’else ” end,
主键=case when exists(SELECT 1 FROM sysobjects where xtype=’PK’ and name in (
SELECT name FROM sysindexes WHERE indid in(
SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid
))) then ‘√’ else ” end,
类型=b.name,
占用字节数=a.length,
长度=COLUMNPROPERTY(a.id,a.name,’PRECISION’),
小数位数=isnull(COLUMNPROPERTY(a.id,a.name,’Scale’),0),
允许空=case when a.isnullable=1 then ‘√’else ” end,
默认值=isnull(e.text,”),
字段说明=isnull(g.[value],”)
FROM syscolumns a
left join systypes b on a.xtype=b.xusertype
inner join sysobjects d on a.id=d.id and d.xtype=’U’ and d.name<>’dtproperties’
left join syscomments e on a.cdefault=e.id
left join sysproperties g on a.id=g.id and a.colid=g.smallid
left join sysproperties f on d.id=f.id and f.smallid=0
–where d.name=’要查询的表’ –如果只查询指定表,加上此条件
order by a.id,a.colorder
========================================================
SQL交*表实例
很简单的一个东西,见网上好多朋友问“怎么实现交*表?”,以下是我写的一个例子,数据库基于SQL SERVER 2000。
建表:
在查询分析器里运行:
CREATE TABLE [Test] (
[id] [int] IDENTITY (1, 1) NOT NULL ,
[name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
[subject] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
[Source] [numeric](18, 0) NULL
) ON [PRIMARY]
GO
INSERT INTO [test] ([name],[subject],[Source]) values (N’张三’,N’语文’,60)
INSERT INTO [test] ([name],[subject],[Source]) values (N’李四’,N’数学’,70)
INSERT INTO [test] ([name],[subject],[Source]) values (N’王五’,N’英语’,80)
INSERT INTO [test] ([name],[subject],[Source]) values (N’王五’,N’数学’,75)
INSERT INTO [test] ([name],[subject],[Source]) values (N’王五’,N’语文’,57)
INSERT INTO [test] ([name],[subject],[Source]) values (N’李四’,N’语文’,80)
INSERT INTO [test] ([name],[subject],[Source]) values (N’张三’,N’英语’,100)
Go

交*表语句的实现:
–用于:交*表的列数是确定的
select name,sum(case subject when ‘数学’ then source else 0 end) as ‘数学’,
sum(case subject when ‘英语’ then source else 0 end) as ‘英语’,
sum(case subject when ‘语文’ then source else 0 end) as ‘语文’
from test
group by name

–用于:交*表的列数是不确定的

declare @sql varchar(8000)
set @sql = ’select name,’
select @sql = @sql + ’sum(case subject when ”’+subject+”’
then source else 0 end) as ”’+subject+”’,’
from (select distinct subject from test) as a
select @sql = left(@sql,len(@sql)-1) + ‘ from test group by name’
exec(@sql)
go
================================================================================
SQL Server 存储过程的分页方案比拼
出处

SQL Server 存储过程的分页,这个问题已经讨论过几年了,很多朋友在问我,所以在此发表一下我的观点
建立表:

CREATE TABLE [TestTable] (
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[FirstName] [nvarchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
[LastName] [nvarchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,
[Country] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
[Note] [nvarchar] (2000) COLLATE Chinese_PRC_CI_AS NULL
) ON [PRIMARY]
GO

 

插入数据:(2万条,用更多的数据测试会明显一些)
SET IDENTITY_INSERT TestTable ON

declare @i int
set @i=1
while @i<=20000
begin
insert into TestTable([id], FirstName, LastName, Country,Note) values(@i, ‘FirstName_XXX’,'LastName_XXX’,'Country_XXX’,'Note_XXX’)
set @i=@i+1
end

SET IDENTITY_INSERT TestTable OFF

 

————————————-

分页方案一:(利用Not In和SELECT TOP分页)
语句形式:
SELECT TOP 10 *
FROM TestTable
WHERE (ID NOT IN
(SELECT TOP 20 id
FROM TestTable
ORDER BY id))
ORDER BY ID

SELECT TOP 页大小 *
FROM TestTable
WHERE (ID NOT IN
(SELECT TOP 页大小*页数 id
FROM 表
ORDER BY id))
ORDER BY ID
————————————-
分页方案二:(利用ID大于多少和SELECT TOP分页)
语句形式:
SELECT TOP 10 *
FROM TestTable
WHERE (ID >
(SELECT MAX(id)
FROM (SELECT TOP 20 id
FROM TestTable
ORDER BY id) AS T))
ORDER BY ID

SELECT TOP 页大小 *
FROM TestTable
WHERE (ID >
(SELECT MAX(id)
FROM (SELECT TOP 页大小*页数 id
FROM 表
ORDER BY id) AS T))
ORDER BY ID

————————————-
分页方案三:(利用SQL的游标存储过程分页)
create procedure XiaoZhengGe
@sqlstr nvarchar(4000), –查询字符串
@currentpage int, –第N页
@pagesize int –每页行数
as
set nocount on
declare @P1 int, –P1是游标的id
@rowcount int
exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1,@rowcount=@rowcount output
select ceiling(1.0*@rowcount/@pagesize) as 总页数–,@rowcount as 总行数,@currentpage as 当前页
set @currentpage=(@currentpage-1)*@pagesize+1
exec sp_cursorfetch @P1,16,@currentpage,@pagesize
exec sp_cursorclose @P1
set nocount off
其它的方案:如果没有主键,可以用临时表,也可以用方案三做,但是效率会低。
建议优化的时候,加上主键和索引,查询效率会提高。

通过SQL 查询分析器,显示比较:我的结论是:
分页方案二:(利用ID大于多少和SELECT TOP分页)效率最高,需要拼接SQL语句
分页方案一:(利用Not In和SELECT TOP分页) 效率次之,需要拼接SQL语句
分页方案三:(利用SQL的游标存储过程分页) 效率最差,但是最为通用
在实际情况中,要具体分析。
====================================================================================
得到随机排序结果
出处

SELECT *
FROM Northwind..Orders
ORDER BY NEWID()

SELECT TOP 10 *
FROM Northwind..Orders
ORDER BY NEWID()
====================================================================================
select
to_char(日期,’yyyymmdd’) DATE_ID,to_char(日期,’yyyy’)||’年’||to_char(日期,’mm’)||’月’||to_char(日期,’dd’)||’日’ DATE_NAME,
to_char(日期,’yyyymm’) MONTH_ID,to_char(日期,’yyyy’)||’年’||to_char(日期,’mm’)||’月’ MONTH_NAME,
‘Q’||to_char(日期,’q.yyyy’) QUARTERID,to_char(日期,’yyyy’)||’年第’||to_char(日期,’q')||’季度’ QUARTERID_NAME,
to_char(日期,’yyyy’) YEAR_ID,to_char(日期,’yyyy’)||’年’ YEAR_NAME
from(
select to_date(‘2000-01-01′,’yyyy-mm-dd’)+(rownum-1) 日期 from user_objects where rownum<367 and to_date(‘2000-01-01′,’yyyy-mm-dd’)+(rownum-1)<to_date(‘2001-01-01′,’yyyy-mm-dd’)
);
–得到季度和月份对应关系
select distinct to_char(日期,’q') 季度,to_char(to_date(‘2001-01-01′,’yyyy-mm-dd’)+(rownum-1),’yyyymm’) 日期 from(
select to_date(‘2001-01′,’yyyy-mm’)+(rownum-1) 日期 from user_objects where rownum<367 and to_date(‘2001-01-01′,’yyyy-mm-dd’)+(rownum-1)<to_date(‘2002-01-01′,’yyyy-mm-dd’)
);
–得到一年中的天数
select to_char(to_date(‘2000-01-01′,’yyyy-mm-dd’)+(rownum-1),’yyyy-mm-dd’) 日期 from user_objects where rownum<367 and to_date(‘2000-01-01′,’yyyy-mm-dd’)+(rownum-1)<to_date(‘2001-01-01′,’yyyy-mm-dd’);
====================================================================================
获取一个数据库的所有存储过程,可以用

select * from sysobjects where type=’p’

====================================================================================
生成交*表的简单通用存储过程
出处

if exists (select * from dbo.sysobjects where id = object_id(N’[dbo].[p_qry]‘) and OBJECTPROPERTY(id, N’IsProcedure’) = 1)
drop procedure [dbo].[p_qry]
GO

/*–生成交*表的简单通用存储过程

根据指定的表名,纵横字段,统计字段,自动生成交*表
并可根据需要生成纵横两个方向的合计

注意,横向字段数目如果大于纵向字段数目,将自动交换纵横字段
如果不要此功能,则去掉交换处理部分

–邹建 204.06–*/

/*–调用示例

exec p_qry ’syscolumns’,'id’,'colid’,'colid’,1,1
–*/

create proc p_qry
@TableName sysname, –表名
@纵轴 sysname, –交*表最左面的列
@横轴 sysname, –交*表最上面的列
@表体内容 sysname, –交*表的数数据字段
@是否加横向合计 bit,–为1时在交*表横向最右边加横向合计
@是否家纵向合计 bit –为1时在交*表纵向最下边加纵向合计
as
declare @s nvarchar(4000),@sql varchar(8000)

–判断横向字段是否大于纵向字段数目,如果是,则交换纵横字段
set @s=’declare @a sysname
if(select case when count(distinct ['+@纵轴+'])<count(distinct ['+@横轴+']) then 1 else 0 end
from ['+@TableName+'])=1
select @a=@纵轴,@纵轴=@横轴,@横轴=@a’
exec sp_executesql @s
,N’@纵轴 sysname out,@横轴 sysname out’
,@纵轴 out,@横轴 out

–生成交*表处理语句
set @s=’
set @s=””
select @s=@s+”,[''+cast(['+@横轴+'] as varchar)+”]=sum(case ['+@横轴
+'] when ”””+cast(['+@横轴+'] as varchar)+””” then ['+@表体内容+'] else 0 end)”
from ['+@TableName+']
group by ['+@横轴+']‘
exec sp_executesql @s
,N’@s varchar(8000) out’
,@sql out

–是否生成合计字段的处理
declare @sum1 varchar(200),@sum2 varchar(200),@sum3 varchar(200)
select @sum1=case @是否加横向合计
when 1 then ‘,[合计]=sum(['+@表体内容+'])’
else ” end
,@sum2=case @是否家纵向合计
when 1 then ‘['+@纵轴+']=case grouping(['
+@纵轴+']) when 1 then ”合计” else cast(['
+@纵轴+'] as varchar) end’
else ‘['+@纵轴+']‘ end
,@sum3=case @是否家纵向合计
when 1 then ‘ with rollup’
else ” end

–生成交*表
exec(’select ‘+@sum2+@sql+@sum1+’
from ['+@TableName+']
group by ['+@纵轴+']‘+@sum3)
go
==========================================================================================================
利用排序规则特点计算汉字笔划和取得拼音首字母
出处


  SQL SERVER的排序规则平时使用不是很多,也许不少初学者还比较陌生,但有
一个错误大家应是经常碰到: SQL SERVER数据库,在跨库多表连接查询时,若两数据
库默认字符集不同,系统就会返回这样的错误:

“无法解决 equal to 操作的排序规则冲突。”

一.错误分析:
  这个错误是因为排序规则不一致造成的,我们做个测试,比如:
create table #t1(
name varchar(20) collate Albanian_CI_AI_WS,
value int)

create table #t2(
name varchar(20) collate Chinese_PRC_CI_AI_WS,
value int )

表建好后,执行连接查询:

select * from #t1 A inner join #t2 B on A.name=B.name

这样,错误就出现了:

服务器: 消息 446,级别 16,状态 9,行 1
无法解决 equal to 操作的排序规则冲突。
  要排除这个错误,最简单方法是,表连接时指定它的排序规则,这样错误就
不再出现了。语句这样写:

select *
from #t1 A inner join #t2 B
on A.name=B.name collate Chinese_PRC_CI_AI_WS

二.排序规则简介:

什么叫排序规则呢?MS是这样描述的:"在 Microsoft SQL Server 2000 中,
字符串的物理存储由排序规则控制。排序规则指定表示每个字符的位模式以及存
储和比较字符所使用的规则。"
  在查询分析器内执行下面语句,可以得到SQL SERVER支持的所有排序规则。

    select * from ::fn_helpcollations()

排序规则名称由两部份构成,前半部份是指本排序规则所支持的字符集。
如:
  Chinese_PRC_CS_AI_WS
前半部份:指UNICODE字符集,Chinese_PRC_指针对大陆简体字UNICODE的排序规则。
排序规则的后半部份即后缀 含义:
  _BIN 二进制排序
  _CI(CS) 是否区分大小写,CI不区分,CS区分
  _AI(AS) 是否区分重音,AI不区分,AS区分   
  _KI(KS) 是否区分假名类型,KI不区分,KS区分 
_WI(WS) 是否区分宽度 WI不区分,WS区分 

区分大小写:如果想让比较将大写字母和小写字母视为不等,请选择该选项。
区分重音:如果想让比较将重音和非重音字母视为不等,请选择该选项。如果选择该选项,
比较还将重音不同的字母视为不等。
区分假名:如果想让比较将片假名和平假名日语音节视为不等,请选择该选项。
区分宽度:如果想让比较将半角字符和全角字符视为不等,请选择该选项


三.排序规则的应用:
  SQL SERVER提供了大量的WINDOWS和SQLSERVER专用的排序规则,但它的应用往往
被开发人员所忽略。其实它在实践中大有用处。

  例1:让表NAME列的内容按拼音排序:

create table #t(id int,name varchar(20))
insert #t select 1,’中’
union all select 2,’国’
union all select 3,’人’
union all select 4,’阿’

select * from #t order by name collate Chinese_PRC_CS_AS_KS_WS
drop table #t
/*结果:
id name
———– ——————–
4 阿
2 国
3 人
1 中
*/

  例2:让表NAME列的内容按姓氏笔划排序:

create table #t(id int,name varchar(20))

insert #t select 1,’三’
union all select 2,’乙’
union all select 3,’二’
union all select 4,’一’
union all select 5,’十’
select * from #t order by name collate Chinese_PRC_Stroke_CS_AS_KS_WS
drop table #t
/*结果:
id name
———– ——————–
4 一
2 乙
3 二
5 十
1 三
*/

四.在实践中排序规则应用的扩展
  SQL SERVER汉字排序规则可以按拼音、笔划等排序,那么我们如何利用这种功能
来处理汉字的一些难题呢?我现在举个例子:

          用排序规则的特性计算汉字笔划

  要计算汉字笔划,我们得先做准备工作,我们知道,WINDOWS多国汉字,UNICODE目前
收录汉字共20902个。简体GBK码汉字UNICODE值从19968开始。
  首先,我们先用SQLSERVER方法得到所有汉字,不用字典,我们简单利用SQL语句就
可以得到:

select top 20902 code=identity(int,19968,1) into #t from syscolumns a,syscolumns b

再用以下语句,我们就得到所有汉字,它是按UNICODE值排序的:

  select code,nchar(code) as CNWord from #t

  然后,我们用SELECT语句,让它按笔划排序。

select code,nchar(code) as CNWord
from #t
order by nchar(code) collate Chinese_PRC_Stroke_CS_AS_KS_WS,code

结果:
code CNWord
———– ——
19968 一
20008 丨
20022 丶
20031 丿
20032 乀
20033 乁
20057 乙
20058 乚
20059 乛
20101 亅
19969 丁
……….

  从上面的结果,我们可以清楚的看到,一笔的汉字,code是从19968到20101,从小到大排,但到
了二笔汉字的第一个字“丁”,CODE为19969,就不按顺序而重新开始了。有了这结果,我们就可以轻
松的用SQL语句得到每种笔划汉字归类的第一个或最后一个汉字。
下面用语句得到最后一个汉字:

create table #t1(id int identity,code int,cnword nvarchar(2))

insert #t1(code,cnword)
select code,nchar(code) as CNWord from #t
order by nchar(code) collate Chinese_PRC_Stroke_CS_AS_KS_WS,code

select A.cnword
from #t1 A
left join #t1 B on A.id=B.id-1 and A.code<B.code
where B.code is null
order by A.id

得到36个汉字,每个汉字都是每种笔划数按Chinese_PRC_Stroke_CS_AS_KS_WS排序规则排序后的
最后一个汉字:

亅阝马风龙齐龟齿鸩龀龛龂龆龈龊龍龠龎龐龑龡龢龝齹龣龥齈龞麷鸞麣龖龗齾齉龘

  上面可以看出:“亅”是所有一笔汉字排序后的最后一个字,“阝”是所有二笔汉字排序后的最后
一个字……等等。
  但同时也发现,从第33个汉字“龗(33笔)”后面的笔划有些乱,不正确。但没关系,比“龗”笔划
多的只有四个汉字,我们手工加上:齾35笔,齉36笔,靐39笔,龘64笔

建汉字笔划表(TAB_HZBH):
create table tab_hzbh(id int identity,cnword nchar(1))
–先插入前33个汉字
insert tab_hzbh
select top 33 A.cnword
from #t1 A
left join #t1 B on A.id=B.id-1 and A.code<B.code
where B.code is null
order by A.id
–再加最后四个汉字
set identity_insert tab_hzbh on
go
insert tab_hzbh(id,cnword)
     select 35,N’齾’
union all select 36,N’齉’
union all select 39,N’靐’
union all select 64,N’龘’
go
set identity_insert tab_hzbh off
go

  到此为止,我们可以得到结果了,比如我们想得到汉字“国”的笔划:

declare @a nchar(1)
set @a=’国’
select top 1 id
from tab_hzbh
where cnword>=@a collate Chinese_PRC_Stroke_CS_AS_KS_WS
order by id

id
———–
8
(结果:汉字“国”笔划数为8)

  上面所有准备过程,只是为了写下面这个函数,这个函数撇开上面建的所有临时表和固
定表,为了通用和代码转移方便,把表tab_hzbh的内容写在语句内,然后计算用户输入一串
汉字的总笔划:

create function fun_getbh(@str nvarchar(4000))
returns int
as
begin
declare @word nchar(1),@n int
set @n=0
while len(@str)>0
begin
set @word=left(@str,1)
–如果非汉字,笔划当0计
set @n=@n+(case when unicode(@word) between 19968 and 19968+20901
then (select top 1 id from (
select 1 as id,N’亅’ as word
union all select 2,N’阝’
union all select 3,N’马’
union all select 4,N’风’
union all select 5,N’龙’
union all select 6,N’齐’
union all select 7,N’龟’
union all select 8,N’齿’
union all select 9,N’鸩’
union all select 10,N’龀’
union all select 11,N’龛’
union all select 12,N’龂’
union all select 13,N’龆’
union all select 14,N’龈’
union all select 15,N’龊’
union all select 16,N’龍’
union all select 17,N’龠’
union all select 18,N’龎’
union all select 19,N’龐’
union all select 20,N’龑’
union all select 21,N’龡’
union all select 22,N’龢’
union all select 23,N’龝’
union all select 24,N’齹’
union all select 25,N’龣’
union all select 26,N’龥’
union all select 27,N’齈’
union all select 28,N’龞’
union all select 29,N’麷’
union all select 30,N’鸞’
union all select 31,N’麣’
union all select 32,N’龖’
union all select 33,N’龗’
union all select 35,N’齾’
union all select 36,N’齉’
union all select 39,N’靐’
union all select 64,N’龘’
) T
where word>=@word collate Chinese_PRC_Stroke_CS_AS_KS_WS
order by id ASC) else 0 end)
set @str=right(@str,len(@str)-1)
end
return @n
end

–函数调用实例:
select dbo.fun_getbh(‘中华人民共和国’),dbo.fun_getbh(‘中華人民共和國’)
 
  执行结果:笔划总数分别为39和46,简繁体都行。

当然,你也可以把上面“UNION ALL”内的汉字和笔划改存在固定表内,在汉字
列建CLUSTERED INDEX,列排序规则设定为:
    Chinese_PRC_Stroke_CS_AS_KS_WS
这样速度更快。如果你用的是BIG5码的操作系统,你得另外生成汉字,方法一样。
但有一点要记住:这些汉字是通过SQL语句SELECT出来的,不是手工输入的,更不
是查字典得来的,因为新华字典毕竟不同于UNICODE字符集,查字典的结果会不正
确。

  
    用排序规则的特性得到汉字拼音首字母

  用得到笔划总数相同的方法,我们也可以写出求汉字拼音首字母的函数。如下:

create function fun_getPY(@str nvarchar(4000))
returns nvarchar(4000)
as
begin
declare @word nchar(1),@PY nvarchar(4000)
set @PY=”
while len(@str)>0
begin
set @word=left(@str,1)
–如果非汉字字符,返回原字符
set @PY=@PY+(case when unicode(@word) between 19968 and 19968+20901
then (select top 1 PY from (
select ‘A’ as PY,N’驁’ as word
union all select ‘B’,N’簿’
union all select ‘C’,N’錯’
union all select ‘D’,N’鵽’
union all select ‘E’,N’樲’
union all select ‘F’,N’鰒’
union all select ‘G’,N’腂’
union all select ‘H’,N’夻’
union all select ‘J’,N’攈’
union all select ‘K’,N’穒’
union all select ‘L’,N’鱳’
union all select ‘M’,N’旀’
union all select ‘N’,N’桛’
union all select ‘O’,N’漚’
union all select ‘P’,N’曝’
union all select ‘Q’,N’囕’
union all select ‘R’,N’鶸’
union all select ‘S’,N’蜶’
union all select ‘T’,N’籜’
union all select ‘W’,N’鶩’
union all select ‘X’,N’鑂’
union all select ‘Y’,N’韻’
union all select ‘Z’,N’咗’
) T
where word>=@word collate Chinese_PRC_CS_AS_KS_WS
order by PY ASC) else @word end)
set @str=right(@str,len(@str)-1)
end
return @PY
end

–函数调用实例:
select dbo.fun_getPY(‘中华人民共和国’),dbo.fun_getPY(‘中華人民共和國’)
结果都为:ZHRMGHG

  你若有兴趣,也可用相同的方法,扩展为得到汉字全拼的函数,甚至还可以得到全拼的读
音声调,不过全拼分类大多了。得到全拼最好是用对照表,两万多汉字搜索速度很快,用对照
表还可以充分利用表的索引。
排序规则还有很多其它的巧妙用法,限于篇幅在此就不再详细说明。欢迎大家共同探讨。
==================================================================================================
如何实现对数据库单个字段进行加密 选择自 callzjy 的 Blog
关键字 callzjy 字段加密 sqlserver 函数
出处

create view v_rand

as

select c=unicode(cast(round(rand()*255,0) as tinyint))

go

 

create function f_jmstr(@str varchar(8000),@type bit)returns varchar(8000)

/*

*参数说明

*str:要加密的字符串或已经加密后的字符

*type:操作类型–0加密–解密

*返回值说明

*当操作类型为加密时(type–0):返回为加密后的str,即存放于数据库中的字符串

*当操作类型为解密时(type–1):返回为实际字符串,即加密字符串解密后的原来字符串

*/

As

begin

declare @re varchar(8000)–返回值

declare @c int–加密字符

declare @i int

/*

*加密方法为原字符异或一个随机ASCII字符

*/
if @type=0–加密
begin
select @c=c,@re=”,@i=len(@str) from v_rand
while @i>0
select @re=nchar(unicode(substring(@str,@i,1))^@c^@i)+@re
,@i=@i-1
set @re=@re+nchar(@c)
end
else–解密
begin
select @i=len(@str)-1,@c=unicode(substring(@str,@i+1,1)),@re=”
while @i>0
select @re=nchar(unicode(substring(@str,@i,1))^@c^@i)+@re ,@i=@i-1
end
return(@re)
end
go
–测试
declare @tempstr varchar(20)
set @tempstr=’ 1 2 3aA’
select dbo.f_jmstr(dbo.f_jmstr(@tempstr,0),1)
输出结果
1 2 3aA
(完)
==================================================================================================
让数据库产生一张详细的日历表

也许有了这张表,你的工作会轻松很多!

CREATE TABLE [dbo].[time_dimension] (
[time_id] [int] IDENTITY (1, 1) NOT NULL ,
[the_date] [datetime] NULL ,
[the_day] [nvarchar] (15) NULL ,
[the_month] [nvarchar] (15) NULL ,
[the_year] [smallint] NULL ,
[day_of_month] [smallint] NULL ,
[week_of_year] [smallint] NULL ,
[month_of_year] [smallint] NULL ,
[quarter] [nvarchar] (2) NULL ,
[fiscal_period] [nvarchar] (20) NULL
) ON [PRIMARY]


DECLARE @WeekString varchar(12),
@dDate SMALLDATETIME,
@sMonth varchar(20),
@iYear smallint,
@iDayOfMonth smallint,
@iWeekOfYear smallint,
@iMonthOfYear smallint,
@sQuarter varchar(2),
@sSQL varchar(100),
@adddays int

SELECT @adddays = 1 –日期增量(可以自由设定)
SELECT @dDate = ‘01/01/2002′ –开始日期

WHILE @dDate < ‘12/31/2004′ –结束日期
BEGIN

SELECT @WeekString = DATENAME (dw, @dDate)
SELECT @sMonth=DATENAME(mm,@dDate)
SELECT @iYear= DATENAME (yy, @dDate)
SELECT @iDayOfMonth=DATENAME (dd, @dDate)
SELECT @iWeekOfYear= DATENAME (week, @dDate)
SELECT @iMonthOfYear=DATEPART(month, @dDate)
SELECT @sQuarter = ‘Q’ + CAST(DATENAME (quarter, @dDate)as varchar(1))

INSERT INTO time_dimension(the_date, the_day, the_month, the_year,
day_of_month,
week_of_year, month_of_year, quarter) VALUES
(@dDate, @WeekString, @sMonth, @iYear, @iDayOfMonth, @iWeekOfYear,
@iMonthOfYear, @sQuarter)
SELECT @dDate = @dDate + @adddays
END
GO

select * from time_dimension

=================================================================================
–搜索某个字符串在那个表的那个字段中
declare @str varchar(100)
set @str=’White’ –要搜索的字符串
declare @s varchar(8000)
declare tb cursor local for
select s=’if exists(select 1 from ['+b.name+'] where ['+a.name+'] like ”%’+@str+’%”)
print ”所在的表及字段: ['+b.name+'].['+a.name+']”’
from syscolumns a join sysobjects b on a.id=b.id
where b.xtype=’U’ and a.status>=0
and a.xusertype in(175,239,231,167)
open tb
fetch next from tb into @s
while @@fetch_status=0
begin
exec(@s)
fetch next from tb into @s
end
close tb
deallocate tb
/*–测试结果
所在的表及字段: [authors].[au_lname]
–*/
======================================================================================
–查询指定的表在那些数据库中存在

declare @tbname sysname
set @tbname=’客户资料’

declare @dbname sysname,@sql nvarchar(4000),@re bit,@sql1 varchar(8000)
set @sql1=”
declare tb cursor for select name from master..sysdatabases
open tb
fetch next from tb into @dbname
while @@fetch_status=0
begin
set @sql=’set @re=case when exists(select 1 from ['
+@dbname+']..sysobjects where xtype=”U” and name=”’
+@tbname+”’) then 1 else 0 end’
exec sp_executesql @sql,N’@re bit out’,@re out
if @re=1 set @sql1=@sql1+’ union all select ”’+@dbname+””
fetch next from tb into @dbname
end
close tb
deallocate tb
set @sql1=substring(@sql1,12,8000)
exec(@sql1)

======================================================================================
比较两个数据库的表结构差异 选择自 zjcxc 的 Blog
关键字 表结构,差异
出处

/*–比较两个数据库的表结构差异

–*/
/*–调用示例

exec p_comparestructure ‘xzkh_model’,'xzkh_new’
–*/

if exists (select * from dbo.sysobjects where id = object_id(N’[dbo].[p_comparestructure]‘) and OBJECTPROPERTY(id, N’IsProcedure’) = 1)
drop procedure [dbo].[p_comparestructure]
GO

create proc p_comparestructure
@dbname1 varchar(250), –要比较的数据库名1
@dbname2 varchar(250) –要比较的数据库名2
as
create table #tb1(表名1 varchar(250),字段名 varchar(250),序号 int,标识 bit,主键 bit,类型 varchar(250),
占用字节数 int,长度 int,小数位数 int,允许空 bit,默认值 varchar(500),字段说明 varchar(500))

create table #tb2(表名2 varchar(250),字段名 varchar(250),序号 int,标识 bit,主键 bit,类型 varchar(250),
占用字节数 int,长度 int,小数位数 int,允许空 bit,默认值 varchar(500),字段说明 varchar(500))

–得到数据库1的结构
exec(‘insert into #tb1 SELECT
表名=d.name,字段名=a.name,序号=a.colid,
标识=case when a.status=0×80 then 1 else 0 end,
主键=case when exists(SELECT 1 FROM ‘+@dbname1+’..sysobjects where xtype=”PK” and name in (
SELECT name FROM ‘+@dbname1+’..sysindexes WHERE indid in(
SELECT indid FROM ‘+@dbname1+’..sysindexkeys WHERE id = a.id AND colid=a.colid
))) then 1 else 0 end,
类型=b.name, 占用字节数=a.length,长度=a.prec,小数位数=a.scale, 允许空=a.isnullable,
默认值=isnull(e.text,”””),字段说明=isnull(g.[value],”””)
FROM ‘+@dbname1+’..syscolumns a
left join ‘+@dbname1+’..systypes b on a.xtype=b.xusertype
inner join ‘+@dbname1+’..sysobjects d on a.id=d.id and d.xtype=”U” and d.name<>”dtproperties”
left join ‘+@dbname1+’..syscomments e on a.cdefault=e.id
left join ‘+@dbname1+’..sysproperties g on a.id=g.id and a.colid=g.smallid
order by a.id,a.colorder’)

–得到数据库2的结构
exec(‘insert into #tb2 SELECT
表名=d.name,字段名=a.name,序号=a.colid,
标识=case when a.status=0×80 then 1 else 0 end,
主键=case when exists(SELECT 1 FROM ‘+@dbname2+’..sysobjects where xtype=”PK” and name in (
SELECT name FROM ‘+@dbname2+’..sysindexes WHERE indid in(
SELECT indid FROM ‘+@dbname2+’..sysindexkeys WHERE id = a.id AND colid=a.colid
))) then 1 else 0 end,
类型=b.name, 占用字节数=a.length,长度=a.prec,小数位数=a.scale, 允许空=a.isnullable,
默认值=isnull(e.text,”””),字段说明=isnull(g.[value],”””)
FROM ‘+@dbname2+’..syscolumns a
left join ‘+@dbname2+’..systypes b on a.xtype=b.xusertype
inner join ‘+@dbname2+’..sysobjects d on a.id=d.id and d.xtype=”U” and d.name<>”dtproperties”
left join ‘+@dbname2+’..syscomments e on a.cdefault=e.id
left join ‘+@dbname2+’..sysproperties g on a.id=g.id and a.colid=g.smallid
order by a.id,a.colorder’)
–and not exists(select 1 from #tb2 where 表名2=a.表名1)
select 比较结果=case when a.表名1 is null and b.序号=1 then ‘库1缺少表:’+b.表名2
when b.表名2 is null and a.序号=1 then ‘库2缺少表:’+a.表名1
when a.字段名 is null and exists(select 1 from #tb1 where 表名1=b.表名2) then ‘库1 ['+b.表名2+'] 缺少字段:’+b.字段名
when b.字段名 is null and exists(select 1 from #tb2 where 表名2=a.表名1) then ‘库2 ['+a.表名1+'] 缺少字段:’+a.字段名
when a.标识<>b.标识 then ‘标识不同’
when a.主键<>b.主键 then ‘主键设置不同’
when a.类型<>b.类型 then ‘字段类型不同’
when a.占用字节数<>b.占用字节数 then ‘占用字节数’
when a.长度<>b.长度 then ‘长度不同’
when a.小数位数<>b.小数位数 then ‘小数位数不同’
when a.允许空<>b.允许空 then ‘是否允许空不同’
when a.默认值<>b.默认值 then ‘默认值不同’
when a.字段说明<>b.字段说明 then ‘字段说明不同’
else ” end,
*
from #tb1 a
full join #tb2 b on a.表名1=b.表名2 and a.字段名=b.字段名
where a.表名1 is null or a.字段名 is null or b.表名2 is null or b.字段名 is null
or a.标识<>b.标识 or a.主键<>b.主键 or a.类型<>b.类型
or a.占用字节数<>b.占用字节数 or a.长度<>b.长度 or a.小数位数<>b.小数位数
or a.允许空<>b.允许空 or a.默认值<>b.默认值 or a.字段说明<>b.字段说明
order by isnull(a.表名1,b.表名2),isnull(a.序号,b.序号)–isnull(a.字段名,b.字段名)
go
==========================================================================================
行列转换 交*表
出处

总结了一些有代表性的贴子,具体见http://expert.csdn.net/Expert/topic/2440/2440306.xml?temp=.6941645

1: 列转为行:
eg1:
Create table test (name char(10),km char(10),cj int)
go
insert test values(‘张三’,'语文’,80)
insert test values(‘张三’,'数学’,86)
insert test values(‘张三’,'英语’,75)
insert test values(‘李四’,'语文’,78)
insert test values(‘李四’,'数学’,85)
insert test values(‘李四’,'英语’,78)

想变成

姓名 语文 数学 英语
张三 80 86 75
李四 78 85 78

declare @sql varchar(8000)
set @sql = ’select name’
select @sql = @sql + ‘,sum(case km when ”’+km+”’ then cj end) ['+km+']‘
from (select distinct km from test) as a
select @sql = @sql+’ from test group by name’
exec(@sql)

drop table test

 

eg2:
有表A,
id pid
1 1
1 2
1 3
2 1
2 2
3 1
如何化成表B:
id pid
1 1,2,3
2 1,2
3 1
或者是从表B变成A(不要用游标)
以前有相似的列子,现在找不到了,帮帮忙!

–1.创建一个合并的函数
create function fmerg(@id int)
returns varchar(8000)
as
begin
declare @str varchar(8000)
set @str=”
select @str=@str+’,'+cast(pid as varchar) from 表A where id=@id
set @str=right(@str,len(@str)-1)
return(@str)
End
go

–调用自定义函数得到结果
select distinct id,dbo.fmerg(id) from 表A

2:
/*********** 行转列 *****************/
测试:
create table t1 (a int,b int,c int,d int,e int,f int,g int,h int)
insert t1 values(15, 9, 1, 0, 1, 2, 2, 0)

declare @ varchar(8000)
set @=”
select @=@+rtrim(name)+’ from t1 union all select ‘ from syscolumns where id=object_id(‘t1′)
set @=left(@,len(@)-len(‘ from t1 union all select ‘))
–print @
exec(’select ‘+@+’ from t1′)

a
———–
15
9
1
0
1
2
2
0
====================================================================================================
动态SQL语句 选择自 txlicenhe 的 Blog
关键字 动态SQL EXEC SP_EXECUTESQL
出处

1:
普通SQL语句可以用Exec执行
eg: Select * from tableName
Exec(’select * from tableName’)
sp_executesql N’select * from tableName’ — 请注意字符串前一定要加N

2:
字段名,表名,数据库名之类作为变量时,必须用动态SQL
eg:
declare @fname varchar(20)
set @fname = ‘[name]‘
Select @fname from sysobjects — 错误
Exec(’select ‘ + @fname + ‘ from sysobjects’) — 请注意 加号前后的 单引号的边上要加空格
exec sp_executesql N’ select ‘ + @fname + ‘ from sysobjects’
当然将字符串改成变量的形式也可
declare @s varchar(1000)
set @s = ’select ‘ + @fname + ‘ from sysobjects’
Exec(@s) — 成功
exec sp_executesql @s — 此句会报错

declare @s Nvarchar(1000) — 注意此处改为nvarchar(1000)
set @s = ’select ‘ + @fname + ‘ from sysobjects’
Exec(@s) — 成功
exec sp_executesql @s — 此句正确,

3: 输出参数
eg:
declare @num,
@sqls
set @sqls=’select count(*) from ‘ + @servername + ‘.a.dbo.b’
exec(@sqls)
我如何能将exec执行的结果存入变量@num中

declare @num int,
@sqls nvarchar(4000)
set @sqls=’select @a=count(*) from ‘+@servername+’.a.dbo.b’
exec sp_executesql @sqls,N’@a int output’,@num output
select @num
=========================================================================================

介紹取一表前N筆記錄的各种數据庫的寫法…

作者﹕CCBZZP

1. ORACLE
SELECT * FROM TABLE1 WHERE ROWNUM<=N
2. INFORMIX
SELECT FIRST N * FROM TABLE1
3. DB2
SELECT * ROW_NUMBER() OVER(ORDER BY COL1 DESC) AS ROWNUM WHERE ROWNUM<=N
或者
SELECT COLUMN FROM TABLE FETCH FIRST N ROWS ONLY
4. SQL SERVER
SELECT TOP N * FROM TABLE1
5. SYBASE
SET ROWCOUNT N
GO
SELECT * FROM TABLE1
6. MYSQL
SELECT * FROM TABLE1 LIMIT N
7. FOXPRO
SELECT * TOP N FROM TABLE ORDER BY COLUMN
===================================================================================

create procedure SP_GET_TABLE_INFO
@ObjName varchar(128) /* The table to generate sql script */
as

declare @Script varchar(255)
declare @ColName varchar(30)
declare @ColID TinyInt
declare @UserType smallint
declare @TypeName sysname
declare @Length TinyInt
declare @Prec TinyInt
declare @Scale TinyInt
declare @Status TinyInt
declare @cDefault int
declare @DefaultID TinyInt
declare @Const_Key varchar(255)
declare @IndID SmallInt
declare @IndStatus Int
declare @Index_Key varchar(255)
declare @DBName varchar(30)
declare @strPri_Key varchar (255)

/*
** Check to see the the table exists and initialize @objid.
*/
if not Exists(Select name from sysobjects where name = @ObjName)
begin
select @DBName = db_name()
raiserror(15009,-1,-1,@ObjName,@DBName)
return (1)
end

create table #spscript
(
id int IDENTITY not null,
Script Varchar(255) NOT NULL,
LastLine tinyint
)

declare Cursor_Column INSENSITIVE CURSOR
for Select a.name,a.ColID,a.usertype,b.name,a.length,a.prec,a.scale,a.Status, a.cDefault,
case a.cdefault when 0 then ‘ ‘ else (select c.Text from syscomments c where a.cdefault = c.id) end const_key
from syscolumns a, systypes b where object_name(a.id) = @ObjName
and a.usertype = b.usertype order by a.ColID

set nocount on
Select @Script = ‘Create table ‘ + @ObjName + ‘(‘
Insert into #spscript values(@Script,0)

/* Get column information */
open Cursor_Column

fetch next from Cursor_Column into @ColName,@ColID,@UserType,@TypeName,@Length,@Prec,@Scale,
@Status,@cDefault,@Const_Key

Select @Script = ”
while (@@FETCH_STATUS <> -1)
begin
if (@@FETCH_STATUS <> -2)
begin
Select @Script = @ColName + ‘ ‘ + @TypeName
if @UserType in (1,2,3,4)
Select @Script = @Script + ‘(‘ + Convert(char(3),@Length) + ‘) ‘
else if @UserType in (24)
Select @Script = @Script + ‘(‘ + Convert(char(3),@Prec) + ‘,’
+ Convert(char(3),@Scale) + ‘) ‘
else
Select @Script = @Script + ‘ ‘
if ( @Status & 0×80 ) > 0
Select @Script = @Script + ‘ IDENTITY(1,1) ‘

if ( @Status & 0×08 ) > 0
Select @Script = @Script + ‘ NULL ‘
else
Select @Script = @Script + ‘ NOT NULL ‘
if @cDefault > 0
Select @Script = @Script + ‘ DEFAULT ‘ + @Const_Key
end
fetch next from Cursor_Column into @ColName,@ColID,@UserType,@TypeName,@Length,@Prec,@Scale,
@Status,@cDefault,@Const_Key
if @@FETCH_STATUS = 0
begin
Select @Script = @Script + ‘,’
Insert into #spscript values(@Script,0)
end
else
begin
Insert into #spscript values(@Script,1)
Insert into #spscript values(‘)’,0)
end
end
Close Cursor_Column
Deallocate Cursor_Column

/* Get index information */
Declare Cursor_Index INSENSITIVE CURSOR
for Select name,IndID,status from sysindexes where object_name(id)=@ObjName
and IndID > 0 and IndID<>255 order by IndID /*增加了对InDid为255的判断*/
Open Cursor_Index
Fetch Next from Cursor_Index into @ColName, @IndID, @IndStatus
while (@@FETCH_STATUS <> -1)
begin
if @@FETCH_STATUS <> -2
begin

declare @i TinyInt
declare @thiskey varchar(50)
declare @IndDesc varchar(68) /* string to build up index desc in */

Select @i = 1
while (@i <= 16)
begin
select @thiskey = index_col(@ObjName, @IndID, @i)
if @thiskey is null
break

if @i = 1
select @Index_Key = index_col(@ObjName, @IndID, @i)
else
select @Index_Key = @Index_Key + ‘, ‘ + index_col(@ObjName, @IndID, @i)
select @i = @i + 1
end
if (@IndStatus & 0×02) > 0
Select @Script = ‘Create unique ‘
else
Select @Script = ‘Create ‘
if @IndID = 1
select @Script = @Script + ‘ clustered ‘

if (@IndStatus & 0×800) > 0
select @strPri_Key = ‘ PRIMARY KEY (‘ + @Index_Key + ‘)’
else
select @strPri_Key = ”

if @IndID > 1
select @Script = @Script + ‘ nonclustered ‘
Select @Script = @Script + ‘ index ‘ + @ColName + ‘ ON ‘+ @ObjName
+ ‘(‘ + @Index_Key + ‘)’
Select @IndDesc = ”
/*
** See if the index is ignore_dupkey (0×01).
*/
if @IndStatus & 0×01 = 0×01
Select @IndDesc = @IndDesc + ‘ IGNORE_DUP_KEY’ + ‘,’
/*
** See if the index is ignore_dup_row (0×04).
*/
/* if @IndStatus & 0×04 = 0×04 */
/* Select @IndDesc = @IndDesc + ‘ IGNORE_DUP_ROW’ + ‘,’ */ /* 2000 不在支持*/
/*
** See if the index is allow_dup_row (0×40).
*/
if @IndStatus & 0×40 = 0×40
Select @IndDesc = @IndDesc + ‘ ALLOW_DUP_ROW’ + ‘,’
if @IndDesc <> ”
begin
Select @IndDesc = SubString( @IndDesc, 1, DataLength(@IndDesc) – 1 )
Select @Script = @Script + ‘ WITH ‘ + @IndDesc
end
/*
** Add the location of the data.
*/
end
if (@strPri_Key = ”)
Insert into #spscript values(@Script,0)
else
update #spscript set Script = Script + @strPri_Key where LastLine = 1

Fetch Next from Cursor_Index into @ColName, @IndID, @IndStatus
end
Close Cursor_Index
Deallocate Cursor_Index

Select Script from #spscript

set nocount off

return (0)

==============================================================================================
收藏几段SQL Server语句和存储过程

 

– ======================================================

–列出SQL SERVER 所有表,字段名,主键,类型,长度,小数位数等信息

–在查询分析器里运行即可,可以生成一个表,导出到EXCEL中

– ======================================================

SELECT

(case when a.colorder=1 then d.name else ” end)表名,

a.colorder 字段序号,

a.name 字段名,

(case when COLUMNPROPERTY( a.id,a.name,’IsIdentity’)=1 then ‘√’else ” end) 标识,

(case when (SELECT count(*)

FROM sysobjects

WHERE (name in

(SELECT name

FROM sysindexes

WHERE (id = a.id) AND (indid in

(SELECT indid

FROM sysindexkeys

WHERE (id = a.id) AND (colid in

(SELECT colid

FROM syscolumns

WHERE (id = a.id) AND (name = a.name))))))) AND

(xtype = ‘PK’))>0 then ‘√’ else ” end) 主键,

b.name 类型,

a.length 占用字节数,

COLUMNPROPERTY(a.id,a.name,’PRECISION’) as 长度,

isnull(COLUMNPROPERTY(a.id,a.name,’Scale’),0) as 小数位数,

(case when a.isnullable=1 then ‘√’else ” end) 允许空,

isnull(e.text,”) 默认值,

isnull(g.[value],”) AS 字段说明

 

FROM syscolumns a left join systypes b

on a.xtype=b.xusertype

inner join sysobjects d

on a.id=d.id and d.xtype=’U’ and d.name<>’dtproperties’

left join syscomments e

on a.cdefault=e.id

left join sysproperties g

on a.id=g.id AND a.colid = g.smallid

order by a.id,a.colorder

————————————————————————————————-

 

 

 

 

 

 

列出SQL SERVER 所有表、字段定义,类型,长度,一个值等信息

并导出到Excel 中

– — Export all user tables definition and one sample value

– jan-13-2003,Dr.Zhang

– 在查询分析器里运行:

SET ANSI_NULLS OFF

GO

SET NOCOUNT ON

GO

 

SET LANGUAGE ‘Simplified Chinese’

go

DECLARE @tbl nvarchar(200),@fld nvarchar(200),@sql nvarchar(4000),@maxlen int,@sample nvarchar(40)

 

SELECT d.name TableName,a.name FieldName,b.name TypeName,a.length Length,a.isnullable IS_NULL INTO #t

FROM syscolumns a, systypes b,sysobjects d

WHERE a.xtype=b.xusertype and a.id=d.id and d.xtype=’U’

 

DECLARE read_cursor CURSOR

FOR SELECT TableName,FieldName FROM #t

 

SELECT TOP 1 ‘_TableName ‘ TableName,

‘FieldName ‘ FieldName,’TypeName ‘ TypeName,

‘Length’ Length,’IS_NULL’ IS_NULL,

‘MaxLenUsed’ AS MaxLenUsed,’Sample Value ‘ Sample,

‘Comment ‘ Comment INTO #tc FROM #t

 

OPEN read_cursor

 

FETCH NEXT FROM read_cursor INTO @tbl,@fld

WHILE (@@fetch_status <> -1) — failes

BEGIN

IF (@@fetch_status <> -2) — Missing

BEGIN

SET @sql=N’SET @maxlen=(SELECT max(len(cast(‘+@fld+’ as nvarchar))) FROM ‘+@tbl+’)’

–PRINT @sql

EXEC SP_EXECUTESQL @sql,N’@maxlen int OUTPUT’,@maxlen OUTPUT

–print @maxlen

SET @sql=N’SET @sample=(SELECT TOP 1 cast(‘+@fld+’ as nvarchar) FROM ‘+@tbl+’ WHERE len(cast(‘+@fld+’ as nvarchar))=’+convert(nvarchar(5),@maxlen)+’)’

EXEC SP_EXECUTESQL @sql,N’@sample varchar(30) OUTPUT’,@sample OUTPUT

–for quickly

–SET @sql=N’SET @sample=convert(varchar(20),(SELECT TOP 1 ‘+@fld+’ FROM ‘+

–@tbl+’ order by 1 desc ))’

PRINT @sql

print @sample

print @tbl

EXEC SP_EXECUTESQL @sql,N’@sample nvarchar(30) OUTPUT’,@sample OUTPUT

INSERT INTO #tc SELECT *,ltrim(ISNULL(@maxlen,0)) as MaxLenUsed,

convert(nchar(20),ltrim(ISNULL(@sample,’ ‘))) as Sample,’ ‘ Comment FROM #t where TableName=@tbl and FieldName=@fld

END

FETCH NEXT FROM read_cursor INTO @tbl,@fld

END

 

CLOSE read_cursor

DEALLOCATE read_cursor

GO

 

SET ANSI_NULLS ON

GO

SET NOCOUNT OFF

GO

select count(*) from #t

DROP TABLE #t

GO

 

select count(*)-1 from #tc

 

select * into ##tx from #tc order by tablename

DROP TABLE #tc

 

–select * from ##tx

 

declare @db nvarchar(60),@sql nvarchar(3000)

set @db=db_name()

–请修改用户名和口令 导出到Excel 中

set @sql=’exec master.dbo.xp_cmdshell ”bcp ..dbo.##tx out c:\’+@db+’_exp.xls -w -C936 -Usa -Psa ”’

print @sql

exec(@sql)

GO

DROP TABLE ##tx

GO

 

 

 

– 根据表中数据生成insert语句的存储过程

–建立存储过程,执行 spGenInsertSQL 表名

–感谢playyuer

– CREATE proc spGenInsertSQL (@tablename varchar(256))

 

as

begin

declare @sql varchar(8000)

declare @sqlValues varchar(8000)

set @sql =’ (‘

set @sqlValues = ‘values (”+’

select @sqlValues = @sqlValues + cols + ‘ + ”,” + ‘ ,@sql = @sql + ‘[' + name + '],’

from

(select case

when xtype in (48,52,56,59,60,62,104,106,108,122,127)

then ‘case when ‘+ name +’ is null then ”NULL” else ‘ + ‘cast(‘+ name + ‘ as varchar)’+’ end’

when xtype in (58,61)

then ‘case when ‘+ name +’ is null then ”NULL” else ‘+””””’ + ‘ + ‘cast(‘+ name +’ as varchar)’+ ‘+””””’+’ end’

when xtype in (167)

then ‘case when ‘+ name +’ is null then ”NULL” else ‘+””””’ + ‘ + ‘replace(‘+ name+’,””””,””””””)’ + ‘+””””’+’ end’

when xtype in (231)

then ‘case when ‘+ name +’ is null then ”NULL” else ‘+”’N””” + ‘ + ‘replace(‘+ name+’,””””,””””””)’ + ‘+””””’+’ end’

when xtype in (175)

then ‘case when ‘+ name +’ is null then ”NULL” else ‘+””””’ + ‘ + ‘cast(replace(‘+ name+’,””””,””””””) as Char(‘ + cast(length as varchar) + ‘))+””””’+’ end’

when xtype in (239)

then ‘case when ‘+ name +’ is null then ”NULL” else ‘+”’N””” + ‘ + ‘cast(replace(‘+ name+’,””””,””””””) as Char(‘ + cast(length as varchar) + ‘))+””””’+’ end’

else ”’NULL”’

end as Cols,name

from syscolumns

where id = object_id(@tablename)

) T

set @sql =’select ”INSERT INTO ['+ @tablename + ']‘ + left(@sql,len(@sql)-1)+’) ‘ + left(@sqlValues,len(@sqlValues)-4) + ‘)” from ‘+@tablename

–print @sql

exec (@sql)

end

 

GO

 

 

 

– –根据表中数据生成insert语句的存储过程

–建立存储过程,执行 proc_insert 表名

–感谢Sky_blue

CREATE proc proc_insert (@tablename varchar(256))

as

begin

set nocount on

declare @sqlstr varchar(4000)

declare @sqlstr1 varchar(4000)

declare @sqlstr2 varchar(4000)

select @sqlstr=’select ”insert ‘+@tablename

select @sqlstr1=”

select @sqlstr2=’ (‘

select @sqlstr1= ‘ values ( ”+’

select @sqlstr1=@sqlstr1+col+’+”,”+’ ,@sqlstr2=@sqlstr2+name +’,’ from (select case

– when a.xtype =173 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(‘+convert(varchar(4),a.length*2+2)+’),’+a.name +’)'+’ end’

when a.xtype =104 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(1),’+a.name +’)'+’ end’

when a.xtype =175 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+””””’+'+’replace(‘+a.name+’,””””,””””””)’ + ‘+””””’+’ end’

when a.xtype =61 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+””””’+'+’convert(varchar(23),’+a.name +’,121)’+ ‘+””””’+’ end’

when a.xtype =106 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(‘+convert(varchar(4),a.xprec+2)+’),’+a.name +’)'+’ end’

when a.xtype =62 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(23),’+a.name +’,2)’+’ end’

when a.xtype =56 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(11),’+a.name +’)'+’ end’

when a.xtype =60 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(22),’+a.name +’)'+’ end’

when a.xtype =239 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+””””’+'+’replace(‘+a.name+’,””””,””””””)’ + ‘+””””’+’ end’

when a.xtype =108 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(‘+convert(varchar(4),a.xprec+2)+’),’+a.name +’)'+’ end’

when a.xtype =231 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+””””’+'+’replace(‘+a.name+’,””””,””””””)’ + ‘+””””’+’ end’

when a.xtype =59 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(23),’+a.name +’,2)’+’ end’

when a.xtype =58 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+””””’+'+’convert(varchar(23),’+a.name +’,121)’+ ‘+””””’+’ end’

when a.xtype =52 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(12),’+a.name +’)'+’ end’

when a.xtype =122 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(22),’+a.name +’)'+’ end’

when a.xtype =48 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(6),’+a.name +’)'+’ end’

– when a.xtype =165 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+’convert(varchar(‘+convert(varchar(4),a.length*2+2)+’),’+a.name +’)'+’ end’

when a.xtype =167 then ‘case when ‘+a.name+’ is null then ”NULL” else ‘+””””’+'+’replace(‘+a.name+’,””””,””””””)’ + ‘+””””’+’ end’

else ”’NULL”’

end as col,a.colid,a.name

from syscolumns a where a.id = object_id(@tablename) and a.xtype <>189 and a.xtype <>34 and a.xtype <>35 and a.xtype <>36

)t order by colid

 

select @sqlstr=@sqlstr+left(@sqlstr2,len(@sqlstr2)-1)+’) ‘+left(@sqlstr1,len(@sqlstr1)-3)+’)” from ‘+@tablename

– print @sqlstr

exec( @sqlstr)

set nocount off

end

GO

 

 

说明:本贴纯属收藏,目的在于大家交流,在此对作者表示感谢!

==========================================================================================
小写转大写金额 选择自 webmin 的 Blog
关键字 金额 SQL Server
出处

在网上见到一个Oracle的版本的小写转大写金额的函数,感觉还不错现在把它转成SQL Server版本。

/********************************************************
作者:(birdie_7761@cmmail.com)
版本:1.0
创建时间:20020227
修改时间:
功能:小写金额转换成大写
参数:n_LowerMoney 小写金额
v_TransType 种类 — 1: directly translate, 0: read it in words
输出:大写金额
********************************************************/
CREATE FUNCTION dbo.L2U (@n_LowerMoney numeric(15,2),@v_TransType int)
RETURNS VARCHAR(200) AS
BEGIN
Declare @v_LowerStr VARCHAR(200) — 小写金额
Declare @v_UpperPart VARCHAR(200)
Declare @v_UpperStr VARCHAR(200) — 大写金额
Declare @i_I int

set @v_LowerStr = LTRIM(RTRIM(ROUND(@n_LowerMoney,2))) –四舍五入为指定的精度并删除数据左右空格
set @i_I = 1
set @v_UpperStr = ”

while ( @i_I <= len(@v_LowerStr))
begin
select @v_UpperPart = case substring(@v_LowerStr,len(@v_LowerStr) – @i_I + 1,1)
WHEN ‘.’ THEN ‘元’
WHEN ‘0′ THEN ‘零’
WHEN ‘1′ THEN ‘壹’
WHEN ‘2′ THEN ‘贰’
WHEN ‘3′ THEN ‘叁’
WHEN ‘4′ THEN ‘肆’
WHEN ‘5′ THEN ‘伍’
WHEN ‘6′ THEN ‘陆’
WHEN ‘7′ THEN ‘柒’
WHEN ‘8′ THEN ‘捌’
WHEN ‘9′ THEN ‘玖’
END
+
case @i_I
WHEN 1 THEN ‘分’
WHEN 2 THEN ‘角’
WHEN 3 THEN ”
WHEN 4 THEN ”
WHEN 5 THEN ‘拾’
WHEN 6 THEN ‘佰’
WHEN 7 THEN ‘仟’
WHEN 8 THEN ‘万’
WHEN 9 THEN ‘拾’
WHEN 10 THEN ‘佰’
WHEN 11 THEN ‘仟’
WHEN 12 THEN ‘亿’
WHEN 13 THEN ‘拾’
WHEN 14 THEN ‘佰’
WHEN 15 THEN ‘仟’
WHEN 16 THEN ‘万’
ELSE ”
END
set @v_UpperStr = @v_UpperPart + @v_UpperStr
set @i_I = @i_I + 1
end

if ( 0 = @v_TransType)
begin
set @v_UpperStr = REPLACE(@v_UpperStr,’零拾’,'零’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零佰’,'零’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零仟’,'零’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零零零’,'零’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零零’,'零’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零角零分’,'整’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零分’,'整’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零角’,'零’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零亿零万零元’,'亿元’)
set @v_UpperStr = REPLACE(@v_UpperStr,’亿零万零元’,'亿元’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零亿零万’,'亿’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零万零元’,'万元’)
set @v_UpperStr = REPLACE(@v_UpperStr,’万零元’,'万元’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零亿’,'亿’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零万’,'万’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零元’,'元’)
set @v_UpperStr = REPLACE(@v_UpperStr,’零零’,'零’)
end

– 对壹元以下的金额的处理
if ( ‘元’ = substring(@v_UpperStr,1,1))
begin
set @v_UpperStr = substring(@v_UpperStr,2,(len(@v_UpperStr) – 1))
end

if ( ‘零’ = substring(@v_UpperStr,1,1))
begin
set @v_UpperStr = substring(@v_UpperStr,2,(len(@v_UpperStr) – 1))
end

if ( ‘角’ = substring(@v_UpperStr,1,1))
begin
set @v_UpperStr = substring(@v_UpperStr,2,(len(@v_UpperStr) – 1))
end

if ( ‘分’ = substring(@v_UpperStr,1,1))
begin
set @v_UpperStr = substring(@v_UpperStr,2,(len(@v_UpperStr) – 1))
end

if (‘整’ = substring(@v_UpperStr,1,1))
begin
set @v_UpperStr = ‘零元整’
end
return @v_UpperStr
END

例子:

select dbo.L2U(56588441.111,0)

select dbo.L2U(00.00,0)


一、适合读者对象:数据库开发程序员,数据库的数据量很多,涉及到对SP(存储过程)的优化的项目开发人员,对数据库有浓厚兴趣的人。  

  二、介绍:在数据库的开发过程中,经常会遇到复杂的业务逻辑和对数据库的操作,这个时候就会用SP来封装数据库操作。如果项目的SP较多,书写又没有一定的规范,将会影响以后的系统维护困难和大SP逻辑的难以理解,另外如果数据库的数据量大或者项目对SP的性能要求很,就会遇到优化的问题,否则速度有可能很慢,经过亲身经验,一个经过优化过的SP要比一个性能差的SP的效率甚至高几百倍。  

  三、内容:  

  1、开发人员如果用到其他库的Table或View,务必在当前库中建立View来实现跨库操作,最好不要直接使用“databse.dbo.table_name”,因为sp_depends不能显示出该SP所使用的跨库table或view,不方便校验。  

  2、开发人员在提交SP前,必须已经使用set showplan on分析过查询计划,做过自身的查询优化检查。  

  3、高程序运行效率,优化应用程序,在SP编写过程中应该注意以下几点:   

  a)SQL的使用规范:

   i. 尽量避免大事务操作,慎用holdlock子句,提高系统并发能力。

   ii. 尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。

   iii. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。

   iv. 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。

   v. 不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

   vi. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。

   vii. 尽量使用“>=”,不要使用“>”。

   viii. 注意一些or子句和union子句之间的替换

   ix. 注意表之间连接的数据类型,避免不同类型数据之间的连接。

   x. 注意存储过程中参数和数据类型的关系。

   xi. 注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。   

  b)索引的使用规范:

   i. 索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引。

   ii. 尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引

   iii. 避免对大表查询时进行table scan,必要时考虑新建索引。

   iv. 在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。

   v. 要注意索引的维护,周期性重建索引,重新编译存储过程。  

  c)tempdb的使用规范:

   i. 尽量避免使用distinct、order by、group by、having、join、cumpute,因为这些语句会加重tempdb的负担。

   ii. 避免频繁创建和删除临时表,减少系统表资源的消耗。

   iii. 在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。

   iv. 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。

    v. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。

    vi. 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。  

  d)合理的算法使用:   

  根据上面已提到的SQL优化技术和ASE Tuning手册中的SQL优化内容,结合实际应用,采用多种算法进行比较,以获得消耗资源最少、效率最高的方法。具体可用ASE调优命令:set statistics io on, set statistics time on , set showplan on 等。

一.资源类网站
 
1,国内网站
1)Ajax中国(推荐)
一个专业的ajax技术网站,分类清晰,有大量的电子版教程,以及AJAX源码下载!
http://www.okajax.com
 
2)ajaxcn.org
李琨老师的blog。李琨:《ajax实战》译者.
http://www.ajaxcn.org
—————————————–
2,国外网站
1)、Ajax 的诞生
Ajax: A New Approach to Web Applications
http://www.adaptivepath.com/publications/essays/archives/000385.php

2)、AJAX Matters
http://www.ajaxmatters.com
关于 Ajax 技术一个信息量非常大的网站,有点像 Ajax 世界中的 TSS。

3)、AJAX Patterns
与 AJAX 设计模式相关的资源。
http://www.ajaxpatterns.org
http://www.softwareas.com/ajax-patterns

4)、http://www.ajaxian.com

——————————————————————————–

二.热门文章

1.什么是AJAX
http://www.okajax.com/info/net/20060510.html
2.ajax技术简介
http://www.okajax.com/info/basic/200604654.html
3.ajax.net
http://www.okajax.com/info/basic/200604556.html
http://www.okajax.com/info/tech/200605589.html
http://weblogs.asp.net/mschwarz/archive/2005/04/11/399893.aspx(英文)

——————————————————————————–

三.ajax相关下载(电子书教程/源码)
1.Ajax基础教程电子版
http://www.okajax.com/info/bookdown/200604950.htm
2.《AJAX实战》ajax in action电子版
http://www.okajax.com/info/bookdown/200604366.htm
3.AJAX开发简略
http://www.okajax.com/info/bookdown/20060538.htm
—————————-
4.AJAX版CD管理系统 (jsp)
http://www.okajax.com/info/codedown/200604327.htm
5.Alexa 查询系统 (Xmlhttp + Ajax)
http://www.okajax.com/info/codedown/200604982.htm
6.ASP.NET+AJAX解决网页打开等待问题
http://www.okajax.com/info/codedown/200604166.htm
7.Ajax技术简介 (ajax demo)
http://www.okajax.com/info/codedown/200604576.htm

——————————————————————————–

四.ajax相关图书
1.《Ajax实战》(目前Ajax领域最为全面深入的一本著作)
http://www.china-pub.com/computers/common/info.asp?id=28433
2.《Ajax基础教程》(亚马逊计算机榜首图书,国内第1本Ajax图书)
http://www.china-pub.com/computers/common/info.asp?id=28432
3.《Ajax 修炼之道-Web 2.0 入门》(Ajax从小工到专家的首选图书)
http://www.china-pub.com/computers/common/info.asp?id=29430
4.《征服Ajax+Lucene构建搜索引擎》
http://www.china-pub.com/computers/common/info.asp?id=29996
5.《征服Ajax–Web 2.0快速入门与项目实践(.net)》
http://www.china-pub.com/computers/common/info.asp?id=29995
6.《Ajax开发精要–概念、案例与框架》
http://www.china-pub.com/computers/common/info.asp?id=30006
[Page]
 

——————————————————————————–

五.部分技术文章
http://www.dragonson.com/doc/ajax.html
Ajax内部交流文档
http://info96.k12studio.com/~nio/comments.php?id=242_0_1_0_C
简单地对 Ajax 进行描述介绍
http://www.adaptivepath.com/publications/essays/archives/000385.php
Ajax: A New Approach to Web Applications
http://jibbering.com/2002/4/httprequest.html
Using the XML HTTP Request object
http://developer.apple.com/internet/webcontent/xmlhttpreq.html
Dynamic HTML and XML: The XMLHttpRequest Object
http://del.icio.us/popular/ajax
del.icio.us / popular / ajax
http://www.fiftyfoureleven.com/resources/programming/xmlhttprequest/examples
XMLHttpRequest & Ajax Working Examples
http://www.xml.com/lpt/a/2005/02/09/xml-http-request.html
Very Dynamic Web Interfaces
http://www.standards-schmandards.com/index.php?2005/03/01/16-ajax-and-accessibility
AJAX and Accessibility
http://www.xml.com/lpt/a/2005/05/11/ajax-error.html
Errors and AJAX
http://www.softwareas.com/ajax-patterns
AJAX Patterns: Design Patterns for AJAX Usability
http://www.backbase.com/download/Whitepaper%20Backbase%20AJAX%20and%20Beyond.pdf
Whitepaper Backbase AJAX and Beyond
http://www.ajaxpatterns.org/index.php?title=Main_Page
AJAX patterns
http://www.onlamp.com/pub/a/onlamp/2005/05/19/xmlhttprequest.html
A Simpler Ajax Path
继续整理中……

2006年05月04日

今年是我大学毕业满10年的日子,也是我投身it技术的第10年。一直想能对过去的经历做些回顾与反思,以更好地走向未来,但总没有笔。刚好csdn举办“讲述程序员的故事”征文,这件事成了一个引子,我终于趁着暑期有时间,敲了一天键盘,便有了这篇人生自述。
10年对于一个人来说是比较长的一个阶段,10年之后,同期大学毕业的同学情况差异之大,让人叹息命运之变幻莫测。

我在此记录下了我的生活足迹,由于时间较长,有些事记得不太清了,但我是尽量真实的记录当时的情况,有兴趣看我这篇长文的人请把我看成是一个在你我中间一块生活,一起喜怒哀乐,一起努力的平凡人,相信我们之间会有很多跨越时空的情感交流。
好,就说到此吧。


从1994 到2003,不知不觉之间,我已在计算机技术的世界里沉浸了十年。有位哲人说过:如果一个人能用十年的时间专心致志地研究一门学问,或学习一门手艺,或从事一个职业,他一定能成为这个领域的专家。我天资平庸,但哲人的话是有道理的。我用十年的时间先成为了一名合格的程序员,再成为了一个重点大学计算机专业的大学教师。十年磨一剑,蓦然回首,年华如水,青春如歌!

我父母都是城市中的平民,家境仅能解决温饱,我想我可能代表了中国社会的一大类青年,我的经历与体会可能会对许多条件并不好的年青程序员有所启示:只要努力,天资差与环境不好都是可以克服的。因为我不是牛人,所以我不能教你如何成为牛人,我只是尽量真实地写下我的经历与感触,写一个平凡人的努力过程。我为我是一个软件开发者而自豪,为我从事的教书育人的事业而自豪,并愿意为中国落后的软件业尽己菲薄之力,为此,我人生无悔!
我想分为三部分讲述我的人生经历:上篇:学习人生:主要是我学习计算机技术的经历与感触,时间跨度大约是从1994~1996年;中篇:艰难人生:主要是我流迹社会,失业一年半,三次考研的痛苦经历;时间跨度大约是从1997~1999年;下篇:实践人生:主要是我的技术实践经历:在公司打工,开发软件,毕业求职与教书生涯,时间跨度是1999~2003。

一 计算机是什么东西?
我是七十年代初在中国出生的那一代人,等我们长大开始读书时,已经结束,相对于我们的兄长,从我们这一代开始,以后出生的孩子都能受到较为正规的教育,这是我们幸运的地方。

也就是我们上小学的时候,七十年代末,以美国为技术资源中心的计算机技术开始在全球范围内迅猛发展,这时正是世界性的“软件危机”极为严重的时候,计算机技术各方面都在酝酿着新的突破,而此时比尔盖茨也正在准备构建他的明日微软帝国。

七十年代末与八十年代初,在我们的生活中,看不到一点计算机技术的影子,刚刚改革开放的中国,99%以上的人连“计算机”这个词恐怕都没听说过,中国游离于信息技术大门之外。
在成为时代主旋律的八十年代,整个中国真的是呈现出一种勃勃的生机,这是一个民族在经历百年耻辱,十年挫折之后的再起步,象一些老歌—《金梭与银梭》,《在希望的田野上》等,就是当时青年的一种积极向上的精神风貌的体现。这种环境下,对于正在成长中的我们这一批少年,在潜意识中有极为深刻的影响,我们认为爱国是天经地义的事,并且成为了努力学习的主要动力(呵呵,也许真的是吧,这种信念确实会给人以极大的动力,我看不出有什么不好,至少我不会有现在这一批青年所经历的非常迷茫的感觉)。
1986年我考上了桂林市重点高中—桂林中学,在那儿,我第一次见到了电脑。

1.中学的计算机兴趣班

1987 年我在桂林中学读高二,桂中是重点高中,条件较好,当时学校课外安排了两个班:无线电班和计算机班,我上的是计算机班,当时用的微机是apple ii,用basic编程。记得当时中国也开发了一种同档次的计算机,叫做中华学习机,要在中小学中大力推广,但apple ii后来发展成了著名的mac系列,并拥有了自己的操作系统,而中国的中华学习机,还有后来的小霸王等电脑学习机,都无疾而终了。
学校安排上机,有同学不知从哪找来了一些有趣的basic程序,什么“比翼双飞”,“飞机空战”等等,都是一些非常简单的小游戏,当时他抄了厚厚一本,一上机就敲一些到计算机中运行,而我是一点也不会,只有站在一边看的份。
那时会计算机的人很少,老师讲的我又不明白,考试的时候也不知道怎么过的。呵呵,后来我读研打工时,在网上看到洛阳一个高中生用vb编的一个“电子书童”程序,水平之高,我当时真是没法比。
1989 年****,广西高考名额一下减了7000多,竞争激烈,十几人里才能取一个,由于压力过大,我高考前夕失眠,第二天考语文,120分的试卷当时只考了 68分,因而与重点高校无缘。本想复读,但父母怕我来年考不上,一定要让我读广西大学,我万分不情愿地顺从了父母的意愿,去了我不喜欢的学校,读了我不喜欢的专业。男怕入错行,我在大学毕业后重新开始学我喜欢的专业,足足用了五六年的时间才转过来,真正成为一名合格的劳动者,走了一条长长的弯路,所以,父母的决定对缺乏经验与决断力的孩子而言,真的是非常重要。
现在想起来,父母当时没错,我也没错,那都是在时代与社会环境下的自然选择。

2.失败的四年本科与工作初期

作为之后第一批进入大学的本科生,四年里我是个老老实实的好学生,但四年除了玩命过了六级,什么也没学会。
记得大学第一节课就是basic语言,呵呵,也根本没学会,但我看来注定了要与basic难解难分了,后面的实践篇中我会讲我用basic开发的事。
大学里我学的专业是电力系统自动化,学过微机原理与fortran,上机时还用的是古老的主机加终端方式的计算机(叫做l/f机),我上机时真是一塌糊涂,因为这门课我从没学进去,也从没学懂过。有同学很厉害的,会用电脑打印出许多漂亮的图片(当然现在看起来很粗糙,针式打印机打的),我那时就称这些人为牛人了。
呵呵,我如果没记错的话,做超级解霸的梁肇新也是广西大学的,但不是我们系,记得我当时就已听到有关几个计算机牛人的事,估计其中就有他吧,如果我记错了,梁肇新先生请指正。在本科期间,我对电脑是神秘多于兴趣。这也说明我成为不了牛人与大师,天资有限。后来,我发现一个普遍的规律,如果能成为计算机方面的牛人与大师,请去翻翻他们的传记,绝大多数在本科期间就已展露出他们的才华。而我们现在的中国高校,这么多的本科生都在为考试、考研、考托、考g而奔忙,这种填鸭式的应试教育体制,扼杀了太多学生的创造性。结果是一个学生在大学里读得越认真,就受到的错误影响越大,我是用好几年的时间反思才得出这个结论的。所以,中国高校十几年数以几十万计的本科生中出不了可编写linux内核的杰出人才,一点也不奇怪。

1993年我毕业了,用我总结的一段话来说,是:
一张白纸进来,一脑浆糊出去。
毕业后我分到了一所号称是“中港合资”的生产蓄电池的公司,呵呵,刚刚工作的日子让我这个单纯的大学生被生活好好的教训了一下,许多无法以前想象的社会现象让我目瞪口呆,年轻气盛导致穿了小鞋,还只能忍气吞声。吃了生活的一个闷棍,我才明白,我除了有张文凭,什么都不是!
当时,我感到最郁闷的是我在公司里什么都不会做,一个电机烧坏了我楞不知如何下手修,公司老总办公室里的灯不亮了,学电的大学生我花了半小时楞不知原因在哪!我学的东西没一样能用的。开始我还以为是因为我在学校里没认真学,但后来问了很多同学,90%以上的都有同感,我现在终于认识到这不单是我个人的错,我自认为在学校中是很努力的,从未偷懒过,但如果90%以上的学生所学不能服务社会,这种教育体制一定存在问题。

四年里,我的电脑知识与技能=0!
现在看起来,我浪费了宝贵的四年光阴!但当时的环境与认识水平,也只能如此了,没有当初的浪费,怎有后来的珍惜?这是人生必经的一个阶段。

二 漫长的理论学习时期

1.从电脑培训班开始

我毕业后的第一个工作单位是一个非常差的企业,我在那儿见识了几乎所有中国差的企业的弊端:人浮于事,拉帮结派,暗箭伤人,损公肥私……。可以想见,在这样的企业工作,人是多么的颓废。我在无聊之中过了大半年的时间。1994年4月的一天我去桂林新华书店买书,看到桂林电子工业学院老师举办一个电脑培训班,教打字、排版、常用dos命令等电脑使用基础,反正也没事,不如上个班学点东西吧。不曾想就是这样一个培训班,却根本影响了我的人生之路。
我清楚地记得当我第一次上机时,连电脑开关在哪都找不到,老师拿着一张五寸软盘,插入软驱,打开电源,在一阵“吱吱”的声音之后,屏幕上开始有英文字符出现,然后,教师在键盘上敲入几个键,电脑沉默一会,突然在屏幕上跳出一个小人,出现一行字:快快乐乐学电脑,还传出了音乐。我当时真是惊呆了,电脑怎么还会唱歌?现在想起来,那个软件其实是松岗系列教学软件,而我一个大学本科生,在毕业之后居然对电脑无知到这个地步,我真不知道是该哭还是该笑……!
俗语说:“一见钟情”。我是相信这种事的,因为就是这第一次上机,让我产生了搜索这一神秘世界的强烈兴趣,从此就再难与电脑分开了。
当时,有两位电子学院的老师:尹老师和陈老师。他们给我们讲dos,讲汉字输入,讲他们如何修改天汇汉字系统使它能在一张软盘上运行,讲打字排版……,一个月之后,我不满足,又学了一个月。
在上培训班的时间里,我向老师要来了桂林电子工业学院计算机应用专业本科四年的培养方案,回到家中,列出了所有本科课程,再根据书上的先修课程介绍,绘制了一张巨大的课程树,从树根开始,决定一本本地啃,从头开始学计算机技术。计算机科学的学习有较强的系列性,想一步到位很可能会学不懂,但循序渐进又无法跟上技术发展的速度,这是一个非常突出的问题。我现在想起来,觉得好的学习方式是:学理论打基础,在实践中逐步理解理论,多思多悟,就会发现层出不穷的新技术中有许多一直不变的东西存在。把握这种东西,就可以以不变应万变。这种“不变”的东西是什么?就是软件开发的基本原理与理论,基本思想与方法!在我对面向对象理论进行了较深入的学习与把握之后,我学.net只用了三个月的时间就可以独立设计一个完整的全面向对象软件架构,并且用vb.net与c#混合语言把它给做出来了。对于j2ee,我同样有这种自信,也用三个月的时间,一定可以开发与设计基于j2ee平台的软件,因为它们背后的计算机技术基础是一致的,完全可以触类旁通。
尹老师和陈老师可能不知道,他们开的一个也许是为了经济效益的培训班,最后居然培育出了一个计算机硕士!所以,作为一名教师,其责任真的是非常重大的,可能会影响学生的一辈子,我感激他们!

2.漫长而艰辛的学习之路
我当时每月只有一百多元的工资收入,把大半收入都用到买书上去了。电子工业学院门旁的那个科技书店,以及桂林科技书店,成为了我最常去“站读”的地方。我买了数据结构、操作系统、微机原理、ibmpc汇编程序设计,c程序设计……这些教材,就按照预定的计划开始学了。
我当时也说不清楚为什么要学这些东西,决不象现在学生学什么东西都有很强的功利性:学英语为了出国,学电脑为了好找工作。当时根本就没有想过今后如何如何,就是对计算机技术本身的兴趣推动我去学习的。
当时计算机书还不象现在这样扑天盖地,我几乎看完了我能弄到的每一本计算机书,看了一些书,就特别想有地方上机,能练一练。但我一个月一百多元收入,一台电脑怎么也得几千元,要五六年不吃不喝才攒够钱,父母都是普通百姓,家境仅能温饱,拥有一台电脑真成了我最大的美梦。
为了有机会上机,我这个脸皮极薄的人也会什么都顾不上了,带着一盒保存着各种软件的软盘,厚着脸皮到处找可能的地方上机。在这个时候,电子工业学院又一次帮了我,他们的机房管理得并不严,我就冒充是他们的学生去买机票上机。后来去得多了,那儿的老师都熟悉我这张脸了,但搞笑的是,居然没一人知道我是哪个班的!
当时电子学院用的局域网操作系统是novell,我在那儿学会了使用许多最基础的软件,现在仍然如数家珍:cpav,turbo pascal 5.5,ucdos,tway(天慧汉字系统),masm,turboc2.0,还有鼎鼎大名的norton公司的磁盘医生ndd,pctools, borland公司的最牛的常驻内存的软件sidekick,……
在电子学院机房,我还玩了经典游戏:决战中国海,冒险岛,战斧,三国……。
学校网络里软件很多,但其中所染的病毒也五花八门。我记忆深刻的是第一次遇见dir ii病毒时,那种感觉决不是害怕,而是欣喜,呵呵,我见到病毒什么样的啦!由于dos内核是开放的,所以那时开发一个病毒程序相对容易,大家又都喜欢用软盘拷来拷去,结果毒源泛滥,病毒的猖獗使得杀毒软件有了巨大的市场,现在大名鼎鼎的江民kv系列,就是在那时开始占据中国防毒市场,同时还有部的kill 等竞争对手,瑞星,金山等都是后来者。
那时,我白天在公司上班,就盼望着晚上的上机,每次上机我都有详细的计划,因为机时宝贵,所以份外珍惜。
为了上机时能多敲些程序,我一咬牙,向****要了两百元,买了一台电脑学习机,回家苦练英文指法与五笔输入,每天对着家里那台14寸的电视机屏幕,敲击至少键盘2小时以上。练得手腕酸疼,但终于达到了运指如飞盲打的程度。学五笔,背字根“目具上止卜虎皮,……”,足足花了一星期才背下来,并把每个字根与手指对应起来,后来看报纸都不自觉地将汉字给拆得七零八落,再也看不到是一个完整的汉字啦,当时好象练到了每分钟可打60多个字,后来我居然靠着这弹指神功当了一阵打字员混饭吃!呵呵,练好了指法,一上机,确实效率倍增,两小时能敲入并运行调试的程序比以前花三小时敲的还多。

94,95年是我条件最差,但学习劲头最足的两年时期,外在环境的恶劣,却反而造就了我高昂的学习兴趣,那种痴迷,我想热恋也不过如此了,可能因为我太喜欢计算机了,所以爱情与我无缘,这也是无可奈何的事。 :-(

我特别想说说我学习计算机技术书籍的感觉。如果你真的喜欢一门学科,那在啃一本本专业书籍时,就决不会觉得它厚,而只嫌它不够厚。对比我在本科学《电磁学》、《自控原理》之类枯燥无味的课程的感觉,那种课程脱离实际,从纸上到纸上,学生根本无法真正掌握。而学计算机课程《c程序设计》、《数据结构》、《操作系统》等课程的感觉真是天壤之别,原来本科学的东西是越学越糊涂,而我在学习计算机科学的过程中自己就感到慢慢地将所有的知识串成了一条线,脑中有一棵知识树在成长,这不是形容词,是真的感受。我学dos命令,学了很久不明其道理,是在一天早上醒来时灵感一来,突然理解了文件与树的概念,于是,就真正把握了dos命令的内涵,开始对copy,format,move……这些英文单词游刃有余了。有段时间,我甚至能感觉得到我的知识在与日俱增!如果那时能有相对稳定的客观条件:比如有一台随时可用的计算机,那我一定学得更快更好。不过话又说回来,条件好的时候,人又容易为安逸所累,进步反而慢。还是那句老话:业精于勤荒于嬉。现在的许多学生就是物质条件太好了,反而不懂得珍惜。

后来我看了金庸先生的《射雕英雄传》,郭靖成了我最喜欢的人物,不是因为他有黄蓉的爱,而是他那种坚忍不拔的毅力,以平凡之资终登绝顶!金庸先生真是妙笔!此小说我不知看了多少遍。
最值得怀念的就是我学编程的岁月,我学c语言用的是谭浩强的《c程序设计》,当时谭老师在电视上同时讲c程序设计,我每天必看,把书上的每道例题与习题全部做了一遍,并写出了每次上机的安排。我每次都先在纸上把程序写出来,然后上机时争分夺秒地在计算机中调试运行,并根据运行结果思索为什么会运行不正常,在哪儿出错了,如何更正,上机完后还有总结。就是这样,我把谭浩强的《c程序设计》一书中的所有例题和习题全部敲到计算机中运行了一遍,打下了c语言扎实的基础。谭老师的书和课,的确影响了中国多少名程序员!
我学了c语言之后,就开始想做点东西了。我用了一个月的时间,用c编写了一个中文下拉菜单的程序,从ucdos中的字库中提取汉字点阵,然后在图形模式下“画”出汉字来,使程序能脱离汉字系统的支持直接显示汉字。整个程序应该有一两千行代码吧,全部是自己寻找资料,一行行敲进去的。后来,我又编了读取wps文件的程序(当时dos下打字排版是求伯君的wps一统天下,word当时在哪里我不知道呢!《电脑报》介绍过的求伯君,鲍岳桥等作为第一代中国杰出程序员代表,是我当时崇拜的偶像),我还编写了一个绘制简单图形的小程序,从编程调试的过程中,我得到了程序员基本功的最初训练。我当初没人教,遇见各种困难只能自己解决,为找出一个bug,晚上睡觉做梦都在编程,而现在的一些大学生,程序通不过,马上问老师,甚至有学生把程序发到我的邮箱,叫我帮他调程序!有的计算机专业学生,学了四年,四年敲过的代码总数恐怕不超过2千行!
的确,我当时的水平很低,现在的本科生比我当时强太多了,但至少有一点他们中的许多人不如我,他们不如我勤奋,也不具备我的毅力,我觉得这是现在这批八十年代出生的大学生最缺的东西。

我用一年半的时间基本自学完了计算机专业的主干课程(除了《编译原理》,我实在看不懂),1995年我参加了初级程序员考试,结果上下午成绩加起来有90多分。但96,97我两次参加程序员级考试,虽然我已有不少编程实践,但仍然通不过,现在看起来,这反映出一个事实:我理论基础实在太差,天资也并不高。直到现在,我仍然痛感我的计算机基础理论不好,使我难以成为一名计算机科学家,而只能成为一名不错的软件工程师。
从95年开始,我的生活开始加速下降,我所在的公司由于被一群我现在称之为驻虫与流氓的人给弄得不成样子,每月连100多元的工资都无法保证,我只得离开了公司,把档案往人才交流中心一放,就开始了我四年的社会漂浮生活……。

在这四年中,我干了不下四五种工作:当过推销员,当过打字员,当过培训老师,修过电脑,从事售后服务,甚至摆过地摊……,一切的一切,很简单,就为了能有口饭吃。生活是严厉的,不讲情面的,我唯一的爱好就是闲下来就拿起我的计算机书来看。当时我大哥说过:你学这些东西有什么用?能靠它当饭吃?不要惊奇这种观点,在我们那信息闭塞的地方,当时有谁能想到计算机技术会有如此大的改变社会的能力?我之所以不懈学习,纯粹是为了解脱精神上的困惑,在技术的海洋中忘却生活的烦恼。我后来有一句名言:何以解忧,唯有电脑。别人当作一句笑话,但却是我当时的真实写照。
万事都有两面,事情坏到极处,就会向好转化。又是一个很偶然的事,改变了我的人生轨迹。
1996 年,我的一个高中同学杨考上了湖南大学计算机系研究生,请客吃酸菜鱼火锅。在大家喝酒时,杨提了一个问题:请大家说出一个自己最想达到的目标。我的桂中同学大多数都是上的全国重点大学,此时毕业三年了,大家都对社会对生活有了自己的认识,于是就有了各种不同的答案,我当时的回答是:“我希望能有一天开发出一个好的软件,让许许多多的人都能用上它。”,这个目标我到现在都没达到。但当时大家是给我热烈地鼓励。杨饭后跟我说,你既然那么喜欢计算机,为什么不考研呢?对啊,我为什么不去试试呢?当天晚上我躺在床上,没怎么想就确定了要考研的想法。当时却怎么也没想到,考研之路成了另一条折磨人的不归路。对我一生的影响深远……
这就是我早期的学习生涯,深深地体现出中国的时代特色。这段时间我的生活是困苦的,心情是苦闷的,但学习热情却是无以伦比的。现在我一个人拥有两台奔四电脑,随时可上互联网获取丰富的资源,反而在技术上难有寸进了。越来越觉得古语说得好:
艰难困苦,玉汝于成!
这是生活的真理!

下篇 曲折的实践之路
1.希特勒地堡与cih病毒
99年4月,我来到北京参加研究生复试。复试完了之后就不回去了,我拿着一张光盘,里面刻着我用vb和authorware3.5编的一个cool 3d的教学软件,到处参加招聘会,开始了在北京的打工生涯。
说句实话,我心中真是一点底也没有。北京人才济济,我一个三流大学的毕业生,又不是计算机专业的,有人要我吗?
我在北京无依无靠,没有任何一个亲戚在北京,住成了大问题。北京这地方,钱太不经花,生活费用太高,我四处寻找便宜的地方住。后来,我在北京化工大学对面的招待所中租了个床位,每晚20元,地下室。这个招待所的地下室非常大,每天回去的时候,都要走过长长的曲曲折折的通道,加上那昏黄的灯泡,每次我都有走进了希特勒地堡的感觉。地下室里潮湿阴暗,不见天日。地下室里人员很杂,什么人都有,永远没有一种安全的感觉,所幸的是我也是个穷光蛋,光脚的还怕穿鞋的?别人能呆我也能呆,唯一让我担心的是钱,没钱,在北京连流落街头都不够格,立马被收容去昌平筛沙。呵呵,我没这种经历,这是当时住一块的一个外地要考北京中医药大学的博士生说的(我俩居然住到了一个屋里,大家都没钱,只能挤地下室,只是他老兄晚上打呼,我可真受了不少罪)。

日子一天天过去,袋里的钱一天天少了,但工作还是没着落,心中越来越慌,除去学费,来京时父母给我2000元,大哥资助我的1000元就是我当时的全部资金(3000 元在北京能呆多久?!)。我当时订了个计划,留下500元保底,打死都不能动,要靠它作路费回家的。我当时说,一定得找到一家公司,再少的钱都干,只要有口饭吃,有张床让我睡我就满足了。
我一共应聘了四五家公司,最具传奇色彩的是到西单附近一家公司的应聘经历。那时,我带去了我的作品,公司项目经理要看,于是我给他们演示,很奇怪,程序一打开就死机,一连两台电脑都一样,末了,两台电脑都启动不起来了。用瑞星一查,cih病毒——那天正是4月26 日!于是,cih破坏了电脑主板的同时,也无情地摧毁了我的就业机会。还算老板可怜我,没要我赔。其实我当时快到山穷水尽的地步了,真要我出钱,我可能连家都回不了啦。陈盈豪(cih病毒的作者),你小子这么的聪明做什么不行,偏要去做病毒,害人害已,弄得我连个饭碗也找不到,晚上回“希特勒地堡”,把这小子骂了无数次!
总算天无绝人之路,在我数着最后还剩余600元钱的时候,终于有一家公司要我了,做中小学教育软件,月薪1600。干了两星期,又给我在公司的仓库挪开货物,搭了个床位,我就在那住下了,庆幸再也不用到“希特勒地堡”里当冲锋队员了。每天下班后,我就睡在林立的大纸箱之中,如果来个地震,呵呵,我就埋在纸箱中了,休想爬得出来!

我干得非常努力,第一个月工资开出来,扣掉个人所得税,我拿了1500多元。你们不知道我拿到第一笔工资的感觉!这钱在很多人眼里简直不算钱,但对于一个没工作近两年,工作也一个月收入不到200的穷小子而言,就象陈佩斯小品中所演的:“我王老五活了一辈子,还从没见过这么多钱啊!”。好笑吗?我一点也不觉得好笑。我只是心酸,我到了28岁才可以用自己的知识与技能养活自己,再不用依靠父母和兄长的资助,才真正完全用自己的力量在社会上站起来,一个没有经济自立能力的人,只能是个不成熟的人,一个躺在父母身上的人,不是一个真正的男子汉!我当这样的孬种当了28年!

我找到工作后向家里写过一封长信,信中讲了我对父母平时不好意思说的很多心里话,当时,我父亲说,母亲接到信都哭了。我刚拿到工资,马上打了个电话回家里告诉母亲,以缓解他们的焦虑,毕竟儿行千里母担心啊!父母有退休金,一再表示不需要我赡养,而我再这样不要脸下去,我还是人吗?我就是从那时候开始,下定决心,从今往后,再不向家中要一分钱!今后三年读书和生活的费用我一定要用自己的劳动来获取。
金钱带给我的激动就是在那段时间,温饱问题解决之后,金钱对我的诱惑就再没有了,哪怕让我一个月赚一万,给套房子给我,也不可能再给我带来幸福的感觉了,钱是重要的,但不能成为它的奴隶,家中不需要我负担,我一个人有吃有喝,干着我喜欢的工作,足矣!
我在那公司一直干到九月学校开学为止。我走的那一天,公司居然还开了个欢送宴会,老板封了500元的封包给我。我真是感动。我大学毕业后在社会上挣扎求生,得到的大都是冷眼与蔑视,社会终于认可了我的价值,我不是一个只知道吃饭的废物!
1999年9月9日上午9点,在这个最多9字的时刻,我到学校报道来了,放下行李,我坐在分配给我的床上,心中终于踏实下来。
现在,我在北京终于有一张属于我的床了,一张真正属于我的床了,终于有个地方可以让我安稳地睡觉了。没有这段经历,我怎么体验到“安居乐业”这一个词的真实含义!个人如此,国家要做到,中国要做到,更难!

 2.编程生涯
读研两年半的时间,我四处打工,这两年半我至少在七八家公司做过,编了十几万行代码,为自己赚了一台电脑,两部手机(丢了一部),还有读研期间的所有费用,实现了我的目标:没向家里要一分钱。
我水平不高,但在打工过程中,发现许多中国软件公司的水平更菜!
2000 年暑假,我参加一个商品软件的开发,搬到了北大燕北园的一个宿舍,在那儿搞封闭式开发。在这个项目采用vb6开发,主力程序员除我之处,还有一个华北电力大学的研究生。大家都没开发商品软件的经验,没经过正式的设计,就匆忙编码了。我曾经想采用分层的系统架构,但遭到另一位程序员的反对,争吵没有结果,老板也不能做决定,结果各行其是。现在看起来,那时真是太菜了,任何一本软件工程书都会指出这种做法是不对的。
我们直接就热火朝天的干了起来,那位华北电力大学的老兄,真让我佩服,他可以在一个sub过程中写上2000行代码!我觉得奇怪,为何他能写这么多的代码?一看,顿时我晕倒,他居然将每个控件的left,top,width等属性都用代码来设定!想想这样的程序,调试时光单步执行就需要按多少次f8键!
我们在电脑旁边搭了个行军床,每天都是干到早上6点才睡,12点吃中饭,然后又是一个通宵。当时整个工程只有一个类模块,被我用于封装访问数据库的ado数据引擎,这是整个工程中唯一一处用到了最简单的面向对象技术的地方,然后,我写了近十个bas公用模块,每个模块代码规模都有一两千行,还有十几个窗体,每个窗体中都塞满了事件驱动的vb代码,整个软件应该有10万行代码,我一个人在此期间至少就写了3万多行vb代码。程序的主处理流程我甚至用ado与dao写了两套!
现在想起来,我们当时根本就不知道面向对象为何物,更不理解许多对软件开发至关重要的理论,就凭着一种热情。在这段痛苦的开发经历中,我不仅精熟了vb,而且从中学到了很多。现在再开发同样功能的东西。我至少可以砍掉2/3的代码。怎么学都不如从失败中学得多。
后来我总结vb程序员的三个境界:
(1)所有代码都只放在窗体文件中的,属于菜鸟级,他们只会从面板上拖控件,设置属性,然后再给事件编码
(2)工程中有bas模块的,属于中间级,他们已意识到有大量的代码是重复出现的,应该将其抽取出来作为公用模块
(3)工程中有cls模块的,属于高手,他们已掌握了面向对象的思想,并能应用这种思想来解决实际问题
正是这次开发经历,促使我反思:到底如何开发软件?我个人可以用vb完成各种各样的功能,为什么凑在一起就会那么困难?更正一个bug为何那么难?为什么一个看似简单的软件,要拖半年的时间还看不到结束的日子?
九月,研究生要开题,我自拟了个课题:软件体系结构设计。我决心弄明白,好软件到底是怎么做出来的。
我放下了vb,开始研究c++,原来学过vc,但没学会,就直接从c++ builder入手。borland公司的vcl类库让我大为叹服,很快就迷上了它,为此连带学了delphi,并参与了一个delphi项目。给我真正震撼的是《设计模式》这本书,看了才知道,原来好软件是这么设计出来的!在此,我强烈推荐所有有一定编程经验的程序员一定要看这本书!
一边学习理论,一边可没忘记我没经济来源,于是又四处打工,接一些小项目与小模块来赚些生活费,国内一家横跨家电与软件的著名公司是我打工期间去过的最大的公司。但无论什么公司,都给我一种感觉——烂!公司中充满了对员工的剥削与不尊重,对软件开发这一事物的错误认识,管理混乱。
我没机会去外企,技术水平不到,别人不要我 ,但我所见到的软件公司,我敢说没一家能做出世界一流的软件!
我上的研究生课程也让我失望,整个就是本科教育的再版。经过高考与考研两次重大考试,我对考试已是深恶痛绝,但读研期间仍要考试,而且是闭卷!我就不知道我去背那些条文对我的研究与学习有何帮助?2000年7月当我考完最后一门,终于大松一口气,我终于摆脱了考试的压迫,可以在剩余的一年半中真正搜索我渴望已久的软件技术了。这年,我29岁。
悲哀吗?一个中国的程序员要到29岁才可以真正自由地学习想学的东西!
我开始研究面向对象理论,看了大量的书,每天都在实验室学习到深夜12点,天天上网,csdn成了我最常去的网站。
随着我对软件技术的了解越多,就越深刻地感到国内与国外技术水平的差异是如此的巨大,巨大得甚至有让人绝望的感觉。同时,在北京这个中国软件人才最集中的地方,我也见到了许多牛人,一个清华的本科学生,做程序员可以拿到一个月12000元的工资,一个北大的计算机系研究生,一毕业就到外企,一个月一万多收入,每年发16个月工资!干了两年就开了自己的公司。我看到了一个北方交大的本科生作品,在半年的业余时间里,用delphi写了十几万行代码,他甚至在代码中嵌入汇编,自行编写数据存取引擎读写foxpro,速度超快!软件中有一个计算公式解析模块,他用编译原理理论居然做了一个小型的公式解析器,就象 c++编译器检查c++程序一样,不仅可以判断是否公式正确,而且给出的出错信息还相当准确!其基本功之扎实,水平之高让我望尘莫及!中国优秀的人才真还是不少的!在精英集聚的北京,面对着博大精深的软件科学,我深感自身的渺小。
但我毕竟是靠自己奋斗出来的,我并不自卑,这世界需要牛人,同样需要大批合格的劳动者,我成为不了牛人,但我作一名程序员是合格的。
时间过得飞快,一年半根本就不算时间,马上就要毕业了,学习的成绩如何,要到社会上去竞争了,让社会考场来决定你是否及格。

3.毕业求职

98 级的研究生是it业最后辉煌的回光反照。当时,各大公司都发了疯似的要人,象华为,当时是来者不拒。计算机专业的研究生是一抢而光,本科生也供不应求。 2001年毕业的我的师兄师姐们,平均每个人手头都至少有两三个offer,谈的工资没有低于每月6000的,许多人去了外企,工资在 8000~10000每月的也有。真是毕业生的黄金时间。
但好景不常在,911事件我看来好象成了分水岭,911之后,整个it业急刹车,就业形势急转直下,各大公司都在消化去年吃得过饱的胃口,我形容是大家都吃坏了胃,再也不可能有我师兄师姐们的风光了。虽然时间相隔仅大半年。
从10月开始,11月和12月,我几乎是在招聘会与公共汽车上渡过的,北京东南西北地去面试,上午在上地,下午可能就要跑到朝阳区,疲于奔命。
在找工作过程中,我良好的心理素质与丰富的编程经验起到了很大的作用,并没有遇到很大的困难,就先后有几家公司表示要我。
其中我想说的有两家公司。
一家是台湾公司,应该说他们做得是不错的,但我非常反感他们那种自认为高于大陆人的那种优越感,而且待遇也不高。面试之后,回来感觉很不好。当时台湾是民进党上台主政,我就称之为“民进党”看不起“”(虽然我不是党员,但我们都是中国人,都希望中国能强大,让她的人民能挺直腰杆)。
我想:我们大陆确实是不如台湾发达,难怪别人看不起我们,这世界势利得很,社会如此,国家亦然。但我中华960万平方公里的国土,13亿人口,难道要依靠一个小小的岛屿来撑门面?难道除了台湾公司,我就无处可去了?虽然改变不了什么,但我决定绝不去台资和日资的公司工作(日本人我更不喜欢,但我认为他们做事实在优秀,我们一定要向日本人学习)!在这种公司呆着,不爽!同时,我们的确也得争气些,这世界只尊重强者。
另一家是个很不错的民族软件企业,想要我,其老总专门找我去面谈了一次,希望我能到他的公司工作。这位老总白手起家,能在五六年的时间内由几个人发展到300多人,当时,是中国这个领域软件市场占有率最高的公司,我非常佩服,他为人也很好,并表示可以给我每月比其他研究生多加1000元工资,而且希望锻炼一两年之后我能够带领一个团队来开发产品,并负担我毕业所需交纳的各种费用。在就业不景气的2002年,对我一个30岁的技术水平有限的老程序员如此看重,真叫我感动。虽然我最后还是没去,但我仍然对这个公司充满了好感。一个软件企业最重要的是什么?是对人的尊重,没有这点,可以断定,这个公司长不大。
我为什么没去软件公司?放弃有可能在几年之后年薪突破10万的收入?是因为我已有了更想做的事,我想当老师!
我自己是从自学的路上走过来的,其中的艰辛历历在目,无人指点,我走了多少弯路?中国还有多少是象我这样的年轻人无人指点的?我幸运能爬出来了,因为我至少还能有饭吃,比我条件更差的,比如农村的孩子,可能就被生活的压力所淹没了。我个人的力量很微弱,技术也有限,但我自认为至少是一名合格的程序员,如果能培养出一大批达到我的水平的学生,他们毕业后成为合格的程序员,中国软件的根基就会更扎实,如果更能有一批远远超过我水平的学生出来,中国软件就有希望了。软件是什么?软件以人为本!
人生追求什么?金钱?我很缺钱,但不可能把金钱作为我的目标,我希望我能够成为一个对社会有所贡献的人,能够获得心灵的充实,于是,我选择了留校。
对教育的反思与教书生涯

4.对教育的反思与教书生涯
2002年下半年,我参加了北京高校青年教师岗前培训。一位杜教授的两堂课让我印象深刻,其中两句话让我深深震动:
第一句话:我是一个教书匠,教书匠好啊,好就好在“匠心独运”!
第二句话:做什么工作都要达到变魔术的境界!
是的,作为一名教师,就必须起到一个传声筒与放大器的作用,将人类最聪明的人探索世界所得到的知识予以拓宽发展,以便让更多的人能够掌握这些知识并进而应用于实践,从而推动人类的不断进步。
作为一名计算机专业的教师,就要努力把先进的计算机技术以尽可能高的效率传授给广大的学生,引导他们直接面对真实的软件世界,而不是向他们硬灌各种各样的理论知识,强迫他们去死记硬背以应付考试。
我是一个小人物,才低学浅,但我真的认为高校计算机教育存在严重的问题。我在打工期间的开发经历,以及阅读国外相关资料的时候,发现学校中教的和社会上用的差得太远!都已经是什么时代了,许多大学还设立foxpro课程,难道就不能直接用现代主流的数据库如oracle,sql server,哪怕是access也好,来讲授数据库知识?还有所谓的计算机等级考试,呵呵,过了三级学生的我见过不少,但他们真正对软件,对计算机又理解多少?
在中国甚至于升职称也要考计算机,我看了一本职称计算机考试教材,里面几乎原样照搬计算机原理课程中的相关章节,弄得我给搞艺术的大嫂讲了一个小时的二进制与八进制、十六进制的转换方法,她还是弄不太清。我都泄气了,对啊,他们搞艺术的,有必要去分清二进制的10与十进制的2有何分别吗?他们八辈子也用不上。但是考试用得上,呵呵,又是中国特色。
拿c++来说,我讲过c++课,许多的国内教材对c++语法下了大功夫,却对体现了c ++精华的stl只字不提,对代码背后所体现的软件开发思想与方法更是视而不见,我用c++也编过不少程序了,说句实话,我用到的特性不到c++的三分之一!于是就出现了这种怪现象,许多学生考试可以拿八九十分,给他一个简单的实际问题他却不知如何下手!
还有uml,现在好象很火的样子,我们读研时就开了这门课,讲句实话,当时这门课我就没听懂,可后来我尝试着用c++用面向对象的方法来编程序,然后,由代码倒推回uml类图,一下子就明白了,原来uml只不过是一种描述面向对象系统的符号罢了,如果学生没进行过真正的oop,那就是为了学uml而学uml,根本就本末倒置。
还有软件工程,我的感觉,一个人如果没写过一万行以上的程序,他看软件工程书就同看政治书差不多,每句都对,呵呵,就不知道为什么对。我完成了那个几万行的vb程序之后,再回过头看看理论,真是句句是真理!每个理论背后都是大量实践经验的总结。
回想我做软件的体会,我发现所有的知识都是一个完整的体系,根本就无法区分哪些知识是本科的,哪些知识是研究生课程。于是,一种想法产生了,我主讲程序语言类的课程,那我就直接以真实的软件开发过程为主线,实践中需要什么我就讲什么。讲c++/,我就抛开了指定的教材,c++我讲了stl,我讲了ooad,两者我都讲了用rose进行双向工程,引导学生去学uml,一开始就用uml去描述自己开发的程序,何必浪费大量的时间去学结构化的编程方法?
我现在简直成了面向对象技术的布道者,我下学期即将开设ooad基础选修课,从实际项目中抽取典型的案例,讲uml,讲rose,讲设计模式,讲软件体系结构,例子代码横跨c++/和.net下的开发语言(c#和vb.net),这对于我一个技术水平有限公司的董事长而言,实在有点不自量力,我鼻子上才插上几根葱,就想装大象?但我想,如果我能成功地激发出哪怕1%的学生的兴趣,能引导他们走向我认为是正确的方向,能启发他们思考,能直接面向真实的软件开发活动而不是学校的考试,就是我的成绩。我现在正在看引进的原版书籍—《设计模式解析》,时时击节赞叹!《设计模式》经典但难懂难用,而《解析》一书的两位作者既有丰富的从业经验,又对ooad进行了深入的思索,真知灼见时时展露于书中,我现在刚看了这本书的80%,基本上可以用一个成语来描述这部书:深入浅出!

要做到深入浅出谈何容易!只有同时具备技术专家与文学作家素质的人才能做到!
反观我们国内的大量教材,还有大量的垃圾论文(包括硕士,博士的,本科的就算了,我看绝大部分根本不能称之为论文),全都是“浅入深出”,作者自己都未必明白,就东拼西凑,摆出个样子吓人。本来完全可以用大白话三言两语讲清楚让人明白的道理,有人专门要绕一大圈,专挑用高深的数学公式来表达,唯恐别人容易理解,显得他水平不高!呵呵,什么东西一沾上“数学”,立马身价百倍。我就听过一个北师大的一个在读博士说过:如果一篇博士论文中没有一个数学公式,根本就不算是博士论文。这句话对不对,大家可以多思索。我数学不好,没资格讨论数学,我的直觉:数学是工具,但如果为了发表论文等目的而故弄玄虚,为数学而数学,是不合道理的。中国的学术水平与现状,大家都心知肚明,不用我废话了。
现在每年都毕业大批的计算机专业学生,可其中真正具备扎实根基的可以很快胜任工作的我看只有5%(95%的学生不要扁我,不同意就当我在说胡话,我很瘦的,一扁就没了)。多年以来,我国高校计算机教育是按照计算机科学研究者来培养的,可事实上,有多少学生能从事计算机方面研究的能力?我看应分流,80%的学生按工程师方向来培养,20%的学生按研究者方向来培养,而且应大幅减少必修课的数量,计算机领域太深太广,样样都想教给学生,反而成了夹生饭,一个想从事硬件设计的学生和一个想从事软件开发的学生,能给他们一样的饭吃吗?学校应提供各种条件和资源去引导学生,激发学生的创造性与主动探索性,让他们去尝试,去发现自己的长处,最终找到最适合自己的发展方向。不这样做,中国软件后继乏人,没希望。

5.实践第一
2002年下半年,我和一个朋友去了北京郊区的一家杂志社,看到的情形让我震惊:在信息产业最发达的北京,这家杂志社还用人工查对杂志订单和款单,看着那按省来登记的几大本厚厚的客户登记本,工作人员需要手工来在其中查找相应的信息。这样的工作效率,这样的行业信息化水平!北京如此,全国又如何?
从这件事上,我看到了中国软件业的另一方面。一方面我们没有核心技术,另一方面,已有的技术又根本没推广没用好。追踪世界先进水平,对于我等这种水平的人来说,确实勉为其难,但将已有的技术用于解决实际问题,却是我们可以做的。如果中国各行各业都真正能通过进行信息化而提高生产效益,那中国不就从根本上强大起来了吗(象印度,it一枝独秀,其他行业没有起色,我称之为跛足的国家,绝不能成为世界强国)?在扎实的社会基础之上,软件业不就有了更大的市场与发展潜力?中国许多行业的信息化水平非常低,而且中国地域广大,发展非常不平衡,有些地方信息化水平甚至为0!努力推动行业信息化,是我们这一代软件开发者的责任!而追赶国外先进水平,恐怕就不是一代两代程序员可以达到目标的,需要长期的努力。作为一名软件开发者,只能脚踏实地,哪怕你只做一个小小的mis产品,也要尽力把它做好来。
于是,我开始了期刊发行系统的开发,这完全是自发的,没有任何资金投入,只有一种热情在支撑。我开始选择c++ builder开发,做了几个月,完成了第一个版本,但我发现,我找不到足够水平的c++程序员进行合作开发,而且整个一个exe文件,在体系结构设计上虽采用了分层分块的设计方案,但却是源代码级别的,要拆分成com组件难度太大,不是一个人能完成的,于是中途流产。
2003年3月,我开始系统地学习.net,我吃惊地发现,我原来想在c++中实现的许多功能,比如对象串行化为xml,在.net中已有现成的类可用,.net更把oo的功用发挥到了极致,它的混合语言开发,它的反射机制,它的新的自识别的软件组件,以及强大好用的开发环境vs.net,都让我惊叹微软对开发者遇到的困难的深入把握,相比用c++开发,至少可以提高1/3的开发效率。于是一个新的想法产生了,我要把以前的产品用.net重写,利用.net强大的组件模型,将设计模式理论推广应用到组件级别,通过xml和反射机制建立一套可动态装配的软件生产流水线,实现象dell直销pc那样的软件动态装配直销。只要建立好灵活可不断重构的系统架构,配以对业务领域的深入分析,逐步建立功能强大的业务组件仓库,就可以实现软件系统的动态装配。说干就干,我用vb,net重写了原先用c++编写的系统,将原先的一个exe变为十几个dll,而且这些dll还会随着发展而不断地分裂,也有可能重新组合,利用refractoring (重构)不断进化,最终形成一整套完备的行业软件组件库。做软件关键因素是变化,只有适应变化的系统才是有生命力的。
我用两个月的时间大体上弄清了.net平台下的主要类库,然后又用两个月的时间再次重新实现了期刊发行系统的c++版本的全部功能,到8月份我写这篇文章之时,第一个产品里程碑已经完成。同样的,除了需求是由另一个合作者去搜集的之外,几乎又是全部由我一个人包揽了所有的系统设计及编码工作!
毫无疑问,我自觉得新系统要比老系统可维护性强得多,真正实现了彻底的全组件化系统开发,计划再有一个月的界面美化,改正bug,引用多线程提高运行效率,优化系统结构,就可以提供给用户试用了。市场如何,难说难测。
在开发过程中,我再次感到巨大的挑战,不光是技术上的,更是管理上的。人才是我最头痛的问题,没有一流的人才,哪来一流的产品?我们找不到足够水平的程序员一起合作(牛人哪屑于做这样一个小儿科的产品?)我们要走的路还很长。也许我们开发的产品是失败的,但我从来就没指望能用这赚钱,我只是尽一个软件开发者的责任而己,成败已不重要!人生不尝试,怎能体现人活着的价值?

结束语 永无止境的软件之路
1.辛酸与苦辣
做软件开发很苦的,技术进步如此神速,每个软件开发者都得不断地学习以跟上发展的步伐。有时我常想,何苦呢?我现在在大学中混,怎么样不能混下去?为什么要做这些吃力不讨好的事?我投了十年的时间去学习电脑,却最终发现自己不过中人之资,离顶峰远之又远。古语云:三十而立。我今年32了,仍是孤身一人,看看周围的同学,他们的小孩都差不多上小学了!许多都有了自己的房子和车子,而我到现在才开始申报中级职称,还挤在集体宿舍中,望着北京高昂的房价而叹息。现在社会越来越功利了,看着象我这样的穷光蛋,看着由于长期面对电脑缺少锻炼而瘦弱的身躯,又有哪个女孩愿意嫁过来受苦?
软件人的生活很苦,压力很大,我认为是拿青春赌明天。就是在这种恶劣的开发环境中,还有许多业外人士指手划脚说程序员如何如何,还有太多的公司只顾压榨程序员的劳动,缺乏对程序员基本的尊重,怎不叫人寒心?
“三十而衰”,这句本不应该流行的话居然成了中国软件人员的流行语。中国程序员的悲哀!
2.无止境的追求
人类已进入信息时代,计算机技术几乎每隔半年就有一次大的变化,我现在又面临着这样的抉择:今后的路如何走?
北大的教师聘任制度的改革一石激起千层浪,说明了中国高校的改革势在必行。我是欢迎这种变化的,虽然到时我可能会下岗。但人生中总会遇到各种各样的挑战,只要你不倒下爬不起来,就有希望。
我想我必须再次让自己有个提升,要从小事做起,但做小事则绝不能成为最终的目标,也许,不远的将来,我会走出国门,到世界软件技术最发达的地区去汲取丰富的养份。今后的路怎么走,我还在摸索之中。
3.人生无悔
人生年华如水,时光无情。在过去的岁月中,我尽了自己的力,回顾往事,我可以说:过去的事只有遗憾,却没有后悔。如果给我再一次选择职业的机会,我还会再次选择软件!
再过10年,到2013年的时候,我也许会再次写一篇人生的十年回顾,到那时中国的软件会如何?中国软件的明天靠你我这些普通人去扎扎实实地去工作来支撑!少发些牢骚,多做些实事,中国软件才会有光辉的明天。你我共勉!

————————————–一个老程序员的心理话

2006年04月26日

 

PL/SQL单行函数和组函数详解
 函数是一种有零个或多个参数并且有一个返回值的程序。在SQL中Oracle内建了一系列函数,这些函数都可被称为SQL或PL/SQL语句,函数主要分为两大类:

   单行函数

   组函数

  本文将讨论如何利用单行函数以及使用规则。

  SQL中的单行函数

  SQL和PL/SQL中自带很多类型的函数,有字符、数字、日期、转换、和混合型等多种函数用于处理单行数据,因此这些都可被统称为单行函数。这些函数均可用于SELECT,WHERE、ORDER BY等子句中,例如下面的例子中就包含了TO_CHAR,UPPER,SOUNDEX等单行函数。
SELECT ename,TO_CHAR(hiredate,’day,DD-Mon-YYYY’)FROM empWhere UPPER(ename) Like ’AL%’ORDER BY SOUNDEX(ename)

  单行函数也可以在其他语句中使用,如update的SET子句,INSERT的VALUES子句,DELET的WHERE子句,认证考试特别注意在SELECT语句中使用这些函数,所以我们的注意力也集中在SELECT语句中。

  NULL和单行函数

  在如何理解NULL上开始是很困难的,就算是一个很有经验的人依然对此感到困惑。NULL值表示一个未知数据或者一个空值,算术操作符的任何一个操作数为NULL值,结果均为提个NULL值,这个规则也适合很多函数,只有CONCAT,DECODE,DUMP,NVL,REPLACE在调用了 NULL参数时能够返回非NULL值。在这些中NVL函数时最重要的,因为他能直接处理NULL值,NVL有两个参数:NVL(x1,x2),x1和x2 都式表达式,当x1为null时返回X2,否则返回x1。

  下面我们看看emp数据表它包含了薪水、奖金两项,需要计算总的补偿
column name emp_id salary bonuskey type pk nulls/unique nn,u nnfk table datatype number number numberlength 11.2 11.2

  不是简单的将薪水和奖金加起来就可以了,如果某一行是null值那么结果就将是null,比如下面的例子:
update empset salary=(salary+bonus)*1.1

  这个语句中,雇员的工资和奖金都将更新为一个新的值,但是如果没有奖金,即 salary + null,那么就会得出错误的结论,这个时候就要使用nvl函数来排除null值的影响。
所以正确的语句是:
update empset salary=(salary+nvl(bonus,0)*1.1
单行字符串函数

  单行字符串函数用于操作字符串数据,他们大多数有一个或多个参数,其中绝大多数返回字符串

  ASCII()
  c1是一字符串,返回c1第一个字母的ASCII码,他的逆函数是CHR()
SELECT ASCII(‘A’) BIG_A,ASCII(‘z’) BIG_z FROM empBIG_A BIG_z65 122

  CHR(<i>)[NCHAR_CS]
  i是一个数字,函数返回十进制表示的字符
select CHR(65),CHR(122),CHR(223) FROM empCHR65 CHR122 CHR223A z B

  CONCAT(,)
  c1,c2均为字符串,函数将c2连接到c1的后面,如果c1为null,将返回c2.如果c2为null,则返回c1,如果c1、c2都为null,则返回null。他和操作符||返回的结果相同
select concat(’slobo ’,'Svoboda’) username from dualusernameslobo Syoboda

  INITCAP()
  c1为一字符串。函数将每个单词的第一个字母大写其它字母小写返回。单词由空格,控制字符,标点符号限制。
select INITCAP(‘veni,vedi,vici’) Ceasar from dualCeasarVeni,Vedi,Vici

  INSTR(,[,<i>[,]])
  c1,c2均为字符串,i,j为整数。函数返回c2在c1中第j次出现的位置,搜索从c1的第i个字符开始。当没有发现需要的字符时返回0,如果i为负数,那么搜索将从右到左进行,但是位置的计算还是从左到右,i和j的缺省值为1.
select INSTR(‘Mississippi’,'i’,3,3) from dualINSTR(‘MISSISSIPPI’,'I’,3,3)11select INSTR(‘Mississippi’,'i’,-2,3) from dualINSTR(‘MISSISSIPPI’,'I’,3,3)2

  INSTRB(,[,i[,j])
  与INSTR()函数一样,只是他返回的是字节,对于单字节INSTRB()等于INSTR()

  LENGTH()
  c1为字符串,返回c1的长度,如果c1为null,那么将返回null值。
select LENGTH(‘Ipso Facto’) ergo from dualergo10

  LENGTHb()
  与LENGTH()一样,返回字节。

  lower()
  返回c的小写字符,经常出现在where子串中
select LOWER(colorname) from itemdetail WHERE LOWER(colorname) LIKE ’%white%’COLORNAMEWinterwhite

  LPAD(,<i>[,])
  c1,c2均为字符串,i为整数。在c1的左侧用c2字符串补足致长度i,可多次重复,如果i小于c1的长度,那么只返回i那么长的c1字符,其他的将被截去。c2的缺省值为单空格,参见RPAD。
select LPAD(answer,7,”) padded,answer unpadded from question;PADDED UNPADDED Yes YesNO NOMaybe maybe

  LTRIM(,)
  把c1中最左边的字符去掉,使其第一个字符不在c2中,如果没有c2,那么c1就不会改变。
select LTRIM(‘Mississippi’,'Mis’) from dualLTRppi

  RPAD(,<i>[,])
  在c1的右侧用c2字符串补足致长度i,可多次重复,如果i小于c1的长度,那么只返回i那么长的c1字符,其他的将被截去。c2的缺省值为单空格,其他与LPAD相似

  RTRIM(,)
  把c1中最右边的字符去掉,使其第后一个字符不在c2中,如果没有c2,那么c1就不会改变。

  REPLACE(,[,])
  c1,c2,c3都是字符串,函数用c3代替出现在c1中的c2后返回。
select REPLACE(‘uptown’,'up’,'down’) from dualREPLACEdowntown

  STBSTR(,<i>[,])
  c1为一字符串,i,j为整数,从c1的第i位开始返回长度为j的子字符串,如果j为空,则直到串的尾部。
select SUBSTR(‘Message’,1,4) from dualSUBSMess

  SUBSTRB(,<i>[,])
  与SUBSTR大致相同,只是I,J是以字节计算。

  SOUNDEX()
  返回与c1发音相似的词
select SOUNDEX(‘dawes’) Dawes SOUNDEX(‘daws’) Daws, SOUNDEX(‘dawson’) from dualDawes Daws DawsonD200 D200 D250

  TRANSLATE(,,)
  将c1中与c2相同的字符以c3代替
select TRANSLATE(‘fumble’,'uf’,'ar’) test from dualTEXTramble

  TRIM([[]] from c3)
  将c3串中的第一个,最后一个,或者都删除。
select TRIM(‘ space padded ’) trim from dual TRIMspace padded

  UPPER()
  返回c1的大写,常出现where子串中
select name from dual where UPPER(name) LIKE ’KI%’NAMEKING
单行数字函数

  单行数字函数操作数字数据,执行数学和算术运算。所有函数都有数字参数并返回数字值。所有三角函数的操作数和值都是弧度而不是角度,oracle没有提供内建的弧度和角度的转换函数。

  ABS()
  返回n的绝对值

  ACOS()
  反余玄函数,返回-1到1之间的数。n表示弧度
select ACOS(-1) pi,ACOS(1) ZERO FROM dualPI ZERO3.14159265 0

  ASIN()
  反正玄函数,返回-1到1,n表示弧度

  ATAN()
  反正切函数,返回n的反正切值,n表示弧度。

  CEIL()
  返回大于或等于n的最小整数。

  COS()
  返回n的余玄值,n为弧度

  COSH()
  返回n的双曲余玄值,n 为数字。
select COSH(<1.4>) FROM dualCOSH(1.4)2.15089847

  EXP()
  返回e的n次幂,e=2.71828183.

  FLOOR()
  返回小于等于N的最大整数。

  LN()
  返回N的自然对数,N必须大于0

  LOG(,)
  返回以n1为底n2的对数

  MOD()
  返回n1除以n2的余数,

  POWER(,)
  返回n1的n2次方

  ROUND(,)
  返回舍入小数点右边n2位的n1的值,n2的缺省值为0,这回将小数点最接近的整数,如果n2为负数就舍入到小数点左边相应的位上,n2必须是整数。
select ROUND(12345,-2),ROUND(12345.54321,2) FROM dualROUND(12345,-2) ROUND(12345.54321,2)12300 12345.54

  SIGN()
  如果n为负数,返回-1,如果n为正数,返回1,如果n=0返回0.

  SIN()
  返回n的正玄值,n为弧度。

  SINH()
  返回n的双曲正玄值,n为弧度。

  SQRT()
  返回n的平方根,n为弧度

  TAN()
  返回n的正切值,n为弧度

  TANH()
  返回n的双曲正切值,n为弧度

  TRUNC(,)
  返回截尾到n2位小数的n1的值,n2缺省设置为0,当n2为缺省设置时会将n1截尾为整数,如果n2为负值,就截尾在小数点左边相应的位上。

  单行日期函数

  单行日期函数操作DATA数据类型,绝大多数都有DATA数据类型的参数,绝大多数返回的也是DATA数据类型的值。

  ADD_MONTHS(,<i>)
  返回日期d加上i个月后的结果。i可以使任意整数。如果i是一个小数,那么数据库将隐式的他转换成整数,将会截去小数点后面的部分。

  LAST_DAY()
  函数返回包含日期d的月份的最后一天

  MONTHS_BETWEEN(,)
  返回d1和d2之间月的数目,如果d1和d2的日的日期都相同,或者都使该月的最后一天,那么将返回一个整数,否则会返回的结果将包含一个分数。

  NEW_TIME(,,)
  d1是一个日期数据类型,当时区tz1中的日期和时间是d时,返回时区tz2中的日期和时间。tz1和tz2时字符串。

  NEXT_DAY(,)
  返回日期d后由dow给出的条件的第一天,dow使用当前会话中给出的语言指定了一周中的某一天,返回的时间分量与d的时间分量相同。
select NEXT_DAY(‘01-Jan-2000′,’Monday’) "1st Monday",NEXT_DAY(‘01-Nov-2004′,’Tuesday’)+7 "2nd Tuesday") from dual;1st Monday 2nd Tuesday03-Jan-2000 09-Nov-2004

  ROUND([,])
  将日期d按照fmt指定的格式舍入,fmt为字符串。

  SYADATE
  函数没有参数,返回当前日期和时间。

  TRUNC([,])
  返回由fmt指定的单位的日期d.
单行转换函数

  单行转换函数用于操作多数据类型,在数据类型之间进行转换。

  CHARTORWID()
  c 使一个字符串,函数将c转换为RWID数据类型。
SELECT test_id from test_case where rowid=CHARTORWID(‘AAAA0SAACAAAALiAAA’)

  CONVERT(,[,])
  c尾字符串,dset、sset是两个字符集,函数将字符串c由sset字符集转换为dset字符集,sset的缺省设置为数据库的字符集。

  HEXTORAW()
  x为16进制的字符串,函数将16进制的x转换为RAW数据类型。

  RAWTOHEX()
  x是RAW数据类型字符串,函数将RAW数据类转换为16进制的数据类型。

  ROWIDTOCHAR()
  函数将ROWID数据类型转换为CHAR数据类型。

  TO_CHAR([[,)
  x是一个data或number数据类型,函数将x转换成fmt指定格式的char数据类型,如果x为日期nlsparm=NLS_DATE_LANGUAGE 控制返回的月份和日份所使用的语言。如果x为数字nlsparm=NLS_NUMERIC_CHARACTERS 用来指定小数位和千分位的分隔符,以及货币符号。
NLS_NUMERIC_CHARACTERS ="dg", NLS_CURRENCY="string"

  TO_DATE([,[,)
  c表示字符串,fmt表示一种特殊格式的字符串。返回按照fmt格式显示的c,nlsparm表示使用的语言。函数将字符串c转换成date数据类型。

  TO_MULTI_BYTE()
  c表示一个字符串,函数将c的担子截字符转换成多字节字符。

  TO_NUMBER([,[,)
  c表示字符串,fmt表示一个特殊格式的字符串,函数返回值按照fmt指定的格式显示。nlsparm表示语言,函数将返回c代表的数字。

  TO_SINGLE_BYTE()
  将字符串c中得多字节字符转化成等价的单字节字符。该函数仅当数据库字符集同时包含单字节和多字节字符时才使用

  其它单行函数

  BFILENAME(
,)
  dir是一个directory类型的对象,file为一文件名。函数返回一个空的BFILE位置值指示符,函数用于初始化BFILE变量或者是BFILE列。

  DECODE(,,[,,,[])
  x是一个表达式,m1是一个匹配表达式,x与m1比较,如果m1等于x,那么返回r1,否则,x与m2比较,依次类推m3,m4,m5….直到有返回结果。

  DUMP(,[,[,[,]]])
  x是一个表达式或字符,fmt表示8进制、10进制、16进制、或则单字符。函数返回包含了有关x的内部表示信息的VARCHAR2类型的值。如果指定了n1,n2那么从n1开始的长度为n2的字节将被返回。

  EMPTY_BLOB()
  该函数没有参数,函数返回 一个空的BLOB位置指示符。函数用于初始化一个BLOB变量或BLOB列。

  EMPTY_CLOB()
  该函数没有参数,函数返回 一个空的CLOB位置指示符。函数用于初始化一个CLOB变量或CLOB列。

  GREATEST()
  exp_list是一列表达式,返回其中最大的表达式,每个表达式都被隐含的转换第一个表达式的数据类型,如果第一个表达式是字符串数据类型中的任何一个,那么返回的结果是varchar2数据类型,同时使用的比较是非填充空格类型的比较。

  LEAST()
  exp_list是一列表达式,返回其中最小的表达式,每个表达式都被隐含的转换第一个表达式的数据类型,如果第一个表达式是字符串数据类型中的任何一个,将返回的结果是varchar2数据类型,同时使用的比较是非填充空格类型的比较。

  UID
  该函数没有参数,返回唯一标示当前数据库用户的整数。

  USER
  返回当前用户的用户名

  USERENV()
  基于opt返回包含当前会话信息。opt的可选值为:

  ISDBA    会话中SYSDBA脚色响应,返回TRUE
  SESSIONID  返回审计会话标示符ENTRYID   返回可用的审计项标示符
  INSTANCE  在会话连接后,返回实例标示符。该值只用于运行Parallel 服务器并且有 多个实例的情况下使用。
  LANGUAGE  返回语言、地域、数据库设置的字符集。
  LANG    返回语言名称的ISO缩写。
  TERMINAL  为当前会话使用的终端或计算机返回操作系统的标示符。

  VSIZE()
  x是一个表达式。返回x内部表示的字节数。
SQL中的组函数

  组函数也叫集合函数,返回基于多个行的单一结果,行的准确数量无法确定,除非查询被执行并且所有的结果都被包含在内。与单行函数不同的是,在解析时所有的行都是已知的。由于这种差别使组函数与单行函数有在要求和行为上有微小的差异.

  组(多行)函数

  与单行函数相比,oracle提供了丰富的基于组的,多行的函数。这些函数可以在select或select的having子句中使用,当用于select子串时常常都和GROUP BY一起使用。

  AVG([{DISYINCT|ALL}])
  返回数值的平均值。缺省设置为ALL.
SELECT AVG(sal),AVG(ALL sal),AVG(DISTINCT sal) FROM scott.empAVG(SAL) AVG(ALL SAL) AVG(DISTINCT SAL)1877.94118 1877.94118 1916.071413

  COUNT({*|DISTINCT|ALL} )
  返回查询中行的数目,缺省设置是ALL,*表示返回所有的行。

  MAX([{DISTINCT|ALL}])
  返回选择列表项目的最大值,如果x是字符串数据类型,他返回一个VARCHAR2数据类型,如果X是一个DATA数据类型,返回一个日期,如果X是numeric数据类型,返回一个数字。注意distinct和all不起作用,应为最大值与这两种设置是相同的。

  MIN([{DISTINCT|ALL}])
  返回选择列表项目的最小值。

  STDDEV([{DISTINCT|ALL}])
  返回选者的列表项目的标准差,所谓标准差是方差的平方根。

  SUM([{DISTINCT|ALL}])
  返回选择列表项目的数值的总和。

  VARIANCE([{DISTINCT|ALL}])
  返回选择列表项目的统计方差。

  用GROUP BY给数据分组

  正如题目暗示的那样组函数就是操作那些已经分好组的数据,我们告诉数据库用GROUP BY怎样给数据分组或者分类,当我们在SELECT语句的SELECT子句中使用组函数时,我们必须把为分组或非常数列放置在GROUP BY子句中,如果没有用group by进行专门处理,那么缺省的分类是将整个结果设为一类。
select stat,counter(*) zip_count from zip_codes GROUP BY state;ST ZIP_COUNT– ———AK 360AL 1212AR 1309AZ 768CA 3982

  在这个例子中,我们用state字段分类;如果我们要将结果按照zip_codes排序,可以用ORDER BY语句,ORDER BY子句可以使用列或组函数。
select stat,counter(*) zip_count from zip_codes GROUP BY state ORDER BY COUNT(*) DESC;ST COUNT(*)– ——–NY 4312PA 4297TX 4123CA 3982

  用HAVING子句限制分组数据

  现在你已经知道了在查询的SELECT语句和ORDER BY子句中使用主函数,组函数只能用于两个子串中,组函数不能用于WHERE子串中,例如下面的查询是错误的:
错误SELECT sales_clerk,SUN(sale_amount) FROM gross_sales WHERE sales_dept=’OUTSIDE’ AND SUM(sale_amount)>10000 GROUP BY sales_clerk

  这个语句中数据库不知道SUM()是什么,当我们需要指示数据库对行分组,然后限制分组后的行的输出时,正确的方法是使用HAVING语句:
SELECT sales_clerk,SUN(sale_amount) FROM gross_sales WHERE sales_dept=’OUTSIDE’ GROUP BY sales_clerkHAVING SUM(sale_amount)>10000;

  嵌套函数

  函数可以嵌套。一个函数的输出可以是另一个函数的输入。操作数有一个可继承的执行过程。但函数的优先权只是基于位置,函数遵循由内到外,由左到右的原则。嵌套技术一般用于象DECODE这样的能被用于逻辑判断语句IF….THEN…ELSE的函数。


 

数学函数 
  1.绝对值 
  S:select abs(-1) value 
  O:select abs(-1) value from dual 

  2.取整(大) 
  S:select ceiling(-1.001) value 
  O:select ceil(-1.001) value from dual 

  3.取整(小) 
  S:select floor(-1.001) value 
  O:select floor(-1.001) value from dual 

  4.取整(截取) 
  S:select cast(-1.002 as int) value 
  O:select trunc(-1.002) value from dual 

  5.四舍五入 
  S:select round(1.23456,4) value 1.23460 
  O:select round(1.23456,4) value from dual 1.2346 

  6.e为底的幂 
  S:select Exp(1) value 2.7182818284590451 
  O:select Exp(1) value from dual 2.71828182 

  7.取e为底的对数 
  S:select log(2.7182818284590451) value 1 
  O:select ln(2.7182818284590451) value from dual; 1 

  8.取10为底对数 
  S:select log10(10) value 1 
  O:select log(10,10) value from dual; 1 

  9.取平方 
  S:select SQUARE(4) value 16 
  O:select power(4,2) value from dual 16 

  10.取平方根 
  S:select SQRT(4) value 2 
  O:select SQRT(4) value from dual 2 

  11.求任意数为底的幂 
  S:select power(3,4) value 81 
  O:select power(3,4) value from dual 81 

  12.取随机数 
  S:select rand() value 
  O:select sys.dbms_random.value(0,1) value from dual; 

  13.取符号 
  S:select sign(-8) value -1 
  O:select sign(-8) value from dual -1 

  14.圆周率 
  S:SELECT PI() value 3.1415926535897931 
  O:不知道 

  15.sin,cos,tan 参数都以弧度为单位 
  例如:select sin(PI()/2) value 得到1(SQLServer) 

  16.Asin,Acos,Atan,Atan2 返回弧度 

  17.弧度角度互换(SQLServer,Oracle不知道) 
  DEGREES:弧度-〉角度 
  RADIANS:角度-〉弧度 

数值间比较 

  18. 求集合最大值 
  S:select max(value) value from 
  (select 1 value 
  union 
  select -2 value 
  union 
  select 4 value 
  union 
  select 3 value)a 

  O:select greatest(1,-2,4,3) value from dual 

  19. 求集合最小值 
  S:select min(value) value from 
  (select 1 value 
  union 
  select -2 value 
  union 
  select 4 value 
  union 
  select 3 value)a 

  O:select least(1,-2,4,3) value from dual 

  20.如何处理null值(F2中的null以10代替) 
  S:select F1,IsNull(F2,10) value from Tbl 
  O:select F1,nvl(F2,10) value from Tbl 

  21.求字符序号 
  S:select ascii(‘a’) value 
  O:select ascii(‘a’) value from dual 

  22.从序号求字符 
  S:select char(97) value 
  O:select chr(97) value from dual 

  23.连接 
  S:select ’11′+’22′+’33′ value 
  O:select CONCAT(‘11′,’22′)  33 value from dual 

23.子串位置 –返回3 
  S:select CHARINDEX(’s’,’sdsq’,2) value 
  O:select INSTR(’sdsq’,’s’,2) value from dual 

  23.模糊子串的位置 –返回2,参数去掉中间%则返回7 
  S:select patindex(‘%d%q%’,’sdsfasdqe’) value 
  O:oracle没发现,但是instr可以通过第四个参数控制出现次数 
  select INSTR(’sdsfasdqe’,’sd’,1,2) value from dual 返回6 

  24.求子串 
  S:select substring(‘abcd’,2,2) value 
  O:select substr(‘abcd’,2,2) value from dual 

  25.子串代替 返回aijklmnef 
  S:SELECT STUFF(‘abcdef’, 2, 3, ’ijklmn’) value 
  O:SELECT Replace(‘abcdef’, ’bcd’, ’ijklmn’) value from dual 

  26.子串全部替换 
  S:没发现 
  O:select Translate(‘fasdbfasegas’,'fa’,'我’ ) value from dual 

  27.长度 
  S:len,datalength 
  O:length 

  28.大小写转换 lower,upper 

  29.单词首字母大写 
  S:没发现 
  O:select INITCAP(‘abcd dsaf df’) value from dual 

  30.左补空格(LPAD的第一个参数为空格则同space函数) 
  S:select space(10)+’abcd’ value 
  O:select LPAD(‘abcd’,14) value from dual 

  31.右补空格(RPAD的第一个参数为空格则同space函数) 
  S:select ’abcd’+space(10) value 
  O:select RPAD(‘abcd’,14) value from dual 

  32.删除空格 
  S:ltrim,rtrim 
  O:ltrim,rtrim,trim 

  33. 重复字符串 
  S:select REPLICATE(‘abcd’,2) value 
  O:没发现 

  34.发音相似性比较(这两个单词返回值一样,发音相同) 
  S:SELECT SOUNDEX (‘Smith’), SOUNDEX (‘Smythe’) 
  O:SELECT SOUNDEX (‘Smith’), SOUNDEX (‘Smythe’) from dual 
  SQLServer中用SELECT DIFFERENCE(‘Smithers’, ’Smythers’) 比较soundex的差 
  返回0-4,4为同音,1最高 

日期函数 
  35.系统时间 
  S:select getdate() value 
  O:select sysdate value from dual 

  36.前后几日 
  直接与整数相加减 

  37.求日期 
  S:select convert(char(10),getdate(),20) value 
  O:select trunc(sysdate) value from dual 
  select to_char(sysdate,’yyyy-mm-dd’) value from dual 

  38.求时间 
  S:select convert(char(8),getdate(),108) value 
  O:select to_char(sysdate,’hh24:mm:ss’) value from dual 

39.取日期时间的其他部分 
  S:DATEPART 和 DATENAME 函数 (第一个参数决定) 
  O:to_char函数 第二个参数决定 

  参数———————————下表需要补充 
  year yy, yyyy 
  quarter qq, q (季度) 
  month mm, m (m O无效) 
  dayofyear dy, y (O表星期) 
  day dd, d (d O无效) 
  week wk, ww (wk O无效) 
  weekday dw (O不清楚) 
  Hour hh,hh12,hh24 (hh12,hh24 S无效) 
  minute mi, n (n O无效) 
  second ss, s (s O无效) 
  millisecond ms (O无效) 
  ———————————————- 

  40.当月最后一天 
  S:不知道 
  O:select LAST_DAY(sysdate) value from dual 

  41.本星期的某一天(比如星期日) 
  S:不知道 
  O:SELECT Next_day(sysdate,7) vaule FROM DUAL; 

  42.字符串转时间 
  S:可以直接转或者select cast(‘2004-09-08′as datetime) value 
  O:SELECT To_date(‘2004-01-05 22:09:38′,’yyyy-mm-dd hh24-mi-ss’) vaule FROM DUAL; 

  43.求两日期某一部分的差(比如秒) 
  S:select datediff(ss,getdate(),getdate()+12.3) value 
  O:直接用两个日期相减(比如d1-d2=12.3) 
  SELECT (d1-d2)*24*60*60 vaule FROM DUAL; 

  44.根据差值求新的日期(比如分钟) 
  S:select dateadd(mi,8,getdate()) value 
  O:SELECT sysdate+8/60/24 vaule FROM DUAL; 

  45.求不同时区时间 
  S:不知道 
  O:SELECT New_time(sysdate,’ydt’,'gmt’ ) vaule FROM DUAL; 

  —–时区参数,北京在东8区应该是Ydt——- 
  AST ADT 大西洋标准时间 
  BST BDT 白令海标准时间 
  CST CDT 中部标准时间 
  EST EDT 东部标准时间 
  GMT 格林尼治标准时间 
  HST HDT 阿拉斯加?夏威夷标准时间 
  MST MDT 山区标准时间 
  NST 纽芬兰标准时间 
  PST PDT 太平洋标准时间 
  YST YDT YUKON标准时间