Visual FoxPro中的Select查询(二)

SQL 和字母大小写

SQL 中是区分字母大小写的,为使查询结果有效,要注意区分查询条件中的大小写.即表中的内容是大写的查询时必须将它们转换为大写.若表中内容是大小写混杂的,则以下 Select 语句将难以返回全部结果:
Select CompanyId ;
  From Company ;
  Where CompanyId = “ACME”
以上查询只返回名为 ACME 的记录,对于 CompanyId 为 Acme 的记录则查不到,因此安全的方法是用 UPPER() 函数将所有字符串都转换为大写.以下是正确的查询:
Select CompanyId ;
  From Company ;
  Where UPPER(CompanyId) = “ACME”

SQL 和索引

WHERE 条件可用 Rushmore 和索引加速查找.SQL 可用任何类型的索引:结构化的 .CDX ,外部的 .CDX 及外部的 .IDX.若要使 SQL 使用外部索引,必须在执行 Select 之前将其打开,如以下语句所示:
Use Sales INDEX idxfile,external cdxfile
SQL 可以以同样的方式作为标准过程代码使用 Rushmore.这时选择条件必须是可优化的,即选择表达式必须与建立索引的表达式精确匹配.

用 Order By 子句对结果排序

由于 SQL 优化器是按它认为的最佳方案去存取数据,因些查询结果中的记录顺序是动态不定的.许多应用程序需要查询结果是按一个或多个字段排好序的.为此可用 ORDER BY 子句.
ORDER BY 子句可以包含 Select 子句中一样多的字段.例如:
Select Company,State,CompanyId ;
  From Client ;
  Where State IN(“NY”,”NJ”,”PA”,”CA”) ;
  Order By State,Company
在以上查询中,按所在州及公司名称顺序给出结果.缺省情况下,记录是升序排字段,也可用 DESCENDING 来说明按降序排序.当然也可以升降序并用,例如:
Select Company,State,CompanyId,SaleNo ;
  From Company,Sales ;
  Where Company.SaleId = SalesMan.SaleId ;
  AND State IN(“NY”,”NJ”,”PA”,”CA”) ;
  ORDER BY State,Company,SaleNo DESCENDING
以上查询结果首先按州及公司名称升序排字段,然后按 SaleNo 降序排序.
由于排序是对查询结果的排序,因此 Order By 子句中的字段必须是 Select 子句中出现的字段.
另外, Order By 子句允许用位标号给出排序字段,这些位标号是指 Select 子句中出现的序号.例如上一个查询可以写为:
Select Company,State,CompanyId,SaleNo ;
  From Company,Sales ;
  Where Company.SaleId = SalesMan.SaleId ;
  AND State IN(“NY”,”NJ”,”PA”,”CA”) ;
  ORDER BY 2,1,3 DESCENDING
当 Select 子句包含了用户定义函数或 FoxPro 函数(如 UPPER() 和 LEFT() )时,用位标号来给出排序字段显得十分方便.

列函数

在 Select 子句中,可以用 SQL 提供的五个列函数,如下表所示:

SQL 的列函数

函数            描述
AVG(字段名)        求出指定字段上的平均值,只适用于数值型的字段
SUM(字段名)        求出指定字段上的和,只适用于数值型的字段
MIN(字段名)         求出指定字段上的最小值,字段可以是数值型,字符型或日期型
MAX(字段名)        求出指定字段上的最大值,字段可以是数值型,字符型或日期型
COUNT(字段名)   求出指定字段上的值的出现个数,可以用 COUNT(*)求出记录数
所有列函数的结果都是单值
关于列函数,别有以下需要注意的地方:

列函数中的 DISTINCT

  可以在任一个列函数中(除了 MIN() 和 MAX())使用 DISTINCT,这样在列函数的求值中将不计重复值.例如:
SELECT AVG(DISTINCT SalesPrice) ;
  FROM Sales
在以上查询中,求出商品的销售单价,对于重复的 SalesPrice,只计算一次.注意在 Select 子句中只能使用一次 DISTINCT .以下查询是不正确的:
SELECT DISTINCT AVG(DISTINCT SalesPrice),SalesMan ;
  FROM Sales
还有一点要注意的是,在 COUNT() 函数中,* 不能与 DISTINCT 连用(如 COUNT(DISTINCT *)),若在 COUNT() 中使用 DISTINCT,需给出字段名.

列函数的嵌套

以下查询是列函数嵌套的情况:
Select SUM(AVG(SalesPrice)),SalesMan ;
  From Sales ;
  Group By Product_ID
该查询是想求出每一产品的平均单价的和,但在运行时 FoxPro 会给出如下错误:
文件 “AVG.prg” 不存在
这说明列函数是不能嵌套使用的.
例一:
人员资料表 ryb,内容如下:
科室 姓名 职称 政治面貌
办公室 awfw 助工 党员
通讯科 wafawe 高工 党员
机械科 afawe 技术员 团员
财务科 2wraw
人事科 afwe 工程师
现要统计每个科室各有几名技术员、助工、工程师、高工、党员、团员,以下列表的形式打印出来
科室 技术员 助工 工程师 高工 党员 团员
财务科 2 2 3 3 4 3
  命令如下:
Select 科室,;
  sum(iif(职称=”技术员”,1,0)), ;
  sum(iif(职称=”;助工”,1,0)), ;
  sum(iif(职称=”工程师”,1,0)), ;
  sum(iif(职称=”高工”,1,0)), ;
  sum(iif(政治面貌=”党员”,1,0)),;
  sum(iif(政治面貌=”团员”,1,0)) ;
      from ryb group by 科室
例二:
  表 recdbf
    〖性质〗分一般、重大、特大;
    〖日期〗
要统计出某年的 12 个月内每个月每种性质的记录各有多少,若该月没有记录就为零。
结果:
月份  一般 重大    特大
 1   0  1        3
 2   2  12      3
 ……
 12   3  0        5
这跟上面那个例子有点不同,上例只是按表中科室来分类,而这里就不同,因为不是每个月都有案件的,但在报表里没有案件的月也要占一行。所以这里要建立一个辅助表:tempyf(yf N(2)),内有十二个记录,值为 1 至 12,代表 1-12 月。
我先是老规则:
Select month(日期),;
   iif(性质=”一般”,1,0) as 一般,;
   iif(性质=”重大”,1,0) as 重大,;
   iif(性质=”特大”,1,0) as 特大 ;
    from recdbf ;
      where year(日期)=?年份 ;
      into curs temp1
再用 tempyf 左联接临时表 temp1,根据 tempyf.yf 分组统计。
但一看,结果好象不对,没有记录的月份不在结果当中,而且这两条select好象可以合而为一。
以上查询可以更简洁地使用以下命令:
SELECT tempyf.*,;
  SUM(IIF(ISNULL(recdbf.日期) OR AT(“一般”,recdbf.性质)=0,0,1)) AS 一般,;
  SUM(IIF(ISNULL(recdbf.日期) OR AT(“重大”,recdbf.性质)=0,0,1)) AS 重大,;
  SUM(IIF(ISNULL(recdbf.日期) OR AT(“特大”,recdbf.性质)=0,0,1)) AS 特大;
 FROM tempyf LEFT OUTER JOIN recdbf ;
  ON tempyf.yf = MONTH(recdbf.日期) AND YEAR(日期) = ?yy;  &&注意这里,on后面是可以加上其它条件的
 GROUP BY tempyf.yf
在上例中, yy 是指具体那一年,如果不指定的话,那就是把表中所有的记录算在一起。而且如果要指定具体那一年的话,那这个 YEAR(日期) = ?yy 的条件是不能放在 where 或者 having 里的。
以上查询的另一个有趣的地方是:sum 命令是可以直接累加 iif 的结果,而且过滤条件从 where 移到 on 后会发生这么大的差别!在 where 时,是先联接统计后再过滤,结果把那些没有记录的月份也过滤掉了;而在 on 是先过滤再联接、统计,过滤结果中虽然没有记录的月份也不在其中,但因是左联接,也一样会以 null 代替,但给 iif 中的 isnull 给变成了 0.
注:其中那个 ON tempyf.yf = MONTH(recdbf.日期) AND YEAR(日期) = ?yy 在视图生成器中是不能直接做的,而只能用 Create sql view as 以命令的方式生成。

用 GROUP BY 求分组小计

GROUP BY 的意思是按给定字段进行分组,所谓分组就是将一组类似的记录压缩成一个结果记录,这样就可以完成基于一组记录的计算。例如,若想找到某一特定地区所有订货的总和,不用单独查看所有的记录,可以把来自相同地区的所有记录合成为一个记录,并获得来自该地区的所有订货的总和。
分组在与某些合计函数联合使用时效果最好,诸如 SUM、COUNT、AVG 等等。
例如,若想看到订单表中具有特定 ID 号的客户订货的金额总值,只需将具有相同 Customer ID 号的订货记录合成为一个记录,同时寻找订货总量即可。

GROUP BY 和 DISTINCT 之间的选择

  如果在字段串列中没使用字段函数,则 group by 参数与关键字 distinct 的效果相同。不过使用 group by 参数的效果显然要比使用关键字 distinct 来得快,而且在字段串列中的字段数越多,速度的差距会越大。
  如果在字段串列中使用字段函数且使用 group by 参数,则仍会显示出多个数据记录。但是如果于字段串列中使用字段且使用关键字 distinct,则只有一个数据记录会显示出来。显然有字段时应使用 group by 参数。
例子:
1、select name,max(salary) from test group by name
2、select distct name,max(salary) from test
结果:
             grouy by                   distinct
    name    salary       name    salary             name    salary
    alex    20            alex     90                mary    95
    alex    10            mary    44
    alex    50            tom     95
    alex    90
    alex    30
    tom     45
    tom     55
    tom     15
    tom     95
    mary    33
    mary    44

HAVING 子句的使用

我们已经可以求分组小计,但另一问题是:如何控制或筛选分组?这里不能用 WHERE 子句,因为它只能对基表中的记录作筛选。而我们要筛选的是中间表的分组记录。
HAVING 子句可用于控制从中间表到最终结果的过滤。虽然在 HAVING 子句中可以用任何合法的字段或表达式,但一般是用列函数。原因是 HAVING 子句要跟在 GROUP BY 子句后使用,这意味着 SELECT 子句中有列函数,例如:
SELECT SALE_NO,SUM(SALE_AMOUNT) AS AMOUNT ;
   FROM SALEITEM,SALES ;
   WHERE SALES. SALE_NO = SALEITEM. SALE_NO ;
   GROUP BY SALE_NO ;
   HAVING SUM(SALE_AMOUNT)>1000
以上查询求出销售金额超过 1000 元的销售单及金额。
HAVING 子句中的列函数不必与 SELECT 子句中的列函数相同,例如
SELECT SALE_NO,SUM(SALE_AMOUNT) AS AMOUNT ;
   FROM SALEITEM,SALES ;
   WHERE SALES. SALE_NO = SALEITEM. SALE_NO ;
   GROUP BY SALE_NO ;
   HAVING AVG(SALE_AMOUNT)>100
此查询求出平均商品金额在100 元以上的销售记录,结果中要求给出销售单的总金额。
WHERE 和 HAVING 都为查询提供过滤条件,经常有人问哪一个要好些。答案是,为了获取更好的性能,应该用 WHERE 子句来过滤一般表的记录,而用HAVING 过滤 GROUP BY 后的分组结果。
另外,使用 HAVING 子句时容易犯的一个错误是:
SELECT * FROM SALES HAVING SALESMAN_ID = ““001”
虽然以上查询也可运行,但不能利用 RUSHMORE,因此要比以下查询慢数百到数千倍:
SELECT * FROM SALES WHERE SALESMAN_ID = ““001”
示例:
对于 HAVING 子句,select 的条件过滤的先后顺序是这样的:先对join 中的 on 表达式进行过滤,再到 where,中间结果出来后再用 having 进行过滤,最后才把结果显示出来。所以说 having 是对 select 结果的最后一次过滤。它与 where 的分别就是 where 能够事先把不要的数据过滤掉,这样 select 里头就不用处理那么多的数据。但有些数据事先不知道要不要过滤,要根据结果才能确定,这时才用 having 这个事后诸葛亮。
这里用例子来比较一下 on、where、having 的不同之处
表 recdbf 内容如下:                    还有一个 tempyf 的辅助表,记录 12 个月
  日期                性质                  yf
  2000年7月3日        特大                      1
  2000年7月9日        特大                      2
  2000年9月3日        特大                      3
  1999年3月2日        一般                      4
  1999年3月4日        一般                      5
  2000年1月3日        一般                      6
  2000年2月1日        一般                      7
  2000年2月3日        一般                      8
  2000年3月4日        一般                      9
  2000年8月7日        一般                      10
  2000年11月2日       一般                      11
  1999年2月3日        重大                      12
  2000年2月3日        重大
  2000年5月2日        重大
  2000年8月9日        重大
on 的命令如下
SELECT tempyf.*,;
  SUM(IIF(ISNULL(recdbf.日期).OR.AT(“一般”,recdbf.性质)=0,0,1)) AS 一般,;
  SUM(IIF(ISNULL(recdbf.日期).OR.AT(“重大”,recdbf.性质)=0,0,1)) AS 重大,;
  SUM(IIF(ISNULL(recdbf.日期).OR.AT(“特大”,recdbf.性质)=0,0,1)) AS 特大;
 FROM tempyf LEFT OUTER JOIN recdbf ;
  ON tempyf.yf = MONTH(recdbf.日期).AND.YEAR(日期) = ?yy;
 GROUP BY tempyf.yf
其中 yy=2000,表示统计 2000 年的数据
用 where 的命令如下:
SELECT tempyf.*,;
SUM(IIF(ISNULL(recdbf.日期).OR.AT(“一般”,recdbf.性质)=0,0,1)) AS 一般,;
SUM(IIF(ISNULL(recdbf.日期).OR.AT(“重大”,recdbf.性质)=0,0,1)) AS 重大,;
SUM(IIF(ISNULL(recdbf.日期).OR.AT(“特大”,recdbf.性质)=0,0,1)) AS 特大;
FROM tempyf LEFT OUTER JOIN recdbf ;
ON tempyf.yf = MONTH(recdbf.日期);
GROUP BY tempyf.yf ;
where YEAR(日期) = ?yy         && 注意,条件从 on 移到这里来了
用 having 的命令如下:
SELECT tempyf.*,;
SUM(IIF(ISNULL(recdbf.日期).OR.AT(“一般”,recdbf.性质)=0,0,1)) AS 一般,;
SUM(IIF(ISNULL(recdbf.日期).OR.AT(“重大”,recdbf.性质)=0,0,1)) AS 重大,;
SUM(IIF(ISNULL(recdbf.日期).OR.AT(“特大”,recdbf.性质)=0,0,1)) AS 特大;
FROM tempyf LEFT OUTER JOIN recdbf ;
ON tempyf.yf = MONTH(recdbf.日期);
GROUP BY tempyf.yf ;
having YEAR(日期) = ?yy         &&注意,条件从 on 移到这里来了
on 的结果如下,这是正确的
YF                   一般                   重大                   特大
1                      1                      0                      0
2                      2                      1                      0
3                      1                      0                      0
4                      0                      0                      0
5                      0                      1                      0
6                      0                      0                      0
7                      0                      0                      2
8                      1                      1                      0
9                      0                      0                      1
10                      0                      0                      0
11                      1                      0                      0
12                      0                      0                      0
用 where 的结果如下:
YF                   一般                   重大                   特大
1                      1                      0                      0
2                      2                      1                      0
3                      1                      0                      0
5                      0                      1                      0
7                      0                      0                      2
8                      1                      1                      0
用 having 的结果如下:
YF                   一般                   重大                   特大
1                      1                      0                      0
2                      2                      2                      0
5                      0                      1                      0
7                      0                      0                      2
8                      1                      1                      0
9                      0                      0                      1
11                      1                      0                      0
以上查询有什么不同呢?
on 是把先把 recdbf 中不是 2000 年的记录过滤掉,剩下的就是 2000 年的了,再用tempyf去和它们进行外联接,其结果可用
select tempyf.*,recdbf.日期 ;
from tempyf left join recdbf ;
ON tempyf.yf = MONTH(recdbf.日期).AND.YEAR(日期) = ?yy;
GROUP BY tempyf.yf
来查看,这个中间结果出来后,再用 isnull 把空值的记录变成 0 或 1,然后由 sum 去统计,结果就出来了
而 where 呢,
1、它是先把 tempyf 外联接 recdbf,相当于
select tempyf.*,recdbf.* ;
from tempyf left join recdbf on tempyf.yf=mont(recdbf.日期)
2、然后把不是2000的记录过滤掉,这里要注意的是,如果某个月没有记录的话,那在第一个步骤后日期那里是 null 值,这当然不是 2000 的记录,所以就给这个条件给过滤出去了,所以下一步的 sum 之后就只剩下那有记录的那个月了,象 4、6 月等几个月。就没有 3、然后进行 sum(……)
再看 having
1、第一步和 where 一样,
2、第二步不同,它是先 sum(),这里的 sum 可不管你是 1999 年还是 2000 的,先累加起来再说,这时, 1999 和 2000 年的 2 月份都有“重大”这个记录,sum 的结果是 2,这里用第三个步骤去分辨这个 2 之中那个是 1999 年的,那个是 2000 的,这当然分不清啦,所以也错了。
3、根据步骤 2 来把 2000 的过滤出来。
所以 on、where、having 这三个都可以加条件的子句中,on 是最先执行,where 次之,having 最后。但有时候如果这先后顺序不影响中间结果的话,那最终结果是相同的。但因为 on 是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的。根据上面的分析,可以知道 where 也应该比 having 快点的,因为它过滤数据后才进行 sum,所以 having 是最慢的。但也不是说 having没用,因为有时在步骤3还没出来都不知道那个记录才符合要求时,就要用 having了。

用 TOP 子句选择一定数量或一定百分比的记录

TOP 子句用来限定结果集中返回的记录数:
l          结果集中的头 50 条记录
TOP 50
l          头 25%
TOP 25 PERCENT
在查询的结果集合中,如果只需要其中一定数量或百分比的记录,可以在“查询设计器”或“视图设计器”中,使用“杂项”选项卡上的“列在前面的记录”属性设置数量或百分比,也可以在 SELECT – SQL 命令中添加 TOP 子句。在 TOP 子句中,所允许的数值范围为 1 到 32767;如果使用的是百分数,则范围为 0.001 到 99.99。
例如,如果要选择具有最高总订货量的前10位顾客,可以用 GROUP BY 指定 CUST_ID 字段,来显示每个顾客的合计记录,然后在 ORDER BY 子句中按照 ORDER_AMT 排序。若要得到最高的前十个记录,需要在 ORDER_AMT 上面指定降序排列,从而使具有最高订货量的顾客在结果中最先显示。如果使用升序排列,结果记录则按照订货量由少到多的次序排列,那么在结果集合中选择的前几个记录实际上是订货量最少的。
SELECT TOP 10 *;
  FROM  testdata!customer INNER JOIN testdata!orders ;
  ON  Customer.cust_id = Orders.cust_id;
  GROUP BY Customer.cust_id;
  ORDER BY Orders.order_amt DESC

查询结果的输出定向

使用 SELECT – SQL 语句的各个子句,可以指定多种不同的输出目标来保存查询结果。
若要将查询结果输出到      使用子句
独立的表                           INTO TABLE mytable
数组                                  INTO ARRAY aMyArray
临时表                               INTO CURSOR mycursor
活动窗口                           TO SCREEN
浏览窗口                           如果没有指定其他目标,则用它作默认值。
保存了查询结果之后,您就可以使用命令将存储的结果加以适当组织,以便显示或打印。
将查询结果存储到表、数组或临时表中
查询结果可以存储到表、数组或临时表中,以备它用,例如处理表单以及打印报表和标签。如果只想暂时保存结果,则可将它们发送到数组或临时表中。若要永久保存结果,则应该将它发送到一个表中。
若要指定一个表作为输出目标,用 SELECT-SQL 语句的 INTO 子句指定一个目标。
下面的示例演示了如何用一个 INTO 子句指定一个表:
SELECT * ;
   FROM tastrade!customer ;
   WHERE customer.country = “Canada” ;
   INTO TABLE mytable
若要指定一个数组作为目标,用 SELECT – SQL 语句的 INTO 子句指定一个目标。
下面的示例演示了如何用 INTO 子句指定一个数组。
SELECT * ;
   FROM tastrade!customer ;
   WHERE customer.country = “Canada” ;
   INTO ARRAY aMyArray
若要指定一个临时表作为目标,用 SELECT-SQL 语句的 INTO 子句指定一个目标。
以下示例演示了如何用 INTO 子句指定一个临时表。
SELECT * ;
   FROM tastrade!customer ;
   WHERE customer.country = “Canada” ;
   INTO CURSOR mycursor

复杂查询

复杂查询是指需要多个 Select 语句来决定最后结果的查询。传统的讲,复杂查询包括子查询,自身连接和合并。
Select 命令参数很多,功能也很强大,适当的使用一些技巧,可以使程序更洁练,运行速度更快。但如果不是很强求速度的话,我认为太过复杂的命令对以后的维护工作带来困难。呵呵,有时我要理解自己写的命令都有点困难,实在是太复杂了。以下内容是工在工作过程中摸索出来的一些小技巧,希望对别人有帮助。

子查询

子查询有时又叫内部查询(INNER QUERY),是一个嵌入到另一个 SELECT 中的 SELECT 语句。
一个包括子查询的语句通常情况下以以下几种形式出现:
l          WHERE 表达式 [NOT] IN (子查询)
l          WHERE 比较操作符 [ANY|ALL] (子查询)
l          WHERE [NOT] EXISTS (子查询)
当需要从一个查询的结果来产生最终结果时,一般要使用子查询。
使用子查询时有如下限制:
l          子查询的 SELECT 子句中只允许有一个字段名,除非是在子查询中使用 * 通配符。
l          只能在主查询中使用 UNION 和 ORDER BY ,而不能在子查询中使用它们。
l          子查询必须用括号括起来。
l          子查询只能使用比较、包含、存在和量词等条件。
l          子查询中的 WHERE 子句可引用主查询中的字段,这称为外部引用

IN 和 NOT IN

IN 关键字用于决定一个特定值是否是内部查询所产生的结果列表值中的一个值。相反,NOT IN 决定了某个特定值是否不存在于一个内部查询的结果集中。
对包含条件(IN 关键字),子查询的结果可以是一组数值。例如,在 temp2 中列出所有包含在 temp1 中的记录。
例一
temp1 记录数:90112
temp2 记录数 22528
两表均按 name 建立索引,temp2 全部都是包含在 temp1 里的。
用子查询:
Select temp2.* ;
  from temp2 ;
  where name in (sele name from temp1) 1.542秒(有索引)  4.596(无索引)
用联接:
select temp2.* ;
  from temp2 ;
   join temp1 ;
     on temp2.name=temp1.name         2.384秒(有索引)  5.658(无索引)
相关的时间都是有一秒左右。这应该是因为子查询首先执行,它把 temp1 中的一部分(name)读入内存,然后 temp1 再在其中进行比较;而联接则一开始就是读文件,所以就慢了一点。
例二
列出没有采购的公司:
select company from customer ;
     where cno not in ;
       (select cno from invoices)
在上例中,先用 IN 关键字进行过滤,当过滤的条件表达式 cno 未出现在 invoices. Cno 中时便被选择出来。请注意 where 子句中的 NOT 关键字的用法,如果不用 NOT 关键字,则上面查询的作用就是列出有采购的公司。
IN查询中的子查询,匹配主查询的一个值。可得出一组值来检查包含关系。
因此,当希望由 SQL 产生一组有效值,而不是由用户输入时,可用 IN 子查询的方式。
量词只适用于来子查询的 IN 谓词。IN 谓词只是简单地判断外部查询的值是否与值表中的某一个值相等。量词可扩展这一包含关系,它可用于任何有效的比较符(如 <,>,<>等等)。量词可以是 ANY、SOME或ALL。

ANY|SOME

列出 ytdsales 字段至少必须大于 offices 其中个 ytdsales 字段之值。
select * from salesman ;
    where ytdsales>ANY ;
      (select ytdsales form offices)
对于外部查询的每一值,若小于子查询结果的任一值,则条件为真。在条件满足后,即终止比较。

ALL

当子查询中的所有值都能符合外部条件的比较符时,ALL 返回真。若有一个不满足,则停止处理并返回假值。
列出 ytdsales 字段必须大于 salesman.ytdsales 字段的值。
select * from offices ;
    where ytdsales>ALL ;
      (select ytdsales from salesman)
含有量词的查询经常引起混淆,因为出现的比较符常与要求中的相反。最后,量词产生一个逻辑值而不是一个比较值。

EXISTS 和 NOT EXISTS

子查询用 EXISTS 和 NOT EXISTS 测试来发现两个集合间是否有交叉功区别。
l          EXISTS (如果元素同时属于两个集合时为真)
l          NOT EXISTS (如果只有第一个集合包含了元素时为真)
EXIST关键字的含意是:判定外查询记录的某些值是否在内查询表中存在。例如:
列出没有采购的公司:
select company from customer ;
      where cno not exists ;
        (select * ;     &&用exists这里一定要用星号“*”
           from invoices where invoices.cno=customer.cno)
对于EXIST关键字有两点要注意。首先,含EXIST关键字的查询总是相关查询:即,内查询必然引用外查询的字段(如上例中的 cno)。这使但存在谓词的子查询相当慢,因因对于外查询的每一记录,子查询都要执行一遍。外查询中要处理的记录数越多,则查询执行时间越长。
其次,EXIST关键字的子查询是唯一允许用通配符的 * 的情况。由于结果只有真或假,对说明什么字段无关紧要,因此可用通配符。实际中,存在谓词的子查询总是用 * 。
存在谓词只是标准的带子查询 IN 谓词的特殊形式。一般来说,IN 谓词要快于存在谓词。
目前在实际的 SQL SELECT 中,其所有子查询都处在同一级,都是作为主查询的子查询。这就是说,仅可在主查询的选择条件中使用子查询。

自身连接

当一个表中的信息要与同一表中其它信息相比较时,需要用到自身连接。需要使用自身连接的有两种典型情况:一种是表中有多对多关系,一种是表中有跨越时间段的信息。
示例:
  按业务员的销售额的高低顺序,显示每一个业务员的销售额,并伴随显示那些比他销售还多的业务员的平均销售额。
Select a.salesman,a.name,a.ytdsales,AVG(b.ytdsales) as better ;
      from salesman A,salesman B ;  &&这里把salesman打开两次,分别起个别名A和B
      where A.ytdsales<B.ytdsales ;
      group by A.salesman ;
      order by A.ytdsales DESC
相同于使用联接:
Select a.salesman,a.name,a.ytdsales,AVG(B.ytdsales) as better ;
      from salesman inner join salesman salesman_a ;
        on salesman.ytdsales<salesman.ytdsales ;
      group by salesman.name ;
      order by salesman.dd desc
注:自连接时必须注意以下事项:
  1、同一来源数据库务必给予不同的别名;
  2、位于 select 命令语句的字段串列中各个字段前都必须中上别名;
  3、由于一旦自连接后即形成“多对多”的关联性连接,因此请小心设定 where 参数的连接条件。在一个有 1 千个记录的表中,执行第一条命令要 188.261 秒,第二条命令要 154.712 秒。而 500 个记录时,第一条命令 6.870 秒,第二条 7.190 秒。虽然其中的数值有点不是很准确,但这不重要,只要知道不要随便使用这种方法就行了。

合并

与其它复杂查询不同,UNION 实际上是 SELECT 语句上的子句。UNION 可以把来自两个或多个独立的 SELECT 语句的信息合并为一个结果。在多数情况下是用 UNION 把不相关表的信息合并起来。
UNION 有一个可选的子句 ALL。与SELECT 语句不同,UNION 在默认情况下使用 DISTINCT 消去结果中的重复记录。若要提高 UNION 的速度,可用 ALL 选项,这样免去 UNION 消去重复记录的操作。
为什么 UNION 在默认情况下要用 DISTINCT ?这是由这一操作的特性决定的。当把两个不相关的表合并为一个结果时,同一记录可能存在于两个表中。
与 SELECT 子句中的 DISTINCT 一样,当使用 DISTINCT 时会使 UNION 的速度下降。因此, 如果基于对应用程序和表的了解,知道不会有重复记录或重复记录很少,这时可用 ALL 来减少执行时间。
象子查询一样,UNION 要成功地执行,需遵循以下规则:
UNION 仅用于外查询, 不能用 UNION 合并子查询。
使用 UNION 的 SELECT 子句必须有相同的字段数,相同的数据类型和相同的长度和精度,因为 SQL 需把 SELECT 语句的结果合并到一张表中。
为此 SQL 使用第一个查询语句的 SELECT 子句作为结果表的结构。若 UNION 中的其它查询中的语句中的字段需在结果中,则必须出现在第一个查询的 SELECT 子句中。若第一个 SELECT 子句中的某字段不在其它查询语句中的表中,则该字段仍必须出现在这些查询语句的 SELECT 子句中。
具体的例子如下:
    在做生产管理时,原材料的每笔进货、领用、报废、退货情况都要详细记录,老板随时都可能了解一个月内每种原材料的各种情况。而编程时对数据的规范化后,必然要用好几个表来记录这四种操作。现在为了更快的求出结果,命令当然是越少越好。各个表的结构如下:
    进货单jhd:jhdbh,jhrq
    进货单明细jhdmx:jhdbh,yclbh,jhsl
    领货单lhd:lhdbh,lhrq
    领货单明细lhdmx:lhdbh,yclbh,lhsl
    报废单bfd:bfdbh,bfrq
    报废单明细bfdmx:bfdbh,yclbh,bfsl
    退货单thd:thdbh,thrq
    退货单明细thdmx:thdbh,yclbh,thsl
    原材料表yclb:yclbh,yclmc,yclgg
    查询日期范围放在 begindata,enddata 里
    为简便起见,只计算进货和退货,其它的照样做就行了。
    Select yclbh,sum(jhsl) as jhsl,100000.00-100000.00 as thsl ;
       From jhd join jhdmx ;
         On jhd.jhdbh=jhdmx.jhdbh ;
       Where between(jhd.jhrq,begindata,enddata) ;
       Group by yclbh ;
    Union ;
    Select yclbh,100000.00-100000.00 ,sum(thsl) ;
       From thd join thdmx ;
         On thd.thdbh=thdmx.thdbh ;
       Where between(thd.thrq,begindata,enddata) ;
       Group by yclbh ;
       Into cursor temp1
    这样就会产生这样的结果
        yclbh    jhsl    thsl
        00001    3432   0    ┐
        00002    4234   0    │这些是第一节select产生的
        ……                  ┘
        00002    0    3423    ┐
        00003    0    4234    │这些是第二节select产生的
        ……                   ┘
    现在再对临时表temp1进行一次合计
    select yclbh,sum(jhsl) as jhsl,sum(thsl) as thsl ;
      from temp1 ;
      group by yclbh ;
      into cursor temp2
    这个temp2就是结果了。
    注1:如果要计算四种操作的话,就再加多两节 select 就行了。
    注2:在命令里使用 100000.00-100000.00 这样奇怪的表达式是因为 select 对那些要计算的、事先不能确定长度和类型的字段,它是根据运算过程中产生的第一条记录中,该字段的值来确定这个字段的类型和长度。所以以上那条命令,第一个记录中 thsl 它是不知道长度是多少的,如果直接用0的话,那它就以为这个字段是数值型,长度是1,没有小数位。这当然不行,所以就要用这样一个表达式,来使它知道这个字段有9位长,小数位是 2。

示例一.快速排名次

  考试排名次,一般有个要求,就是如果有两个人并列第一,那跟着的就不是第二名,而是第三名,即第二名跳空了。所以我想出以下这个方法。
  面对大数据量的排名次,若用 scan…endscan  或 do while  这种方法,时间会非常长。1048576 个记录中花了十多分钟才排了四十多万条记录。而我这种方法只需九十多秒(硬件:PII300(100*3),128M PC100,5.1G,VFP5.0),方法如下:
1。数据库dele:rec_id c(7) 升序;name c(10);fs n(4,1) 降序;rec_orde n(7),文件大小为29M,索引文件16M
2。建一个视图dele1
SELECT Dele.fs, Dele.rec_orde;
  FROM dele;
    ORDER BY Dele.fs DESC
3。根据视图dele1建立视图dele2
SELECT DISTINCT Dele1.fs, MIN(Dele1.rec_orde) AS rec_orde;
  FROM dele1;
  GROUP BY Dele1.fs
4。根据视图dele2和数据库dele建立视图dele3
SELECT Dele.rec_id, Dele.rec_orde, Dele2.rec_orde, Dele.fs;
  FROM dele INNER JOIN dele2 Dele2 ;
  ON Dele.fs = Dele2.fs;
  ORDER BY Dele2.rec_orde
准备工作做好,现在可以排序了:
以上视图是事先做好的,不算入运行时间。
use dele1
replac rec_orde with recno() all
select 0
use dele3
replac rec_orde_a with rec_orde_b all
browse

示例二.选择重复的记录

SELECT * ;
FROM table A ;
WHERE EXISTS ;
(SELECT * ;
FROM table B ;
WHERE B.key = A.key;
GROUP BY key ;
HAVING COUNT(*) > 1 ) ;
ORDER BY fieldlist
SELECT * ;
FROM table ;
WHERE key IN ;
(SELECT key ;
FROM table ;
GROUP BY key ;
HAVING count(*) > 1) ;
ORDER BY fieldlist

外连接

    外连接仅被 Visual FoxPro 5.0及以上版本所支持! 本节讨论使用这一有力的新功能的语法和技术.多年来, 人们常常抱怨关于在 FoxPro 中实现的 SQL 缺乏对外部连接的支持. 现在, 在 Visual FoxPro 5.0 版中, Microsoft 扩展了它的 SQL 并实现了外连接.

什么是外连接?

在Visual FoxPro 5.0以前的版本中执行一个简单的两表查询时, 两表之间的连接类型称为 “自然” 或 “内” 连接. 如果你应用该原理到操作中, 会产生一个两表的交集的结果集.
因而, 内连接的结果集忽略两表中无”连接”的记录, 也就是说, 忽略不满足 SELECT 语句中的 WHERE 子句中指定的条件的记录. 换句话说, 没有子记录的父记录和没有相应父记录的子记录不会包含在内连接中. Visual FoxPro 5.0 以前的版本, 最简单的两表”自然”或内连接的示例语法如以下伪代码所示:
SELECT <field list> ;
   FROM <table1>, <table2> ;
   WHERE <table1.fieldName> = <table2.fieldName>
但是, 许多时候可能你想产生的结果集不仅包括匹配连接条件的记录,而且还要包括不匹配连接条件的记录. 外部连接允许我们包含不匹配连接条件的记录.
例如, 如果你查看 Visual FoxPro  的示例数据(\VFP\SAMPLES\DATA 目录中的 TESTDATA 数据库), 有一个 EMPLOYEE 表,它可以与 ORDERS 表连接 (用 Emp_ID 字段), 允许一个查询来显示雇员的相关订单信息. 如果一个新雇员还没有下过订单, 查询结果集中将不会包含该雇员的信息. 这对于在只对产品订货感兴趣时是令人满意的, 但要报告所有雇员和他们的行动时, 这样的报表就不能完全反映出所有雇员的信息. 如果用自然或内部连接将产生一个不完全结果,因为雇员至少要完成过一份订单, 否则就没有与 ORDERS 表的连接.
在构造一个两表间的外部连接时, 不能简单的称它为“外部连接”, 因为非匹配信息可能会来自一个或另一个, 或两个表中. 因此, 有三种类型的外部连接: 左外部连接, 右外部连接和完全外连接.
以下示例选自 VFP 示例Solution 项目。

用右外连接(Right Outer Join)选择记录

文件: SAMPLES\DATA\ROUTERJ.QPR
Solution 项目中的查询 ROUTERJ , 使用 testdata 数据库并用右外连接从orders 表和employee 表中组合信息.
结果记录中的 orders 表中的 order_id 和 COUNTRY 字段, employee 表中的lastname 字段都在SELECT-SQL 语句中指定.
SELECT Orders.order_id, Employee.last_name;
 FROM testdata!employee RIGHT OUTER JOIN testdata!orders ;
 ON Employee.emp_id = Orders.emp_id
典型的, 右外连接可以回答两个关于你的数据库中的记录的问题. 该查询将回答的一些问题是:
l          哪些订单是由哪些雇员输入的?
l          哪些订单没有指定雇员?
该信息可以帮助跟踪指定的订单并识别在下订单时没有指定雇员的订单.
右外连接从连接条件右边的表中获取全部记录,并从连接条件左边的表中组合符合连接条件的记录结果集包括了两个记录子集.
l          两个表中匹配连接条件的记录.
l          两个表中不匹配连接条件的记录.
因为结果集中的每一记录具有相同的字段,记录中的 lastname  在 orders 表中没有匹配,在记录中的该字段用一个NULL 值作为占位符. 例如, 如果11078号订单没有相关的雇员记录在employee 表中, 出现在结果集中的记录在该lastname字段上是一个NULL 值.
11078 号订单在EMPLOYEE表中没有匹配记录.
可以指定筛, 排序, 分组或其它各种查询选项来改变查询结果.

用左外连接(Left Outer Join)选择记录

文件: SAMPLES\DATA\LOUTERJ.QPR
Solution 项目中的查询 LOUTERJ ,从 orders 表和 customer 表中用左外连接组合信息. 结果记录中的 order 表中的order_id字段和 customer 表中的 cust_id, company, country 字段都在SELECT-SQL 语句中指定.
SELECT Customer.cust_id, Customer.company, Customer.country,;
 Orders.order_id;
 FROM testdata!orders LEFT OUTER JOIN testdata!customer ;
  ON Orders.cust_id = Customer.cust_id
典型的,左外连接可以回答两个关于你的数据库中的记录的问题. 该查询将回答的一些问题是:
l          哪些订单属于哪些客户?
l          哪些订单没有相应的客户信息?
该信息可帮助数据库管理员确定哪些订单记录丢失了信息.
左外连接返回连接条件左边表中的全部记录,并从连接条件左边的表中组合符合连接条件的记录结果集包括了两个记录子集.
l          两个表中匹配连接条件的记录.
l          Orders 表中不匹配连接条件的记录.
因为结果集中的每一记录具有相同的字段,记录中的 lastname  在 orders 表中没有匹配值,在记录中的该字段用一个NULL 值作为占位符. 例如, 如果11078号订单没有相关的雇员记录在employee 表中, 出现在结果集中的记录在该lastname字段上是一个NULL 值.
因为结果集中的每一记录具有相同的字段,Order 表中具有 order_id 记录但在客户表中没有匹配值, ,在记录中的该字段用一个NULL 值作为占位符. 例如, 11079号订单,在customer 表中没有匹配的客户记录,出现在结果集中的该记录的cust_id 和 company 和 country 字段的值为NULL.
11079 号订单在 Customer 表中没有匹配记录
可以指定筛, 排序, 分组或其它各种查询选项来改变查询结果.

用完全外连接(Full Outer Join)选择记录

文件: SAMPLES\DATAT\FOUTERJ.QPR
Solution 项目中的查询 FOUTERJ ,从 country 表和 customer 表中用完全外连接组合信息. 结果记录中的 country 表中的全部字段和 customer 表中的 country 和 cust_id 字段都在SELECT-SQL 语句中指定.
SELECT Country.*, Customer.country, Customer.cust_id;
 FROM testdata!customer FULL JOIN country ;
  ON Customer.country = Country.country
典型的, 完全外连接可以回答关于你的数据库中的记录的三个问题. 该查询将回答的一些问题是
l          哪些客户在哪些国家?
l          哪些国家中没有我们的客户?
l          哪些客户记录丢失了国家信息?
该信息可以帮助一些人决定是否他们将扩展他们的业务到其它国家, 在某些国家中改变他们的市场策略, 或只是数据库中的一些记录需要更新国家信息.
完全外连接从两个表中返回所有记录并按连接条件组合匹配记录.结果集包括三个记录子集.
l          匹配连接条件的记录.
l          国家表中不匹配连接条件的记录.
l          客户表中不匹配连接条件的记录.
因为结果集中的每一记录具有相同的字段, 记录中与另一表中不匹配的字段出现占位的 NULL 值. 例如, 如果一个记录的国家 Russia, 在 customer 表中没有相关的客户记录, 出现在结果集中的该记录的cust_id 和 country_b 字段的值为NULL.
不匹配记录字段中出现 NULL 值.
可以指定筛, 排序, 分组或其它各种查询选项来改变查询结果.

用内连接加外连接选择记录

文件: SAMPLES\DATA\COMBOJ.QPR
Solution 项目中的查询 COMBOJ , 使用 testdata 数据库, 从 customer, orders 和 orditems表中组合信息. 结果记录中的 customer 表中的 company 字段, orders 表中的order_date 字段和 orditems 表中的 line_no 字段都在SELECT-SQL 语句中指定.
SELECT Customer.company, Orders.order_id, Orditems.line_no;
 FROM testdata!customer LEFT OUTER JOIN testdata!orders;
    INNER JOIN testdata!orditems ;
   ON Orders.order_id = Orditems.order_id ;
   ON Customer.cust_id = Orders.cust_id
在该查询中, orders 与 orditems 之间的连接首先被求值并产生一个满足连接条件的一个子集. 因为内连接, 子集中只有满足两表连接条件的记录. 子集中的记录与customer 表中的记录匹配. 因为customer 和 orders间的外连接, 所有没有订单的客户也会出现在结果集中,且订货部份的内容的值为NULL.
没有订货的客户包括在结果中.
可以指定筛, 排序, 分组或其它各种查询选项来改变查询结果.

用内连接(Inner Join)选择记录

文件: SAMPLES\DATA\INNERJ.OPR
Solution 项目中的查询 INNERJ , 使用 testdata 数据库, 并从表 ORDERS 和 CUSTOMER 中用一个内部连接条件组合记录. 查询结果中使用的 CUSTOMERS 表中的 CUST_ID, COMPANY 和 COUNTRY 字段, orders 表中的ORDER_ID 字段都在 SELECT-SQL 语句中指定.
SELECT Customer.cust_id, Customer.company, Customer.country,;
 Orders.order_id;
 FROM testdata!orders INNER JOIN testdata!customer ;
 ON Orders.cust_id = Customer.cust_id
该查询从两个表中获取并组合与连接条件匹配记录. 条件指定了两表中的cust_id字段必须具有相同的值.
内部连接只获取与连接条件匹配的记录.
一个内部连接典型的回答一个基本的问题. 在该查询中, 该问题是各订单的客户是谁? 如果一个客户在数据库中有一条记录但没有下过订单, 该记录不会出现在结果集中,因为它不是匹配连接条件的结果集中的一部份.
可以指定筛, 排序, 分组或其它各种查询选项来改变查询结果.

用嵌套的连接(Nested Join)选择记录

文件: SAMPLES\DATA\NESTED.QPR
Solution 项目中的查询 NESTED , 从 customer, orders 和 orditems 表中用两个内连接组合信息. 一个连接条件在customer 和 orders之间; 另一个是在orders 和 orditems 间. 结果记录中的 CUSTOMERS 表中的 cust_id 和 company 字段, orders 表中的ORDER_ID, orditems 表中的 line_no 字段都在SELECT-SQL 语句中指定.
SELECT Customer.cust_id, Customer.company, Orders.order_id, Orditems.line_no;
 FROM testdata!customer INNER JOIN testdata!orders;
 INNER JOIN testdata!orditems ;
 ON Orders.order_id = Orditems.order_id ;
 ON Customer.cust_id = Orders.cust_id
orders 和 orditems 间的连接首先被求值并产生一个符合连接条件的记录子集. 该记录子集与customer 表中的记录匹配并产生一个最终的结果集.
可以指定筛, 排序, 分组或其它各种查询选项来改变查询结果.

Visual FoxPro 5.0 中的新的 FROM/JOIN 语法

在 version 5.0 以前的版本中, 连接条件在 WHERE 子句中指定. 这容易造成了一些混乱, 特别对于首先学习 FoxPro 的 SQL 的人, 因为WHERE 子句也是放置筛选条件的地方.
虽然 VFP 5.0 仍然支持在 WHERE 子句中指定连接条件(向后兼容, 还记得这个词吗?), 保证所有新编写的查询使用新的扩展了的 FROM 子句是一个好主意, 保留 WHERE 子句给他早期的筛选角色. 要利用外部连接, 你需要使用新的语法.
新的语法在 FROM 子句中. 到目前为止, FROM 子句简单地包括逗号分隔的表的列表, 和必需的别名, 在 FROM 关键字的后面. 所有的表将被打开, 然后是 WHERE 子句中的连接和筛选. 在 FoxPro 3.0b及以前, 最小的两表 SQL 语句如下:
SELECT <field list> FROM table1,table2 ;
  WHERE table1.field = table2.Field
如果你需要一个卡氏积( table1 中的每一条记录匹配table2中的每一条记录) 或你不在乎结果用尽磁盘空间, 你可以去掉 WHERE 子句.
以下是等价的使用新语法的最小的 SQL 语句:
SELECT <field list> FROM table1 JOIN table2 ;
  ON table1.field = table2.field
要产生卡氏积你可以忽略 ON 子句:
SELECT <field list> FROM table1 JOIN table2
这样在通常情况下(当你注意到卡氏积时) 当你查询多个表时, 有一个 FROM 关键字, 第一个表后的每一个表一个 JOIN 关键字(FROM 关键字后面), 和一个 ON 关键字和各 JOIN 关键字的连接表达式.
一些连接类型说明符是可选的, 如表 4 所示.
表 4. JOIN 关键字.
Keyword
JOIN 内连接
RIGHT JOIN 右外连接
LEFT JOIN 左外连接
FULL JOIN 完全外连接
SQL 语句的 FROM 子句语法如下:
FROM [数据库名!]表名[本地别名]
       [[INNER | LEFT [OUTER] | RIGHT [OUTER] | FULL [OUTER] JOIN]
[, [数据库名!]表名[本地别名]
       [[INNER | LEFT [OUTER] | RIGHT [OUTER] | FULL [OUTER] JOIN]
          …]
       [ON [数据库名!]表名[本地别名] .字段名 =
       [数据库名!]表名[本地别名] .字段名]

书写结构清晰的 SELECT 语句

好象是为了把事情搞糟一样, 查询设计器使用违反直觉的结构, 根据 Microsoft 的说法, 是遵循 ANSI SQL ‘92 标准和 ODBC 产品查询格式要求. 我的劝告是在以前学习编写 SQL 语句时已经过份依赖查询设计器, 但一但掌握它后, 会因为手写代码的查询更为灵活而放弃使用查询设计器. 即使目前一些查询设计器的问题可以在产品版本中得到及时解决, 在你学习新语法的运作时,手工编写一些查询是会有好处的.
以下是一些简单的(如果不太精确)你可以在查询设计器中生成的三表查询结构示例:
SELECT table1.FieldName, ;
      table2.FieldName, ;
      table3.FieldName, ;
   FROM table3 ;
   JOIN table2 ;
      JOIN table1 ;
      ON table1.FieldName = table2.FieldName ;
   ON table3.FieldName = table2.FieldName
我将作为 ANSI ‘92 或 “嵌套” 结构提到这一点.
在这一点上, 另一个可选择的一贯的和正确的结构是一个我称为 “sequential” 的 FROM 子句. 我要在这里提起 Chin Bae 在我之前已经这样做了, 并发现这个结构和(就象你看到的一样)它的更为直觉之处. Chin 荐议使用以下语法来写:
FROM …
   [数据库名!]表名[本地别名]
      [[INNER|LEFT [OUTER]|RIGHT [OUTER]|FULL [OUTER] JOIN
         数据库名!]表名[本地别名]
            ON [数据库名!]表名[本地别名].字段名 =
         [数据库名!]表名[本地别名].字段名] …]
   [, [数据库名!]表名[本地别名]
      [[INNER|LEFT [OUTER]|RIGHT [OUTER]|FULL [OUTER] JOIN
         [数据库名!]表名[本地别名]
            ON [数据库名!]表名[本地别名].字段名 =
               [数据库名!]表名[本地别名].字段名]
                            …] …]
在转换到实际使用时, 用 “伪代码” 示例更为清晰:
SELECT table1.FieldName, ;
      table2.FieldName, ;
      table3.FieldName, ;
   FROM table1 ;
      JOIN table2 ;
         ON table2.FieldName = table1.FieldName ;
      JOIN table3 ;
         ON table3.FieldName = table2.FieldName
当第一次使用这种新语法时, 我认为该结构比”嵌套”结构更为直观. 我鼓励使用首先使用 “sequential” 结构, 一但适应了它的结构后再采用 “标准的” ANSI ‘92 结构.
在创建 FROM 子句和连接条件时请记住以下要点:
JOIN 关键字涉及到 右外连接和左外连接. 右外部连接将返回 JOIN 关键字的右边表中的所有记录, 左外连接将返回 JOIN 关键字左边表中的所有记录.
示例一.
问题:
仓库AA.DBF
字段 CK  MC
     111 食品仓
     112 饮品仓
     113 水果仓
销售表BB。DBF
字段 CK   WZDM    WZMC    XIAO_SL XIAO_DANJ-DANJ XIAO_JINE
     111  111001    方便面     2.00     3.00              6.00
     111  111001    面包       1.00     1.00              1.00
     112  112001    牛奶       1       2.00               2.00
我想按AA.DBF进行统计,虽113水果仓在BB.DBF中没有记录,可我想再统计的表中仍有记录,即统计后的表CC.DBF:
CK  T_XIAO_SL   T_XIAO_JINE
111  3.00          7.00
112  1.00          2.00
113  0             0
SELECT AA.CK,;
       NVL(SUM(BB.XIAO_SL),0) AS T_XIAO_SL,;
       NVL(SUM(BB.XIAO_JINE),0) AS T_XIAO_JINE ;
       FROM  AA ;
       FULL OUTER JOIN BB ;
       ON AA.CK = BB.CK ;
       GROUP BY AA.CK
 » 本站地址:http://www.gomoth.com

标签: