SQL /数据库 使用蓝皮书

作者: archonwang
标题: SQL /数据库 使用蓝皮书
关键字: SQL语句
分类: 开发经验
密级: 公开
(评分: ★★ , 回复: 7, 阅读: 22443)  »»

   SQL语言由命令、子句、运算和集合函数等构成。在SQL中,数据定义语言DDL(用来建立及定义数据表、字段以及索引等数据库结构)包含的命令有CREATE、DROP、ALTER;数据操纵语言DML(用来提供数据的查询、排序以及筛选数据等功能)包含的命令有SELECT、INSERT、UPDATE、DELETE。

一、SQL语句

(1)Select 查询语句
语法:SELECT [ALL|DISTINCT]  <目标列表达式> [AS 列名]
 [,<目标列表达式> [AS 列名] ...] FROM <表名> [,<表名>…]
[WHERE <条件表达式> [AND|OR  <条件表达式>...]
[GROUP BY 列名 [HAVING <条件表达式>]]
[ORDER BY 列名 [ASC | DESC]]
解释:[ALL|DISTINCT]   ALL:全部; DISTINCT:不包括重复行
<目标列表达式>  对字段可使用AVG、COUNT、SUM、MIN、MAX、运算符等
<条件表达式>  
查询条件  谓词
比较  =、>,<,>=,<=,!=,<>,
确定范围  BETWEEN AND、NOT BETWEEN AND
确定集合  IN、NOT IN
字符匹配  LIKE(“%”匹配任何长度,“_”匹配一个字符)、NOT LIKE
空值  IS NULL、IS NOT NULL
子查询  ANY、ALL、EXISTS
集合查询  UNION(并)、INTERSECT(交)、MINUS(差)
多重条件  AND、OR、NOT
 对查询结果分组
[HAVING <条件表达式>]  分组筛选条件
[ORDER BY 列名 [ASC | DESC]]  对查询结果排序;ASC:升序 DESC:降序
例1: select student.sno as 学号, student.name as 姓名, course as 课程名, score as 成绩 from score,student where student.sid=score.sid and score.sid=:sid
例2:select student.sno as 学号, student.name as 姓名,AVG(score) as 平均分  from score,student where student.sid=score.sid and student.class=:class and (term=5 or term=6)  group by student.sno, student.name  having count(*)>0 order by 平均分 DESC
例3:select * from score where sid like '9634'
例4:select * from student where class in (select class from student where name='陈小小')

(2)INSERT插入语句
语法:INSERT INTO  <表名> [(<字段名1> [,<字段名2>, ...])]  VALUES (<常量1> [,<常量2>, ...])
语法:INSERT INTO  <表名> [(<字段名1> [,<字段名2>, ...])] 子查询
例子:INSERT INTO  借书表(rid,bookidx,bdate)VALUES (edit1.text,edit2.text,date)
例子:INSERT INTO score1(sno,name)  SELECT sno,name  FROM student  WHERE class=’9634’

(3)UPDATE-SQL
语法:UPDATE 〈表名〉
SET 列名1 = 常量表达式1[,列名2 = 常量表达式2 ...]
 WHERE <条件表达式> [AND|OR  <条件表达式>...]
例子:update score set credithour=4 where course='数据库'

(4)DELETE-SQL
语法:DELETE FROM〈表名〉[WHERE <条件表达式> [AND|OR  <条件表达式>...]]
例子:Delete from student where sid='003101'

(5)CREATE TABLE
CREATE TABLE | DBF TableName1 [NAME LongTableName] [FREE]
 (FieldName1 FieldType [(nFieldWidth [, nPrecision])]
   [NULL | NOT NULL]
   [CHECK lExpression1 [ERROR cMessageText1]]
   [DEFAULT eExpression1]
   [PRIMARY KEY | UNIQUE]
   [REFERENCES TableName2 [TAG TagName1]]
   [NOCPTRANS]
 [, FieldName2 ...]
   [, PRIMARY KEY eExpression2 TAG TagName2
|, UNIQUE eExpression3 TAG TagName3]
   [, FOREIGN KEY eExpression4 TAG TagName4 [NODUP]
     REFERENCES TableName3 [TAG TagName5]]
   [, CHECK lExpression2 [ERROR cMessageText2]])
| FROM ARRAY ArrayName

(6)ALTER TABLE
ALTER TABLE TableName1
 ADD | ALTER [COLUMN] FieldName1
   FieldType [(nFieldWidth [, nPrecision])]
   [NULL | NOT NULL]
   [CHECK lExpression1 [ERROR cMessageText1]]
   [DEFAULT eExpression1]
   [PRIMARY KEY | UNIQUE]
   [REFERENCES TableName2 [TAG TagName1]]
   [NOCPTRANS]

(7)DROP TABLE
DROP TABLE [路径名.]表名

(8)CREATE INDEX
CREATE INDEX index-name ON table-name(column[,column…])
例:CREATE INDEX uspa ON 口令表(user,password)

(9)DROP INDEX
DROP INDEX table-name.index-name|PRIMARY
例:DROP INDEX 口令表.uspa

二、在程序中使用静态SQL语句
    在程序设计阶段,将SQL命令文本作为TQuery组件的SQL属性值设置。

三、在程序中使用动态SQL语句
    动态SQL语句是指在SQL语句中包含有参数变量的SQL语句(如:select * from student where class=:class),在程序中可以为参数赋值。给参数赋值的方法有:

    1、利用参数编辑器为参数赋值
    选中TQuery组件,在对象监视器OI中点取Params项,在弹出的参数编辑窗口中设置参数的值。
例:SELECT bookidx AS 书号,藏书表.bookname AS 书名, bdate AS 借书日期  FROM 借书表,藏书表 where 借书表.bookidx=藏书表.bookidx and rid=:rid

    2、在程序运行中通过程序为参数赋值
  (1)根据参数在SQL语句中出现的顺序,使用TQuery的Params属性为参数赋值;

例:在借书表中插入一条记录
with  Query1 do
begin
   SQL.clear;
   SQL.add('Insert  Into  借书表(bookidx,rid,rdate)');
   SQl.add('Values(:bookidx,:rid,:rdate)');
   Params[0].AsString := bookidxEdit.Text;
   Params[1].AsString := ridEdit.Text;
   Params[2] .AsDate:=date;
   ExecSQL;
End;

(2)根据SQL语句中的参数名字,调用ParamByName方法为参数赋值;
ParamByName('bookidx').AsString := bookidxEdit.Text;
ParamByName('rid').AsString := ridEdit.Text;
ParamByName('rdate') .AsDate:=date;
ExecSQL;
有:AsString 、AsSmallInt 、AsInteger 、AsWord 、AsBoolean 、AsFloat 、AsCurrency 、AsBCD 、AsDate 、AsTime 、AsDateTime转换函数

3、使用数据源为参数赋值
    把TQuery的DataSource属性设置为另一个数据源(T DataSource名字),Delphi会把未赋值的参数与指定的数据源中的各字段相比较,并将匹配的字段的值赋给未赋值的参数,可实现主表—明细表应用。

四、对TQuery返回的数据集进行修改
    一般情况下,TQuery返回的数据集是只读的,不能修改;
    对不包含集操作(如:SUM、COUNT)的单表SELECT查询,设置TQuery的RequsetLive属性为True,则可修改TQuery返回的数据集。

var
  I: Integer;
  ListItem: string;
begin
  for I := 0 to Query1.ParamCount - 1 do
  begin
    ListItem := ListBox1.Items[I];
    case Query1.Params[I].DataType of
      ftString:
        Query1.Params[I].AsString := ListItem;
      ftSmallInt:
        Query1.Params[I].AsSmallInt := StrToIntDef(ListItem, 0);
      ftInteger:
        Query1.Params[I].AsInteger := StrToIntDef(ListItem, 0);
      ftWord:
        Query1.Params[I].AsWord := StrToIntDef(ListItem, 0);
      ftBoolean:
        begin
          if ListItem = 'True' then
            Query1.Params[I].AsBoolean := True
          else
            Query1.Params[I].AsBoolean := False;
        end;
      ftFloat:
        Query1.Params[I].AsFloat := StrToFloat(ListItem);
      ftCurrency:
        Query1.Params[I].AsCurrency := StrToFloat(ListItem);
      ftBCD:
        Query1.Params[I].AsBCD := StrToCurr(ListItem);
      ftDate:
        Query1.Params[I].AsDate := StrToDate(ListItem);
      ftTime:
        Query1.Params[I].AsTime := StrToTime(ListItem);
      ftDateTime:
        Query1.Params[I].AsDateTime := StrToDateTime(ListItem);
    end;
  end;
end;


2003-11-25 9:59:00   
查看评语»»»   
 2003-11-25 10:06:20    运行期间对数据库表的


一、数据集表的打开与关闭
  打开:设置数据集组件的Active属性为True或调用数据集组件的Open方法
  关闭:设置数据集组件的Active属性为False或调用数据集组件的Close方法

二、创建数据库应用程序
利用向导创建:使用Database菜单/Form Wizard选项;
创建主从表:设置从表的MasterSource、MasterField属性;
创建查询表:使用TQuery组件;

三、数据库表记录的定位
使用TDBNavigator组件;
调用数据集组件的First、Next、Prior、Last方法;
数据集组件的EOF属性(或BOF属性)用来判断记录指针是否指向第一条记录(或最后一条记录);
使用数据集的书签BookMark(GetBookMark:获得当前记录的BookMark记号;GotoBookMark:从当前记录直接转到指定BookMark的那条记录;FreeBookMark:释放某个BookMark)
使用GotoKey、FindKey方法查找记录进行定位;

四、数据库表字段对象的使用

  (1)创建永久的字段对象
    双击或单击再右击TTable(TQuery)对象打开字段编辑器,使用其弹出菜单增加字段对象、删除字段对象、定义新的字段对象(字段编辑器的弹出菜单的New Fields选项,可创建计算字段);

  (2)字段对象的属性、方法、事件
    字段对象名:如Table1Name、Query1Sid
    属性:Alignment(对齐方式)、Calculated(是否是从其它字段值计算得到)、DisplayLabel(显示的标题)、DisplayWidth(显示的宽度)、DisplayFormat(显示的格式)、EditMask(输入的限制)、FieldName(字段名)、ReadOnly(是否只读)、Visible(是否显示)
    事件:OnChange(字段值发生变化时触发)、OnGetText(当字段对象获得字段值时触发)、OnSetText(当字段对象被设置字段值时触发)、OnValiData(当修改、插入、进行有效性检验时触发)

  (3)字段对象的类型转换
    有:AsString 、AsSmallInt 、AsInteger 、AsWord 、AsBoolean 、AsFloat 、AsCurrency 、AsBCD 、AsDate 、AsTime 、AsDateTime转换函数
如:Edit1.Text:=Table1Name.Value ;
Table1Bdate.AsString:=DateToStr(DATE) ;

  (4)对字段对象的访问
    动态字段对象的访问:Table1.Fields[0]. DisplayLabel:= '学生编号'
Table1.FieldByName('Sid'). DisplayLabel:= '学生编号' Table1.Fields[0].Assignment:=taCenter
Edit1.Text:= Table1.FieldByName('Sid').AsString
    永久字段对象的访问:Query1Sid.DisplayLabel:= '学生编号'  
Query1Sid.DisplayWidth:= 12

五、对数据库表数据的操作方法

  (1)访问表中某一字段的数据的方法:
Table1.FieldByName('bookidx').AsString
Table1.Field[0].AsInteger
Table1.Fieldvalues['bookidx']

  (2)数据库表的一些属性:
当前记录号:Table1.Recno
记录总数:Table1.RecordCount
得到表的字段名:Table1.GetFieldNames(ListBox1。Items)

  (3)数据维护的方法:
Edit方法:把数据集设置为编辑状态;
Append方法:把数据集设置为插入状态(最后);
Insert方法:把数据集设置为插入状态(当前记录后);
Post方法:把修改的记录写回数据集;
Cancel方法:取消当前的操作;
Delete方法:删除表中当前记录;
AppendRecord方法:
InsertRecord方法:table1.InsertRecord(['963409', NIL, NIL,'考试']);
SetRecords方法:
Abort方法:取消各种方法的调用;

  (4)输入数据的合法性验证
    对数据库表建立合法性验证机制(如在DBD设置表的Validity Check、Table Lookup、Referential Integrity等属性);
  在字段编辑表Fields Editor(双击Ttable对象),选择字段,编写其OnValidate事件,要求非空可设置其Required属性为True;
  在程序中防止不合法输入(如:使用TDBcombobox对象,TDBlookupcombobox对象);

六、数据检索
  (1)利用索引排序
    如:TABLE1.IndexName:='uspa'  或TABLE1.IndexFieldNames:='user_id'
  (2)使用GotoKey方法查找数据库中的记录
  要求查找字段建立了索引,非主索引要设置Ttable对象的IndexName属性。
  调用SetKey方法,把要查找的Ttable对象置成查找模块;
  把查找值送进被查找的Field的查找缓冲区;
  调用Ttable对象的GotoKey方法,测试该方法的返回值判断查找是否成功;
  (3)使用FindKey方法查找数据库中的记录
    把查找值作为参数传递给FindKey函数,允许有多个查找值,要求把要查找的多个字段的索引名赋给Ttable对象的IndexName属性;
  (4)不精确查找
    GotoNearest方法
    FindNearest方法
  (5)使用Locate方法查找数据库中的记录(不用建索引)
    table1.locate(‘字段名1;字段名2’,VarArroyof([‘值1’,‘值2’]),[LoCaseInsensitive,LoPartialKey])
    LoCaseInsensitive:忽略大小写;IoPartialKey:不精确查找
  (6)设定查找范围的方法
    SetRangeStart、SetRangeEnd、EditRangeStart、EditRangeEnd、SetRange([Start Values],[End Value])、ApplyRange、CancelRange
  (7)用TQuery组件的动态SQL语句进行查找

七、修改数据库中的记录
    在程序中对数据库记录进行操作可按下列的步骤进行:
  (1)移动数据指针到要修改的记录;
  (2)调用Edit方法将Ttable组件设置成编辑状态;
  (3)修改字段值;(Table1.Fieldvalues['字段名']:=值、Table1.Field[0].AsString:=值)
  (4)可用Nil对字段赋空值;
  (5)调用Post方法将修改后的记录写入数据库;

八、插入和删除记录
    删除:移动指针到相应记录处,调用Delete方法;
    插入:调用Insert、InsertRecord方法(当前记录处插入)或Append、InsertRecord方法(表的末尾插入);

 
 2003-11-25 10:11:12    动态的添加PARADOX表的方法【王寒松】

下面给出的函数 AddMasterPassword 完成添加PARADOX表主口令的工作
AddMasterPassword(Table1, 'MyNewPassword')
  procedure AddMasterPassword(Table: TTable; pswd: string);
  const
    RESTRUCTURE_TRUE = WordBool(1);
  var
    TblDesc: CRTblDesc;
    hDb: hDBIDb;
  begin
    {表打开?表是独占吗?}
    if (Table.Active = False) or (Table.Exclusive = False) then
      raise EDatabaseError.Create('数据表必须在独占方式才可以添加口令');
    {初始化表描述区 }
    FillChar(TblDesc, SizeOf(CRTblDesc), 0);
    with TblDesc do
    begin
      { 把表名放到描述区 }
      StrPCopy(szTblName, Table.TableName);
      { 把表类型放到描述区 }
      StrCopy(szTblType, szPARADOX);
      StrPCopy(szPassword, pswd);
      { 设置BPROTECTED为TRUE }
      bProtected := RESTRUCTURE_TRUE;
    end;
    { 从当前的HANDLE里得到DATABASE的HANDLE }
    Check(DbiGetObjFromObj(hDBIObj(Table.Handle), objDATABASE, hDBIObj(hDb)));
    { 关闭表 }
    Table.Close;
    { 添加主口令到PARADOX表里}
    Check(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, FALSE));
     {添加一个新口令到SESSION}
    Session.AddPassword(pswd);
    {重新打开表 }
    Table.Open;
  end;
添加副口令的办法与此类似

 
 2003-11-25 11:21:29    如何选择一个好的数据库【三大数据库比较】

 
【开放性】

SQL Server
    只能在windows 上运行,没有丝毫的开放性,操作系统的系统的稳定对数据库是十分重要的。Windows9X系列产品是偏重于桌面应用,NT server只适合中小型企业。而且windows平台的可靠性,安全性和伸缩性是非常有限的。它不象unix那样久经考验,尤其是在处理大数据量的关键业务时.

Oracle
    能在所有主流平台上运行(包括 windows)。完全支持所有的工业标准。采用完全开放策略。可以使客户选择最适合的解决方案。对开发商全力支持。

DB2
    能在所有主流平台上运行(包括windows)。最适于海量数据。DB2在企业级的应用最为广泛,在全球的500家最大的企业中,几乎85%以上用DB2数据库服务器,而国内到97年约占5%.


【可伸缩性,并行性】

SQL server
    并行实施和共存模型并不成熟。很难处理日益增多的用户数和数据卷。伸缩性有限。

Oracle
    平行服务器通过使一组结点共享同一簇中的工作来扩展windownt的能力,提供高可用性和高伸缩性的簇的解决方案。如果windowsNT不能满足需要, 用户可以把数据库移到UNIX中。

DB2    
    DB2具有很好的并行性。DB2把数据库管理扩充到了并行的、多节点的环境.数据库分区是数据库的一部分,包含自己的数据、索引、配置文件、和事务日志。数据库分区有时被称为节点或数据库节点


【安全性】

SQL server
    没有获得任何安全证书。

Oracle Server
    获得最高认证级别的ISO标准认证。

DB2
    获得最高认证级别的ISO标准认证。


【性能】

SQL Server
    多用户时性能不佳

Oracle
    性能最高, 保持windowsNT下的TPC-D和TPC-C的世界记录。

DB2
    适用于数据仓库和在线事物处理,性能较高。


【客户端支持及应用模式】

SQL Server
    C/S结构,只支持windows客户,可以用ADO,DAO,OLEDB,ODBC连接.

Oracle
    多层次网络计算,支持多种工业标准,可以用ODBC,JDBC,OCI等网络客户连接

DB2
    跨平台,多层结构,支持ODBC,JDBC等客户


【操作简便】

SQL Server
    操作简单,但只有图形界面.

Oracle
    较复杂, 同时提供GUI和命令行,在windowsNT和unix下操作相同

DB2
    操作简单,同时提供GUI和命令行,在windowsNT和unix下操作相同


【使用风险】

SQL server
    完全重写的代码,经历了长期的测试,不断延迟,许多功能需要时间来证明。并不十分兼容早期产品。使用需要冒一定风险。

Oracle
    长时间的开发经验,完全向下兼容。得到广泛的应用。完全没有风险。

DB2
    在巨型企业得到广泛的应用,向下兼容性好。风险小。

 
 2003-11-25 11:25:37    SQL查询语句使用

一、简单查询
    简单的Transact-SQL查询只包括选择列表、FROM子句和WHERE子句。它们分别说明所查询列、查询的表或视图、以及搜索条件等。
    例如,下面的语句查询testtable表中姓名为“张三”的nickname字段和email字段。
    SELECT nickname,email
    FROM testtable
    WHERE name='张三'

(一)选择列表
    选择列表(select_list)指出所查询列,它可以是一组列名列表、星号、表达式、变量(包括局部变量和全局变量)等构成。

    1、选择所有列
    例如,下面语句显示testtable表中所有列的数据:
    SELECT *
    FROM testtable
 
    2、选择部分列并指定它们的显示次序
    查询结果集合中数据的排列顺序与选择列表中所指定的列名排列顺序相同。
    例如:
    SELECT nickname,email
    FROM testtable
 
    3、更改列标题
    在选择列表中,可重新指定列标题。定义格式为:
    列标题=列名
    列名 列标题
    如果指定的列标题不是标准的标识符格式时,应使用引号定界符,例如,下列语句使用汉字显示列标题:
    SELECT 昵称=nickname,电子邮件=email
    FROM testtable
 
    4、删除重复行
    SELECT语句中使用ALL或DISTINCT选项来显示表中符合条件的所有行或删除其中重复的数据行,默认为ALL。使用DISTINCT选项时,对于所有重复的数据行在SELECT返回的结果集合中只保留一行。
 
    5、限制返回的行数
    使用TOP n [PERCENT]选项限制返回的数据行数,TOP n说明返回n行,而TOP n PERCENT时,说明n是表示一百分数,指定返回的行数等于总行数的百分之几。
    例如:
    SELECT TOP 2 *
    FROM testtable
    SELECT TOP 20 PERCENT *
    FROM testtable

(二)FROM子句
    FROM子句指定SELECT语句查询及与查询相关的表或视图。在FROM子句中最多可指定256个表或视图,它们之间用逗号分隔。
    在FROM子句同时指定多个表或视图时,如果选择列表中存在同名列,这时应使用对象名限定这些列所属的表或视图。例如在usertable和citytable表中同时存在cityid列,在查询两个表中的cityid时应使用下面语句格式加以限定:

    SELECT username,citytable.cityid
      FROM usertable,citytable
      WHERE usertable.cityid=citytable.cityid
    在FROM子句中可用以下两种格式为表或视图指定别名:
    表名 as 别名
    表名 别名

    例如上面语句可用表的别名格式表示为:
    SELECT username,b.cityid
      FROM usertable a,citytable b
      WHERE a.cityid=b.cityid
   
    SELECT不仅能从表或视图中检索数据,它还能够从其它查询语句所返回的结果集合中查询数据。
    例如:
    SELECT a.au_fname+a.au_lname
      FROM authors a,titleauthor ta
        (SELECT title_id,title
           FROM titles
           WHERE ytd_sales>10000
        ) AS t
      WHERE a.au_id=ta.au_id
        AND ta.title_id=t.title_id
    此例中,将SELECT返回的结果集合给予一别名t,然后再从中检索数据。

(三)使用WHERE子句设置查询条件
    WHERE子句设置查询条件,过滤掉不需要的数据行。例如下面语句查询年龄大于20的数据:
    SELECT *
      FROM usertable
      WHERE age>20
 
    WHERE子句可包括各种条件运算符:
    比较运算符(大小比较):>、>=、=、<、<=、<>、!>、!<
    范围运算符(表达式值是否在指定的范围):BETWEEN…AND…
                                          NOT BETWEEN…AND…
    列表运算符(判断表达式是否为列表中的指定项):IN (项1,项2……)
                                                NOT IN (项1,项2……)
    模式匹配符(判断值是否与指定的字符通配格式相符):LIKE、NOT LIKE
    空值判断符(判断表达式是否为空):IS NULL、NOT IS NULL
    逻辑运算符(用于多条件的逻辑连接):NOT、AND、OR
 
    1、范围运算符例:age BETWEEN 10 AND 30相当于age>=10 AND age<=30
    2、列表运算符例:country IN ('Germany','China')
    3、模式匹配符例:常用于模糊查找,它判断列值是否与指定的字符串格式相匹配。可用于char、varchar、text、ntext、datetime和smalldatetime等类型查询。
    可使用以下通配字符:
    百分号%:可匹配任意类型和长度的字符,如果是中文,请使用两个百分号即%%。
    下划线_:匹配单个任意字符,它常用来限制表达式的字符长度。
    方括号[]:指定一个字符、字符串或范围,要求所匹配对象为它们中的任一个。
    [^]:其取值也[] 相同,但它要求所匹配对象为指定字符以外的任一个字符。
 
    例如:
    限制以Publishing结尾,使用LIKE '%Publishing'
    限制以A开头:LIKE '[A]%'
    限制以A开头外:LIKE '[^A]%'
   
    4、空值判断符例WHERE age IS NULL
 
    5、逻辑运算符:优先级为NOT、AND、OR

(四)查询结果排序
    使用ORDER BY子句对查询返回的结果按一列或多列排序。ORDER BY子句的语法格式为:
    ORDER BY {column_name [ASC|DESC]} [,…n]
    其中ASC表示升序,为默认值,DESC为降序。ORDER BY不能按ntext、text和image数据类型进行排序。
    例如:
    SELECT *
      FROM usertable
      ORDER BY age desc,userid ASC
    另外,可以根据表达式进行排序。

二、联合查询
    UNION运算符可以将两个或两个以上上SELECT语句的查询结果集合合并成一个结果集合显示,即执行联合查询。UNION的语法格式为:
    select_statement
      UNION [ALL] selectstatement
        [UNION [ALL] selectstatement][…n]
    其中selectstatement为待联合的SELECT查询语句。
    ALL选项表示将所有行合并到结果集合中。不指定该项时,被联合查询结果集合中的重复行将只保留一行。
    联合查询时,查询结果的列标题为第一个查询语句的列标题。因此,要定义列标题必须在第一个查询语句中定义。要对联合查询结果排序时,也必须使用第一查询语句中的列名、列标题或者列序号。
    在使用UNION 运算符时,应保证每个联合查询语句的选择列表中有相同数量的表达式,并且每个查询选择表达式应具有相同的数据类型,或是可以自动将它们转换为相同的数据类型。在自动转换时,对于数值类型,系统将低精度的数据类型转换为高精度的数据类型。
    在包括多个查询的UNION语句中,其执行顺序是自左至右,使用括号可以改变这一执行顺序。例如:
    查询1 UNION (查询2 UNION 查询3)

三、连接查询
    通过连接运算符可以实现多个表查询。连接是关系数据库模型的主要特点,也是它区别于其它类型数据库管理系统的一个标志。
    在关系数据库管理系统中,表建立时各数据之间的关系不必确定,常把一个实体的所有信息存放在一个表中。当检索数据时,通过连接操作查询出存放在多个表中的不同实体的信息。连接操作给用户带来很大的灵活性,他们可以在任何时候增加新的数据类型。为不同实体创建新的表,尔后通过连接进行查询。
    连接可以在SELECT 语句的FROM子句或WHERE子句中建立,似是而非在FROM子句中指出连接时有助于将连接操作与WHERE子句中的搜索条件区分开来。所以,在Transact-SQL中推荐使用这种方法。
    SQL-92标准所定义的FROM子句的连接语法格式为:
    FROM join_table join_type join_table
      [ON (join_condition)]
    其中join_table指出参与连接操作的表名,连接可以对同一个表操作,也可以对多表操作,对同一个表操作的连接又称做自连接。
    join_type 指出连接类型,可分为三种:内连接、外连接和交叉连接。内连接(INNER JOIN)使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行。根据所使用的比较方式不同,内连接又分为等值连接、自然连接和不等连接三种。
    外连接分为左外连接(LEFT OUTER JOIN或LEFT JOIN)、右外连接(RIGHT OUTER JOIN或RIGHT JOIN)和全外连接(FULL OUTER JOIN或FULL JOIN)三种。与内连接不同的是,外连接不只列出与连接条件相匹配的行,而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的数据行。
    交叉连接(CROSS JOIN)没有WHERE 子句,它返回连接表中所有数据行的笛卡尔积,其结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
    连接操作中的ON (join_condition) 子句指出连接条件,它由被连接表中的列和比较运算符、逻辑运算符等构成。
    无论哪种连接都不能对text、ntext和image数据类型列进行直接连接,但可以对这三种列进行间接连接。例如:
    SELECT p1.pub_id,p2.pub_id,p1.pr_info
      FROM pub_info AS p1 INNER JOIN pub_info AS p2
      ON DATALENGTH(p1.pr_info)=DATALENGTH(p2.pr_info)
 
    (一)内连接
    内连接查询操作列出与连接条件匹配的数据行,它使用比较运算符比较被连接列的列值。内连接分三种:
    1、等值连接:在连接条件中使用等于号(=)运算符比较被连接列的列值,其查询结果中列出被连接表中的所有列,包括其中的重复列。
    2、不等连接: 在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。这些运算符包括>、>=、<=、<、!>、!<和<>。
    3、自然连接:在连接条件中使用等于(=)运算符比较被连接列的列值,但它使用选择列表指出查询结果集合中所包括的列,并删除连接表中的重复列。
    例,下面使用等值连接列出authors和publishers表中位于同一城市的作者和出版社:
    SELECT *
      FROM authors AS a INNER JOIN publishers AS p
      ON a.city=p.city
 
    又如使用自然连接,在选择列表中删除authors 和publishers 表中重复列(city和state):
    SELECT a.*,p.pub_id,p.pub_name,p.country
      FROM authors AS a INNER JOIN publishers AS p
      ON a.city=p.city
 
    (二)外连接
    内连接时,返回查询结果集合中的仅是符合查询条件( WHERE 搜索条件或 HAVING 条件)和连接条件的行。而采用外连接时,它返回到查询结果集合中的不仅包含符合连接条件的行,而且还包括左表(左外连接时)、右表(右外连接时)或两个边接表(全外连接)中的所有数据行。
    如下面使用左外连接将论坛内容和作者信息连接起来:
    SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b
      ON a.username=b.username
 
    下面使用全外连接将city表中的所有作者以及user表中的所有作者,以及他们所在的城市:
    SELECT a.*,b.*
      FROM city as a FULL OUTER JOIN user as b
      ON a.username=b.username
 
    (三)交叉连接
    交叉连接不带WHERE 子句,它返回被连接的两个表所有数据行的笛卡尔积,返回到结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
    例,titles表中有6类图书,而publishers表中有8家出版社,则下列交叉连接检索到的记录数将等于6*8=48行。
    SELECT type,pub_name
      FROM titles CROSS JOIN publishers
      ORDER BY type

 
 2003-11-25 11:31:30    SQL Server中Image Data Type的使用技巧 【国商网络有限公司 蒋心武】

   MS SQL Server 是微软公司推出的大型数据库软件,在NT平台上拥有近一半的数据库市场,特别是在SQL Server 7.0推出后,其发展势头更加迅猛。SQL Server中Image数据类型是用来存储图形的。在此我谈谈怎样增、删、改此类型数据及在主页中读出此类型数据。

    一、在表中添加图形
    Image 数据类型不同于其它数据类型,不能用Insert 、Update的标准SQL语法进行图形的添加和修改。需用到SQL Server中的TEXTPTR 、WRITETEXT、UPDATETEXT等函数进行图形的添加和修改。

    首先假设在库Im_Test中建立一张表Im_Info,此表中有两个字段,分别为Pr_Id (INT),Pr_Info (IMAGE),用来存储图形编号及图形信息。其语法如下:

 CREATE  TEALE  Im_Info  (
   Pr_Id  INT  NULL  ,
   Pr_Info  IMAGE  NULL
 )

    第一步往表中插入一条记录,并初始化PR_INFO字段。其语法如下:

 INSERT  INTO  Im_Info  VALUES (1 ,0xFFFFFFFF)

    第二步往表中写入图形信息。其语法如下:

          DECLARE @@ptrval varbinary(16)
          SELECT @@ptrval = TEXTPTR(Pr_Info)
   FROM Im_Info
     WHERE Pr_Id = 1
          WRITETEXT Im_Text.Im_Info  
             @@ptrval  0x624fd543fd…..

    其中0x624fd543fd….. 为图形的十六进制数据,可以通过C 、Java等工具获得。注意在写入图形信息前必须先将此数据库的 'select into/bulkcopy' 属性设置为 True ,其语法如下:

          sp_dboption Im_Test ,
          'select into/bulkcopy' ,True

    若想修改图形数据可用UPDATETEXT函数修改,其语法如下:

          DECLARE @@ptrval varbinary(16)
          SELECT @@ptrval = TEXTPTR(Pr_Info)
      FROM Im_Info
   WHERE Pr_Id = 1
          UPDATETEXT Im_Text.Im_Info
          @@ptrval  0xaa31bcfe543fd…..

    二、在主页中显示图形

    第一步建立数据源
    若想将加入的图形显示在主页中,必须先建立数据源,打开Windows 中的控制面板。通过ODBC 应用程序,建立数据源(取名Im_Test)连接到Im_Test数据库

    第二步编写程序
         < % @ LANGUAGE = VBScript % >
         < % Option Explicit % >
         < %
 Dim oConn    
 Dim oRs    
 Dim Pic    
 Dim PicSize    

 Response.Buffer = TRUE
 Response.ContentType = "image/gif"

 Set oConn = Server.CreateObject ("ADODB.Connection")
 oConn.Open "Im_Test",”sa”,””
 Set oRs = oConn.Execute("SELECT Pr_Info FROM Im_Info WHERE Pr_Id=1”)

 PicSize = oRs("Pr_Info").ActualSize
 Pic = oRs("Pr_Info ").GetChunk(PicSize)
 
 Response.BinaryWrite Pic
 Response.End
         % >

    此程序中先定义四个变量。然后设置属性 Response.Buffer=TRU和Response.ContentType = "image/gif" ,再连接数据库取出图形,在加以显示。  

 
 2003-11-25 11:41:44    深入SQL编程【关于存储过程和索引】

  这里所指的SQL编程并不是那些在象ASP,PHP脚本语言里用的某个SQL语句,如果你是个程序员并在做DB C/S开发,我想你会很清楚的知道SQL编程是很复杂的,先抛开嵌入语句,动态执行,高级函数,表达试等这些不谈单就解决性能问题就很头疼,下面就性能问题给出一些解决放案.(以下程序均在NT+SP6 SQL SERVER 7下调试通过)

一,存储过程
    我的一个朋友用VC/SQL SERVER做C/S项目开发,再开发过程中他的程序虽顺利执行,但遇到了由于需要大批量插入数据而引出的性能问题。他找到了我,虽然我没有用过VC但很明显在他程序中看出是在前台用循环操作象后台插入数据。这种方法再处理大批量数时无疑是不可取的,因编译器并不会处理SQL语句而是通过ODBC传输到后台,再在后台解释执行。
    由此可见经过以上几步性能问题以大打折扣,后我将他的程序段改为后台SQL编程,用存储过程实现。然后在前台用VC调用,这样一来问题以得到完美的解决。改后程序如下:(遇到此类问题的朋友可参考解决)

CREATE PROC usp_insert_temp
@iCount VARCHAR(10),
@Text VARCHAR(50),
@price VARCHAR(15)

AS

DECLARE @iIndex INT
DECLARE @pMoney FLOAT

SET @iIndex=CONVERT(INT,@iCount)
SET @pMoney=CONVERT(FLOAT,@price)

BEGIN TRAN
SELECT rygl_id,title,price
INTO rygl_temp FROM rqk
WHERE EXISTS
(SELECT rygl_id
FROM rygl
WHERE rqk.rygl_id=rygl.rygl_id
AND qty<30)
ORDER BY title_id

IF @@ERROR<>0
ROLLBACK TRAN
ELSE
COMMIT TRAN

WHILE @iIndex>0
BEGIN
BEGIN TRAN
SET @pMoney=@pMoney+1.1111
INSERT INTO rygl_temp(rygl_id,title,price)
VALUES(@iIndex,@Text,CONVERT(SMALLMONEY,@pMoney))

IF @@ERROR<>0 OR @@ROWCOUNT=0
ROLLBACK TRAN
ELSE
COMMIT TRAN
SET @iIndex=@iIndex-1
END

二,索引测试,比较
    合理的索引建立,运用可很大幅度提高程序性能,以下是在工作当中得出的经验,与大家共享。

1,ORDER BY和GROPU BY
    如果用户经常选择数据和使用ORDER BY和GROUP BY短语,任何一种索引都有助于SELECT的性能提高。如果用户选择的是顾客并按其姓名分类,两种索引都能快速检索数据。但下面的一些因素会使用户选择使用某一种索引。

2,返回范围内的数据
    列如,如果拥护希望返回在SMITH和TALBERT之间的所有顾客姓名,或者返回在日期“11/1/98”和“11/30/98”之间的订货量,并且用户经常做这类事情,那么最好在该范围所在的指定列使用聚类索引。因聚类索引已包含了经过分类排序的数据,这对于在指定范围内检索数据更为有效。聚类索引只需找到要检索的所有数据中的开头和结尾数据即可;而不象非聚类索,必须在数据层专查找来字叶层的每一个数据项。

3,列中有一个或极少的不同值
    在用户表中的某些列中喊有极少不同值,列如状态列中只包含INACVTIVE,ACVIVE或者TERMINATED。在这种情况下,在该列上使用任何类型索引都是不明智的,原因很简单:如果用户表包含了1500行大概有三分之一的行即500行在状态列中含有ACTIVE。扫描整个表,如果不是更高效,至少也是同先在索引页面中查找每个数据项而后寻找到包含ACTIVE状态的行所在的数
据页面也相同的效率。下面这个列子创建了一个表,它在有很很多重复值的列上进行索引,而该列具有很少的不同值。运行该脚本可能要花几分钟。

*/

DROP TABLE IndexTestTable
CREATE TABLE IndexTestTable
(
Tid INT IDENTITY(1,1) NOT NULL,
Status CHAR(10) NULL
)

GO

SET IDENTITY_INSERT IndexTestTable ON
DECLARE @intCount INT

BEGIN TRAN
SET @intCount=1
WHILE @intCount<=1500
BEGIN
INSERT IndexTestTable(Tid,Status) VALUES(@intCount,'Active')
SET @intCount=@intCount+3
END

SET @intCount=2
WHILE @intCount<=1500
BEGIN
INSERT IndexTestTable(Tid,Status) VALUES(@intCount,'inactive')
SET @intCount=@intCount+3
END

SET @intCount=3
WHILE @intCount<=1500
BEGIN
INSERT IndexTestTable(Tid,Status) VALUES(@intCount,'Terminated')
SET @intCount=@intCount+3
END
COMMIT TRAN

SET IDENTITY_INSERT IndexTestTable OFF
GO

DUMP TRANSACTION pubs WITH NO_LOG
GO

CREATE INDEX inTableUniquesStatus
ON IndexTestTable(Status)
GO

--不用索引查询
SELECT *
FROM IndexTestTable WITH(index(0))
WHERE Status='inactive'

--用索引查询
SELECT *
FROM IndexTestTable WITH(index(inTableUniquesStatus))
WHERE Status='inactive'

/*

    选中SHOW STATS I/O查看运行结果会另人吃惊。第一个SELECT语句引起全表扫描几乎不需要内存操作(因为只是进行插入,所有所有数据都在内存中,并不需要进行磁盘或物理读操作)。第二个SELECT语句则需要执行500个读操作,这就证实了我们所说的在这种情况下,使用任何类型索引都是不明智的。

4,
    以上举列说明了在何种情况下不应使用索引,现在咱们再反过来看看当索引列中
不同数目值增加时即有较少不同值时会怎样?见如下代码

*/

DROP TABLE IndexTestTable
GO

CREATE TABLE IndexTestTable
(
Tid INT IDENTITY(1,1) NOT NULL,
Status CHAR(10) NULL,
Co3 CHAR(20) NOT NULL,
Co4 CHAR(50) NOT NULL
)
GO

DECLARE @intNum INT
SET @intNum=0

BEGIN TRAN
WHILE @intNum<=1300
BEGIN
INSERT indexTestTable
VALUES(CHAR(@intNum %26 +65),'test3','test4')
SET @intNum=@intNum+1
END
COMMIT TRAN
GO

--不用索引查询
SELECT * FROM IndexTestTable WHIT(INDEX(0))
WHERE Status='B'

--创建聚集索引
CREATE CLUSTERED INDEX icIndexTestTable
ON IndexTestTable(Status)
GO

--使用索引查询
SELECT * FROM IndexTestTable WITH(INDEX(icIndexTestTable))
WHERE Status='B'

/*

5,
    用户很明显地能看出,随着表中行的数目和列中不同值的增长。使用索引可以较大幅度提高效率,由此又引出另一个问题,在何种情况下用何种索引更有效?上面列子已经介绍了聚类索引,大家都能看出在对于有较少不同植时使用聚类索引是有很大帮助的,但当不同值的数木增加并达到表中行的树木时则应该选非聚类索引。此时使用非聚类索在读操作上和聚类似索引并无
二异,但在对表进行写操作上的性能却提高不少,如果用户经常从表中的一个或少是数几个字段中检索数据,当非聚集索引包含要检索的所有字段时就会减少所需的读操作,如果不是那么正如上面第二条所说使用非聚集索引通常是钟很差的选择,下面这个列子说明了在何时应该使用聚集索引

*/

DROP TABLE IndexTestTable
GO

CREATE TABLE IndexTestTable
(
Tid INT IDENTITY(1,1)NOT NULL,
Status CHAR(20) NOT NULL
)
GO

DECLARE @intCount INT
SET @intCount=0

BEGIN TRAN
WHILE @intCount<=500
BEGIN
INSERT INTO IndexTestTable(Status) VALUES('test'+CONVERT(CHAR(6),@intCount))
SET @intCount=@intCount+1
END
COMMIT TRAN
GO

--创建聚集索引
CREATE CLUSTERED INDEX icIndexTestTable
ON IndexTestTable(Tid)
GO

--创建非聚集索引
CREATE INDEX inIndexTestTable
ON IndexTestTable(Tid)
GO

--使用非聚集索引查询
SELECT Tid FROM IndexTestTable WITH(INDEX(inIndexTestTable))
WHERE Tid BETWEEN 100 AND 500

--使用聚集索引查询
SELECT Tid FROM IndexTestTable WITH(INDEX(icIndexTestTable))
WHERE Tid BETWEEN 100 AND 500

/*

    集索引包含绝大多数的检索数据,则只需要读取很少的数据页这种情况下非聚集索引要比聚集索引好,如果表的数据行很庞大效果会更加明显。

6,
    要说明的是,索引虽有助于提高性能但不是索引越多越好,恰好相反过多的索引会导致系统低效。用户在表中每加进一个索引,维护索引集合就要做相应的更新工作。  

 
 2003-11-25 11:49:45    SQL Server日期计算

   通常,你需要获得当前日期和计算一些其他的日期,例如,你的程序可能需要判断一个月的第一天或者最后一天。你们大部分人大概都知道怎样把日期进行分割(年、月、日等),然后仅仅用分割出来的年、月、日等放在几个函数中计算出自己所需要的日期!在这篇文章里,我将告诉你如何使用DATEADD和DATEDIFF函数来计算出在你的程序中可能你要用到的一些不同日期。
在使用本文中的例子之前,你必须注意以下的问题。大部分可能不是所有例子在不同的机器上执行的结果可能不一样,这完全由哪一天是一个星期的第一天这个设置决定。第一天(DATEFIRST)设定决定了你的系统使用哪一天作为一周的第一天。所有以下的例子都是以星期天作为一周的第一天来建立,也就是第一天设置为7。假如你的第一天设置不一样,你可能需要调整这些例子,使它和不同的第一天设置相符合。你可以通过@@DATEFIRST函数来检查第一天设置。

    为了理解这些例子,我们先复习一下DATEDIFF和DATEADD函数。DATEDIFF函数计算两个日期之间的小时、天、周、月、年等时间间隔总数。DATEADD函数计算一个日期通过给时间间隔加减来获得一个新的日期。要了解更多的DATEDIFF和DATEADD函数以及时间间隔可以阅读微软联机帮助。

    使用DATEDIFF和DATEADD函数来计算日期,和本来从当前日期转换到你需要的日期的考虑方法有点不同。你必须从时间间隔这个方面来考虑。比如,从当前日期到你要得到的日期之间有多少时间间隔,或者,从今天到某一天(比如1900-1-1)之间有多少时间间隔,等等。理解怎样着眼于时间间隔有助于你轻松的理解我的不同的日期计算例子。

【一个月的第一天】
    第一个例子,我将告诉你如何从当前日期去这个月的最后一天。请注意:这个例子以及这篇文章中的其他例子都将只使用DATEDIFF和DATEADD函数来计算我们想要的日期。每一个例子都将通过计算但前的时间间隔,然后进行加减来得到想要计算的日期。

    这是计算一个月第一天的SQL 脚本:
     SELECT DATEADD(mm, DATEDIFF(mm,0,getdate()), 0)

    我们把这个语句分开来看看它是如何工作的。最核心的函数是getdate(),大部分人都知道这个是返回当前的日期和时间的函数。下一个执行的函数DATEDIFF(mm,0,getdate())是计算当前日期和“1900-01-01 00:00:00.000”这个日期之间的月数。记住:时期和时间变量和毫秒一样是从“1900-01-01 00:00:00.000”开始计算的。这就是为什么你可以在DATEDIFF函数中指定第一个时间表达式为“0”。下一个函数是DATEADD,增加当前日期到“1900-01-01”的月数。通过增加预定义的日期“1900-01-01”和当前日期的月数,我们可以获得这个月的第一天。另外,计算出来的日期的时间部分将会是“00:00:00.000”。

    这个计算的技巧是先计算当前日期到“1900-01-01”的时间间隔数,然后把它加到“1900-01-01”上来获得特殊的日期,这个技巧可以用来计算很多不同的日期。下一个例子也是用这个技巧从当前日期来产生不同的日期。

【本周的星期一】
    这里我是用周(wk)的时间间隔来计算哪一天是本周的星期一。
     SELECT DATEADD(wk, DATEDIFF(wk,0,getdate()), 0)

【一年的第一天】
    现在用年(yy)的时间间隔来显示这一年的第一天。
     SELECT DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)

【季度的第一天】
    假如你要计算这个季度的第一天,这个例子告诉你该如何做。
     SELECT DATEADD(qq, DATEDIFF(qq,0,getdate()), 0)

【当天的半夜】
    曾经需要通过getdate()函数为了返回时间值截掉时间部分,就会考虑到当前日期是不是在半夜。假如这样,这个例子使用DATEDIFF和DATEADD函数来获得半夜的时间点。
     SELECT DATEADD(dd, DATEDIFF(dd,0,getdate()), 0)

【深入DATEDIFF和DATEADD函数计算】
    你可以明白,通过使用简单的DATEDIFF和DATEADD函数计算,你可以发现很多不同的可能有意义的日期。

    目前为止的所有例子只是仅仅计算当前的时间和“1900-01-01”之间的时间间隔数量,然后把它加到“1900-01-01”的时间间隔上来计算出日期。假定你修改时间间隔的数量,或者使用不同的时间间隔来调用DATEADD函数,或者减去时间间隔而不是增加,那么通过这些小的调整你可以发现和多不同的日期。

    这里有四个例子使用另外一个DATEADD函数来计算最后一天来分别替换DATEADD函数前后两个时间间隔。

【上个月的最后一天】
    这是一个计算上个月最后一天的例子。它通过从一个月的最后一天这个例子上减去3毫秒来获得。有一点要记住,在Sql Server中时间是精确到3毫秒。这就是为什么我需要减去3毫秒来获得我要的日期和时间。
     SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(mm,0,getdate()), 0))

    计算出来的日期的时间部分包含了一个Sql Server可以记录的一天的最后时刻(“23:59:59:997”)的时间。

【去年的最后一天】
    连接上面的例子,为了要得到去年的最后一天,你需要在今年的第一天上减去3毫秒。
     SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0))

【本月的最后一天】
    现在,为了获得本月的最后一天,我需要稍微修改一下获得上个月的最后一天的语句。修改需要给用DATEDIFF比较当前日期和“1900-01-01”返回的时间间隔上加1。通过加1个月,我计算出下个月的第一天,然后减去3毫秒,这样就计算出了这个月的最后一天。这是计算本月最后一天的SQL脚本。
     SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0))

【本年的最后一天】
    你现在应该掌握这个的做法,这是计算本年最后一天脚本
     SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate())+1, 0))。

【本月的第一个星期一】
    好了,现在是最后一个例子。这里我要计算这个月的第一个星期一。这是计算的脚本。
     select DATEADD(wk, DATEDIFF(wk,0,dateadd(dd,6-datepart(day,getdate()),getdate())), 0)            
    在这个例子里,我使用了“本周的星期一”的脚本,并作了一点点修改。修改的部分是把原来脚本中“getdate()”部分替换成计算本月的第6天,在计算中用本月的第6天来替换当前日期使得计算可以获得这个月的第一个星期一。

【总结】
    我希望这些例子可以在你用DATEADD和DATEDIFF函数计算日期时给你一点启发。通过使用这个计算日期的时间间隔的数学方法,我发现为了显示两个日期之间间隔的有用历法是有价值的。注意,这只是计算出这些日期的一种方法。要牢记,还有很多方法可以得到相同的计算结果。假如你有其他的方法,那很不错,要是你没有,我希望这些例子可以给你一些启发,当你要用DATEADD和DATEDIFF函数计算你程序可能要用到的日期时。

 
 2003-11-25 12:11:33   

   在数据库管理系统中,查询是一项必不可少的功能。查询功能是直接体现系统功能的一项重要指标。查询的方式主要有以下几种:1固定字段的单一查询;2可选择字段的单一查询;3限制若干个字段的多重查询;4可任意选择字段的多重查询。前两种也称为单条件查询,后两种称为多重(或多条件)查询。在实际中,系统(实为程序员)提供给用户的查询方式以单条件查询为多,即使提供了多条件方式,通常也只有两或三个条件,因为编写多重查询是一项非常棘手且繁琐的事情。仅为此,程序员吃尽了苦头。实际上,利用表格Grid功能,就能轻松地实现多重查询。本人以Delphi为例,介绍具体的实现方法,但这种思想,也同样适合于其它的编程语言(如Visual Foxpro)。

    另外,为使程序方便“移植”, 本人把各功能模块化,使其更具有通用性。

程序主要按如下三个功能来实现:
①设置DBGrid
②生成查询条件(语句)
③执行查询

具体步骤如下:
⑴新建一工程文件,取名为PDBGrid.dpr;
⑵给单元文件取名为UDBGrid.pas,在其相应的表单(取名为frmDBGrid)中添加如下控件并编写相应的代码:

控件名称
主要属性及值
备注
Table1
DataBaseName(MyAlias)
TableName
(MyTable1.db)
Active
(false)
查询对象:数据集
MyTable1.db为任意的表
DataSource1
DataSet(Table1)

DBGrid1
DataSource(DataSource1)

显示数据源
Table2
DataBaseName(MyAlias)
TableName
(CxComm.db)
Active
(false)

记录查询条件的数据集
(CxComm.db结构下述)
DataSource2
DataSet(Table2)

DBGrid2
DataSource(DataSource2)

提供查询处理
Query1
DataBaseName(MyAlias)
Active
(false)

保存和执行SQL语句的数据集
DataSource3
DataSet
(Query 1)

DGrid3
DataSource
(DataSource3)

显示查询结果
Memo1
Lines(<动态赋值>)

显示及修改SQL语句
Button1
Caption(设置DBGrid)

Click事件下述
Button2
Caption(生成查询)

Click事件下述
Button3
Caption(执行查询)

Click事件下述

其中
Table2(记录查询条件的数据集)对应的表CxComm.db的结构定义如下:

字段名
项目
关系
实例
逻辑
类型
A(字符型)
A(字符型)
A(字符型)
A(字符型)
大小
14
5
10
6

表单的FormActivate事件代码如下:
procedure TfrmDBGrid.FormActivate(Sender: TObject);
begin
  if Table2.Active then Table2.Close;
  Table2.EmptyTable; //清空条件
  Table1.Open;
  Table2.Open;
end;

Button1(设置DBGrid)的Click事件代码如下:
procedure TfrmDBGrid.Button1Click(Sender: TObject);
begin
  MySetDBGrid(Table1,DBGrid2); // MySetDBGrid为自定义过程
                               //以Table1为数据源,DBGrid2为记录筛选(查询)条件的表格
end;

Button2(生成查询)的Click事件代码如下:
procedure TfrmDBGrid.Button2Click(Sender: TObject);
begin
  if MyCreate_SQL(Table1,Table2,Query1) //MyCreate_SQL为自定义函数
                                        //由指定数据来源表来生成SQL,存入Query1
  then begin
    Memo1.Lines.Clear;
    Memo1.Lines:=(Query1.SQL);
    Memo1.Modified:=false;
  end
end;

Button3(执行查询)的Click事件代码如下:
procedure TfrmDBGrid.Button3Click(Sender: TObject);
begin
  with Query1,SQL do
  begin
    Close;
    if Memo1.Modified //用户可修改SQL语句
    then SQL:=Memo1.Lines;
    try
      ExecSQL;
      Open;
    except //捕捉错误并处理
      begin
        MessageBeep(0);
        Application.MessageBox('错误的SQL语句!','确认',MB_OK+MB_ICONSTOP);
        Close;
      end;
    end //try
  end;
end; //执行SQL

自定义过程MySetDBGrid (设置DBGrid)的代码如下:
procedure TfrmDBGrid.MySetDBGrid(sTable:TTable;tjDBGrid:TDBGrid);
//参数说明:sTable为数据(包括字段,记录)来源表
// tjDBGrid为记录筛选(查询)条件的表格
var i:byte;
begin
  //设置查询项目
  if not sTable.Active then sTable.Open;
  tjDBGrid.Columns[0].PickList.Clear;
  for i:=0 to sTable.FieldCount-1 do //记录数(即字段数)
  begin
    tjDBGrid.Columns[0].PickList.Add(sTable.Fields[i].FieldName);
  end; //for
  //设置关系(=,<>,>,>=,<,<=) 及逻辑(AND,OR)
  tjDBGrid.Columns[1].PickList.Text:='='+#13+'<>'+#13+'>'+#13+'>='+#13+'<'+#13+'<=';
  tjDBGrid.Columns[3].PickList.Text:='AND'+#13+'OR';
end;//设置DBGrid

自定义函数MyCreate_SQL (生成查询)的代码如下:
function TfrmDBGrid.MyCreate_SQL(sTable,tjTable:TTable;tjQuery:TQuery):boolean;
//参数说明:sTable为数据(包括字段,记录)来源表
// tjTable为记录筛选(查询)条件的表
// tjQuery记录SQL语句
var i:byte;
    lsDate:TDate; //检测日期格式用
    sLj,sFilter,sFieldName:string; //分别表示:逻辑关系,筛选条件,字段名
begin
  Result:=true;
  //生成"筛选条件"语句
  with tjQuery,SQL do
  begin
    Close;
    Clear;
    DatabaseName:=sTable.DatabaseName; //设置Query1的别名
    Add('Select * from '''+sTable.TableName+'''');
  end;
  with tjTable do //查询(筛选)条件表
  begin
    if not Active then Open;
    if IsEmpty
    then begin
      Application.MessageBox('未选择筛选条件!','确定',MB_OK+MB_ICONEXCLAMATION);
      Exit;
    end;
    tjQuery.SQL.Add(' Where ');//含有筛选条件
    sFilter:=''; //临时记录筛选条件
    First;
    for i:=0 to RecordCount-1 do
    begin
      sLj:=Fields[3].AsString; //逻辑关系AND,OR
      //(字段名0>1实际值2)
      sFilter:=sFilter+'(';
      sFilter:=sFilter+Fields[0].AsString+Fields[1].AsString;
      sFieldName:=Fields[0].AsString; //取第1列的字段名
      case Table1.FieldByName(sFieldName).DataType of ftString:
      begin //字符型处理
        sFilter:=sFilter+''''+Fields[2].AsString+'''' //第2列为关系
      end;
      ftFloat, //浮点型处理
      ftAutoInc, //自增型
      ftSmallInt, //短整型
      ftInteger, //整型
      ftCurrency: begin //货币型
      sFilter:=sFilter+Fields[2].AsString;
    end;
    ftDate:
      begin //日期型处理
        try
          lsDate:=StrToDate(Fields[2].AsString);
          sFilter:=sFilter+''''+FormatDateTime('mm/dd/yyyy',StrToDate(Fields[2].AsString))+'''';
        except
          Application.MessageBox('错误的日期格式!','确认',MB_OK+MB_ICONSTOP);
          Result:=false; //返回错误标志
          break;
        end;//try 日期格式判断
      end;
      //此处可增加对其它类型数据的处理
      end;//case
      sFilter:=sFilter+')';
      if sLj<>''
      then begin
        if RecNo<>RecordCount //且"非最后行"的记录
        then sFilter:=sFilter+Fields[3].AsString; //And|Or;
      end
      else break;
      Next
    end;
  end; //not IsEmpty(筛选)非空
  tjQuery.SQL.Add(sFilter); //保存查询条件
end; //处理筛选条件

另外,需要进行如下说明:
...
Type
...
procedure MySetDBGrid(sTable:TTable;tjDBGrid:TDBGrid);
function MyCreate_SQL(sTable,tjTable:TTable;tjQuery:TQuery):boolean;
private
{ Private declarations }
...

    值得说明的是,⑴为从一定程序上简化程序,逻辑关系只提供了AND和OR两种,但为允许用户修改SQL语句,如:在多条件之间增加括号来改变运算顺序等,使得查询功能更加强大,因此增加了Memo控件;⑵在实际系统中,为方便用户的操作,可增加几个Button(按钮),功能分别是对Table2的“增加”、“删除”,这样用户界面会更友好些。
利用这种方法来设置查询,条件个数是无限制的,且在屏幕上不会占据太大的空间,程序员实现起来要简单得多了。

软件环境:中文Win98/中文Delphi5.0。

 
 2003-11-25 12:39:56    有关重复记录的删除

   有两个意义上的重复记录,一是完全重复的记录,也即所有字段均重复的记录,二是部分关键字段重复的记录,比如Name字段重复,而其他字段不一定重复或都重复可以忽略。

1、对于第一种重复,比较容易解决,使用
    select distinct * from tableName
就可以得到无重复记录的结果集。
   如果该表需要删除重复的记录(重复记录保留1条),可以按以下方法删除
    select distinct * into #Tmp from tableName
    drop table tableName
    select * into tableName from #Tmp
    drop table #Tmp
发生这种重复的原因是表设计不周产生的,增加唯一索引列即可解决。

2、这类重复问题通常要求保留重复记录中的第一条记录,操作方法如下假设有重复的字段为Name,Address,要求得到这两个字段唯一的结果集
    select identity(int,1,1) as autoID, * into #Tmp from tableName
    select min(autoID) as autoID into #Tmp2 from #Tmp group by Name,autoID
    select * from #Tmp where autoID in(select autoID from #tmp2)
最后一个select即得到了Name,Address不重复的结果集(但多了一个autoID字段,实际写时可以写在select子句中省去此列)

 
 2003-11-25 13:15:26    SQL SERVER 2000 数据库备份与恢复单元

unit BackRestoreSQLDB;
{描述:SQL SERVER 2000 数据库备份与恢复单元 ,本单元函数在SQL SERVER 2000 +Delphi7.0编译通过
 时间:2003.09.15
 作者:chenshaizi
}
interface

uses
  adodb, db;
function repl_substr(sub_old, sub_new, s: string): string; //把sub_old换成sub_new,后面有用。
function BackupSQLDataBase(connstr_sql, DatabaseName, Backup_FileName: string): Boolean; //数据库备份函数
//SQL数据数据库备份,connstr_sql是ADO控件的connectionstring,DatabaseName是数据库名称,
//Backup_FileName要备份到的目 标文件
function RestoreSQLDataBase(connstr_sql, DatabaseName, Restore_FileName: string): Boolean; //数据库恢复函数
//Restore_FileName以前备份的数据库文件,
implementation

function repl_substr(sub_old, sub_new, s: string): string;
var
  i: integer;
begin
  repeat
    i := pos(sub_old, s);
    if i > 0 then
    begin
      delete(s, i, Length(sub_old));
      insert(sub_new, s, i);
    end;
  until i < 1;
  Result := s;
end;

function BackupSQLDataBase(connstr_sql, DatabaseName, Backup_FileName: string): Boolean;
var
//备份SQL数据库SQL数据数据库备份,connstr_sql是ADO控件的connectionstring,DatabaseName是数据库名称,
//Backup_FileName要备份到的目标文件
  aADOQuery: TADOQuery;
begin
  try
    aADOQuery := TADOQuery.Create(nil);
    aADOQuery.Close;
    aADOQuery.ConnectionString := connstr_sql;
    aADOQuery.SQL.Clear;
    aADOQuery.SQL.Add('backup database ' + DatabaseName + ' to disk = ' + '''' + Backup_FileName + ''' with format');
    try
      aADOQuery.ExecSQL;
      Result := true;
    except
      Result := false;
      exit;
    end;
  finally
    aADOQuery.Free;
  end;
end;

function RestoreSQLDataBase(connstr_sql, DatabaseName, Restore_FileName: string): Boolean;
var //数据库恢复函数,estore_FileName以前备份的数据库文件
  aADOQuery: TADOQuery;
begin
  try
    aADOQuery := TADOQuery.Create(nil);
    aADOQuery.Close; //恢复数据库不能打开数据库,要打开系统数据库master,把连接字符串如adoconnetion的connectionstring中的数据库名称换成"master"数据库
    aADOQuery.ConnectionString := repl_substr(DatabaseName, 'master', connstr_sql);
    aADOQuery.SQL.Clear;
    aADOQuery.SQL.Add('RESTORE DATABASE ' + DatabaseName + ' from disk = ' + '''' + Restore_FileName + '''');
    try
      aADOQuery.ExecSQL;
      Result := true;
    except
      Result := false;
      exit;
    end;
  finally
    aADOQuery.Free;
  end;
end;
end.

 
 2003-11-25 13:17:15    不用控件,通过ADO对象连接sqlserver数据库

关键词:ADO连接sqlserver数据库
 
uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,comobj, Db, ADODB;

//要伸明comobj  下面为连接代码:

var
  adoc,ador:variant;
begin
  adoc:=createoleobject('adodb.connection');
  adoc.open('Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=customer;Data Source=BERN');
  ador:=createoleobject('adodb.recordset');
  ador.activeconnection:=adoc;
  ador.CursorType:=ctDynamic;
  ador.open('select * from t_customer',adoc);  
end;

 
 2003-11-25 13:28:58    存取JPEG文件到SQLSERVER数据库【王大川(WDCZZH)】

【关键词】:JPEG图片图像数据库  
    最近在CSDN上看到两篇关于《DELPHI中存取JPEG文件到SQLSERVER》中的文章之后,觉得其中讲述的方法虽然有可取之处,但颇费时,我这里有更简单的操作方法,而且安全可靠,不敢一人独享,愿发布出来与大家共享。在Delphi7.0+Win2000+SqlServer 2000中测试通过,运行良好,现将思路、源码公开如下:

【解决思路】:
1、 关键在于将打开的JPEG文件动态转换为Tbitmap对象并显示在Timage对象中;
2、 将显示的图片提交到数据库中。

本例中在SQLSERVER2000中建立了一个试例表:exam(xm char(10),photo image);
程序源代码:

unit SavePic;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, ExtDlgs, ExtCtrls, DBCtrls, Grids, DBGrids, DB, ADODB, Buttons,  StdCtrls,Jpeg;

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    ADOConnection1: TADOConnection;
    Table1: TADOTable;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBImage1: TDBImage;
    Image1: TImage;
    SpeedButton2: TSpeedButton;
    OpenPictureDialog1: TOpenPictureDialog;
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    SpeedButton3: TSpeedButton;
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.SpeedButton2Click(Sender: TObject);
var
  bmp1:TBitmap;
  jpg1:TJpegImage;
begin
  OpenPictureDialog1.DefaultExt:=GraphicExtension(TJpegimage);
  if OpenPictureDialog1.Execute then
  begin
    bmp1:=TBitmap.Create;
    jpg1:=TJpegImage.Create;
    try
      jpg1.LoadFromFile(OpenPictureDialog1.FileName);
      bmp1.Assign(jpg1);
      Image1.Picture.Bitmap.Assign(bmp1);
    finally
      jpg1.Free;
      bmp1.Free;
    end;
  end;
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  table1.Open;
  table1.insert;
  table1.fieldbyname('xm').asstring:=Edit1.Text;
  table1.FieldByName('photo').Assign(Image1.Picture);
  table1.post;
  table1.Refresh;
end;

end.

    以上就是将JPEG存取到SQLSERVER中的操作方法,本文在此仅是抛砖引玉,希望各位朋友举一反三想出更多、更好、更实用的应用技术。
 
另外原来那两篇《DELPHI中存取JPEG文件到SQLSERVER》暂时没有找到。。。

 
 2003-11-25 16:41:42    多个 Edit 的 SQL 查询

问:关于判断的问题:假如在form1里有Edit1、Edit2和Edit3,如果是查询,有7种情况:
(1)、当Edit1不为空,而其它两个为空:
with query1 do
begin
  close;
  sql.clear;
  sql.add('select * from fstao where field1=:field1');
  Parambyname('field1').AsString:=Edit1.text;
  Open;
end;
(2)、当Edit2不为空,而其它两个为空:
with query1 do
begin
  close;
  sql.clear;
  sql.add('select * from fstao where field2=:field2');
  Parambyname('field2').AsString:=Edit2.text;
  Open;
end;
(3)、当Edit3不为空,而其它两个为空:
with query1 do
begin
  close;
  sql.clear;
  sql.add('select * from fstao where field3=:field3');
  Parambyname('field3').AsString:=Edit3.text;
  Open;
end;
(4)、当Edit1和Edit2不为空,Edit3为空:
with query1 do
begin
  close;
  sql.clear;
  sql.add('select * from fstao where field1=:field1 and field2=:field2');
  Parambyname('field1').AsString:=Edit1.text;
  Parambyname('field2').AsString:=Edit2.text;
  Open;
end;
(5)、当Edit1和Edit3不为空,Edit2为空:
with query1 do
begin
  close;
  sql.clear;
  sql.add('select * from fstao where field1=:field1 and field3=:field3');
  Parambyname('field1').AsString:=Edit1.text;
  Parambyname('field3').AsString:=Edit2.text;
  Open;
end;
(6)、当Edit2和Edit3不为空,Edit1为空:
with query1 do
begin
  close;
  sql.clear;
  sql.add('select * from fstao where field2=:field2 and field3=:field3');
  Parambyname('field2').AsString:=Edit1.text;
  Parambyname('field3').AsString:=Edit2.text;
  Open;
end;
(7)、当Edit1、Edit2和Edit3都不为空:
with query1 do
begin
  close;
  sql.clear;
  sql.add('select * from fstao where field1=:field1 and field2=:field2 and
field3=:field3');
  Parambyname('field2').AsString:=Edit1.text;
  Parambyname('field3').AsString:=Edit2.text;
  Parambyname('field1').AsString:=Edit2.text;
  Open;
end;
    编程的时候,那么就逐个逐个判断,有3个Edit就有7种情况,那么4个、5个Edit时,那么分别有14种和25种情况,那就很麻烦,如果是逐个判断的话那么就得写一两万行代码,而且很烦。我想请教有没有更好的方法,比如自由判断。数据库为Oracle8。
【答1】:你可以这样做:
query1.sql.add (' select * from table1 where ');
if edit1.text<>'' then
begin
  query1.sql.add ('field1:field1');
  Parambyname('field1').AsString:=Edit1.text;
end;
.......
if edit9.text<>'' then
begin
  query1.sql.add ('field9:field9');
  Parambyname('field9').AsString:=Edit9.text;
end;
..............

【答2】:我是这么干的:
var
  SQLCondition:string;
  FirstConjunction:string;
begin
  FirstConjunction := 'WHERE ';
  if Edit1.Text <> '' then
  begin
    SQLCondition := FirstConjunction + '(FieldName1="'+Edit1.Text + '")';
    FirstConjunction := 'AND ';
  end;
  if Edit2.Text <> '' then begin
    SQLCondition := FirstConjunction + '(FieldName2="'+Edit2.Text + '")';
    FirstConjunction := 'AND ';
  end;

.......

  if Editn.Text <> '' then
  begin
    SQLCondition := FirstConjunction + '(FieldNamen="'+Editn.Text + '")';
    FirstConjunction := 'AND ';
  end;
  sql.clear;
  sql.Add('SELECT * FROM DATABASE');
  sql.Add(SQLCondition);
  open;

【答3】:用不着这么麻烦吧...
我是这么干的:
begin
  sql.clear;
  sql.Add('SELECT * FROM DATABASE');
  if Edit1.Text <> '' then
    sql.Add('(FieldName1="'+Edit1.Text + '") and ';
  if Edit2.Text <> '' then
    sql.Add('(FieldName2="'+Edit2.Text + '")
and ';

.......

  if Edit9.Text <> ''
  then
    sql.Add('(FieldName9="'+Edit9.Text + '") and ';
  if Pos(sql.Lines[1],'and')>0
  then
    Delete(sql.Lines[1],Length(sql.Lines[1]-4),3);
  open;
end;  

 
 2003-11-25 16:51:16    从 Foxpro 到 SQL Server 的数据转移方法

   在计算机应用系统的软件升级改造过程中,我们经常遇到这样一个问题:老系统的数据库平台与新系统不同。例如,老系统的数据库平台是 Foxpro, 而新系统的平台是 SQL Server。 而且,我们需要把旧的数据库应用系统中的一些数据转移到新系统来。但是因为新老系统在定义数据类型、数据格式等方面的差异,就很难用人工录入的方法来实现。因此,需要有一个能实现这种功能的程序。

    本文利用 Borland Delphi 实现了这个转移过程。

    基本思想是:在一个 Form 中,分别用两个 TDatabase 控件连接新老数据库。并采用TTable、TDbGrid 作为数据转移的中心,根据 DbGrid 中的数据生成标准的 SQL 插入语句。这样,就实现了从一个数据库系统到另一个数据库系统的数据转移。在这里,采用 TTable、TDbGrid 作为数据转移的中心是一个技巧,因为:TTable 的 Fields 属性能指示出某字段的字段名称、数据类型等,这为数据转移过程中的 Insert 语句的生成及数据类型转换提供了依。

    下面的例子展示了从 Foxpro 到 SQL Server 的数据转移方法。至于其他系统间的数据转移,只要根据目标系统的数据定义要求,修改相应的 Insert 语句。

---- 程 序 代 码 如 下:

unit ConvertDBF;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DBTables, Db, Grids, DBGrids;

type
 TfrmConvertDB = class(TForm)
  btnOK: TButton;
  Label1: TLabel;
  db1: TDatabase;  {用于连接老数据库系统}
  db2: TDatabase;  {用于连接新数据库系统}
  dbg: TDBGrid;
  tblSource: TTable; {dbg的Datasource}
  qryInsert: TQuery;
   {用于存放生成的SQL Insert语句}
  srcSource: TDataSource;
  tblDest: TTable; {DBGrid1的Datasource}
  DBGrid1: TDBGrid;
  srcDest: TDataSource;
  edFromtbl: TEdit;
  Label2: TLabel;
  Label3: TLabel;
  edToTbl: TEdit;
  procedure btnOKClick(Sender: TObject);
 private
  { Private declarations }
 public
  { Public declarations }
 end;

var
 frmConvertDB: TfrmConvertDB;

implementation

{$R *.DFM}

procedure TfrmConvertDB.btnOKClick
 (Sender: TObject);
var iField :integer;
begin
 if ((edTotbl.text<>'') and (edFromtbl.text<>''))then begin
   tblSource.TableName:=edFromtbl.text;
          {指定TableName}
   tblDest.TableName:=edTotbl.text;
   with tblSource do begin
     Open; {打开老系统的表}
     while EOF=FALSE do begin
       {逐条记录处理}
       qryInsert.SQL.Clear;
       qryInsert.sql.Add('Insert into '+edTotbl.text + '(');
       for iField:=0 to dbg.FieldCount-1 do begin
         qryInsert.sql.add(dbg.Fields[iField].DisplayLabel);
         if iField<>dbg.FieldCount-1 then
           qryInsert.sql.add(',');
       end;
       qryInsert.sql.add(') values(');
       for iField:=0 to dbg.FieldCount-1 do begin
    {进行数据类型转换}
         if dbg.fields[iField].DataType=ftInteger then
           qryInsert.sql.add(inttostr(dbg.fields[iField].asInteger));
         if dbg.fields[iField].DataType=ftFloat then
           qryInsert.sql.add(floattostr(dbg.fields[iField].asFloat));
         if dbg.fields[iField].DataType=ftDate then
           qryInsert.sql.add(''''+datetostr(dbg.fields[iField].asDateTime)+'''');
         if dbg.fields[iField].DataType=ftString then begin
           if dbg.fields[iField].asString<>'' then
             qryInsert.sql.add(''''+dbg.fields[iField].asString+'''')
           else
             qryInsert.sql.add('NULL');
         end;
         if iField<>dbg.FieldCount-1
         then qryInsert.sql.add(',');
       end;
       qryInsert.sql.add(')');
       qryInsert.ExecSQL;
              {把数据插入到新系统的表中}
       next;
     end;
   end;
   tblDest.Close;
   tblDest.Open;;
   ShowMessage('  转换完毕!   ');
 end
 else
  ShowMessage
  ('请输入要插入数据的表的名称   ');
end;

end.

 
 2003-11-25 17:01:50    Delphi数据集过滤技巧

   当我们在操作数据集时,往往需要对数据进行筛选。例如:一个名为Customer的数据表,它具有 CustNo、CustName、Country、Address、Phone、State、TaxRate 等字段,如果只想查看国别为China或顾客号大于1000的顾客记录,就需要对数据集进行过滤。经总结,有下面这些过滤方法:

  一、利用Ttable和Tquery的Filter属性
  1.在设计时设置Filter属性
  例如,设置Filter为:Country=′China′
  然后改变Filtered属性为True(注意,Filtered为True时过滤才有效)。则只能看到对应的
Country字段内容为‘China’的记录。
  设置Filter时可以使用的操作符有:<、>、<=、>=、=、<>、AND、OR、NOT。
  例如,设置Filter为:CustNo>=1000 and CustNo<=5000,则只能看到顾客号在1000与5000之间的顾客记录。
  2.在程序运行期间进行动态过滤
  要在程序运行时改变Filter属性,这包括两种情况:
  (1)操作符右边为常量,例如:
  Table1Filter:=′State′+′=′+′′′HI′′′;
  注意:字符串常量必须用三对单引号括起来。
  (2)操作符右边不为常量,可能是通过一个变量指定的值,或由一输入框给出的值。这时需要用到Format函数。其代码形式为:
  Table1Filter:=Format(′State′+′=′+′′′%S′′′,[StateValue]);
  其中StateValue为已经赋值的一个字符串变量,也可以为其他形式,例如:Edit1Text。

  二、用ApplyRange筛选数据集的记录
  执行下面这段代码,将只能看到顾客号在1000至5000之间的顾客记录。组成该例程的几个过
程为:ApplyRange,SetRangeStart,SetRangeEnd。
  Table1SetRangeStart;
  Table1[′CustNo′]:=1000;
  Table1SetRangeEnd;
  Table1[′CustNo′]:=5000;
  Table1ApplyRange;
  注意:该过程只适用于索引的字段。如果想基于非索引字段筛选,则不妨使用一点小花招:
建立假索引。实现的方法为:
  Table1IndexFieldNames:=字段名;
  Delphi还提供了简单的调用SetRangeStart、SetRangeEnd和ApplyRange的方法,例如:Table1SetRange([Edit1Text],[Edit2Text]);

  三、用OnFilterRecord事件筛选
  OnFilterRecord事件允许按非键控字段建立筛选程序,例如:
  procedure TForm1Table1FilterRecord(DataSet:TDataSet;var Accept:Boolean);
  begin
  Accept:=DataSet[′State′]=′CA′;
  end;

  四、用Tquery控件的SQL语句
  1.SQL语句中不包含变量和参数
  Select*from Customer
  Where CustNo>=1000 and CustNo<=5000
  2.SQL语句中包含参数
  Select*from Customer
  Where CustNo>=:CustNo
  在运行期间给参数CustNo赋值。
  3.SQL语句中包含变量
  这时向Tquery控件添加SQL语句的代码应当这样写:
  Query1Close;
  Query1SQLClear;
  Query1SQLAdd(Format(′Select*from Customer′+′′+′ where State=′+′′′%S′′′,[StateValue]));
  Query1Open;
  在上面的四种方法中,第四种功能最强大,使用最灵活。  

 
 2003-11-25 17:06:12    建立临时表

  数据输入是开发数据库程序的必然环节。在Client/Server结构中,客户端可能要输入一批数据后,再向服务器的后台数据库提交,这就需要在本地(客户端)建立临时数据表来存储用户输入的数据,待提交后,清除本地表数据。这种方法的好处是:提高输入效率,减小网络负担。
 
   由于用户一次输入的数据量一般情况下较小(不会超过几百条记录),所以临时表可以建立
在内存中,这样处理速度较快。

  方法1:使用查询控件(TQuery)
  第1步:在窗体上放上查询控件(TQuery),设置好所连接的数据表。
  第2步:使TQuery. CachedUpdates=True;
       TQuery. RequestLive=True
  第3步:在原有的SQL语句后加入一条Where子语句,要求加入这条Where子语句后SQL查询结果为空。
  例如:

  SELECT Biolife.″Species No″, Category, Common_Name, Biolife.″Species Name″,
Biolife.″Length (cm)″, Length_In, Notes, Graphic
  FROM ″biolife.db″ Biolife
  where Biolife.Category=′A′ and Biolife.Category=′B′

  这样临时表就建立完成了。

  方法2:使用代码创建临时表
  代码如下:

  function CreateTableInMemory(const AFieldDefs:TFieldDefs):
  TDataSet;
  var TempTable:TClientDataSet;
  begin
   TempTable:=nil;
   Result:=nil;
   if AFieldDefs<>nil then
   begin
    try
     TempTable:=TClientDataSet.Create(Application);
     TempTable.FieldDefs.Assign(AFieldDefs);
     TempTable.CreateDataSet;
     Result:=(TempTable as TDataSet);
    Except
     if TempTable<>nil then TempTable.Free;
     Result:=nil;
     raise;
    end
   end
  end;

  在程序中按如下方法使用:

  procedure TForm1.Button1Click(Sender: TObject);
  var ADataSet:TDataSet;
  begin
   ADataSet:=TDataSet.Create(Self);
   with ADataSet.FieldDefs do
   begin
     Add(′Name′,ftString,30,False);
     Add(′Value′,ftInteger,0,False);
   end;
   with DataSource1 do
   begin
     DataSet:=CreateTableInMemory(ADataSet.FieldDefs);
     DataSet.Open;
   end;
   ADataSet.Free;
  end;

  临时表创建完成。

  方法1使用简单,但由于利用查询控件,清空数据时需要查询服务器后台数据库,所以速度稍慢,而且不适用于临时表中各个字段由数个数据表的字段拼凑而成的情况。方法2适用范围广、速度快,但需要编写代码。(代码中TFieldDefs的使用方法十分简单,见Delphi的联机帮助)。  

 
 2003-11-25 17:08:31    Delphi中多库关联查询

  在我们对数据库进行操作时,经常用到TTable控件,但TTable只能同时对一个数据表进行操
作,而TQuery控件不仅具有TTable的多数功能,而且同时可对多个数据表进行操作。不仅如此,
TQuery控件还有更强大的数据库查询功能,可以同时对多个不同结构的数据库进行关联查询,被
查询的数据库可以是ODBC支持的任何一种类型,当然计算机中必须先安装ODBC和BDC(Delphi的数
据库引擎),现举例说明。

  一、连接Paradox和dBASE进行异库查询
  例如:Master.db和Customer.dbf分别为Paradox和dBASE的数据库,它们有一个公共字段
CustNo。这时可输入如下代码:
  Query1.Close;
  Query1.DataBaseName:=′′;
  Query1.SQL.Clear;
  Query1.SQL.Add(′Select * from ″Master.db″ A,″Customer.dbf ″ B Where
A.CustNo=B.CustNo′);{在同一行内输入}
  Query1.Open;
  注意:进行多库联查时,TQuery控件的DataBaseName属性必须先置空,单引号内不包含任何
字符;A和B分别是Master.db和Customer.dbf的别名(引用别名可减少代码长度)。

  二、MS Access的多表联查
  Access数据库属于多表集合数据库(一个数据库中包含多个数据表),所以,对两个Access数
据库进行查询的方法就与上述有所不同。例如:我们有RSDA.mdb和ZFGjj.mdb两个数据库,RSDA中包含人事档案、在职职工目录和离职职工目录等数据表,ZFGjj通过公共字段“用户号”与RSDA相连。如果我们从ZFGjj中提取一个用户号,需要从RSDA中得到拥有此用户号的职工姓名,这时就必须用到TQuery的多表查询。
  第一步 在ODBC中增加两个数据源“ZFGjj”和“RSDA”。
  第二步 在BDE数据库引擎中添加两个数据库列名“ZFGjj”和“RSDA”。
  第三步 在表单中增加两个DataBase控件“DataBase1”和“DataBase2”,在AliasName属性
中分别选择“ZFGjj”和“RSDA”,DataBaseName属性中分别键入“ZFGjj”和“RSDA”,最后将
KeepConnection和Connected属性都设为“True”。

  现在可以输入相应的代码了:
  Begin
    For I:=1 To 10 Do
    Begin
      Query1.Close;
      Query1.DataBaseName:=′′;
      Query1.Sq1.Clear;
      Query1.Sq1.Add(′Select A.七月,B.个人账号,B.用户号,C.姓名From“:ZFGjj:个人汇激部门表”A,“:ZFGjj:住房公积金明细表”B,“:RSDA:在职职工目录”C Where(A.用户号=B.封存=:ib)′);{在同一行输入}
      Query1.Prepare;
      Query1.Params[0].DataType.=ftInteger;
      Query1.Params[1].DataType.=ftBoolean;
      Query1.Params[0].Asinteger:=ia[I];
      Query1.Params[1].Asboolean:=ib;
      Query1.Open;
    End;
  End;

  代码中SQL语句必须在一行内输入,不能分行。SQL语句中使用了动态参数“ia”和“ib”,
Delphi会自动按参数出现的顺序将它们添加在Query1的Params属性中并分配索引号分别为0和1,
参数“ia”和“ib”应在程序开始时声明其类型并赋值。

 
 2003-11-25 17:19:50   

   本来是想写个数据库使用蓝皮书的,但是现在感觉范围太宽泛了,改名吧,自己感觉里面的内容也很松散,不过给像我这样的初学者还是有点帮助。今天就这样吧

你可能感兴趣的:(数据库,操作系统,人工智能)