资源描述
Visual FoxPro 中得 SQL Select 查询
基本得 SQL Select 2
From 子句 2
Select 子句 2
SQL 与用户自定义函数 3
Select 子句中得关键字与其它子句 3
Select 子句中得通配符 3
字段别名(AS) 4
用 DISTINCT 消去重复得记录 4
用 WHERE 子句说明选择记录得条件 4
选择条件 4
连接条件 4
SQL 与宏 4
SQL 与字母大小写 5
SQL 与索引 5
用 Order By 子句对结果排序 5
列函数 6
SQL 得列函数 6
列函数中得 DISTINCT 7
列函数得嵌套 7
用 GROUP BY 求分组小计 9
GROUP BY 与 DISTINCT 之间得选择 9
HAVING 子句得使用 10
用 TOP 子句选择一定数量或一定百分比得记录 13
查询结果得输出定向 14
复杂查询 15
子查询 15
带子查询得 IN 谓词与量词 15
ANY|SOME 16
ALL 16
EXISTS 16
自身连接 17
合并 18
示例一、快速排名次 19
示例二、选择重复得记录 20
外连接 21
什么就是外连接? 21
用右外连接(Right Outer Join)选择记录 21
用左外连接(Left Outer Join)选择记录 22
用完全外连接(Full Outer Join)选择记录 23
用内连接加外连接选择记录 24
用内连接(Inner Join)选择记录 24
用嵌套得连接(Nested Join)选择记录 25
Visual FoxPro 5、0 中得新得 FROM/JOIN 语法 26
为了更加清晰 27
用 SQL Select 生成视图 28
注意事项 29
基本得 SQL Select
From 子句
在 FROM 子句中指定要查询得表,若指定得表未打开,FoxPro 将自动打开指定得表,但在查询完成后,FoxPro并不自动关闭它所打开得表,若要在查询完成后关闭表,则必须以手动方式关闭它们。若在执行 SQL SELECT 时,FROM 子句中指定得表已经打开,则 FoxPro 以 USE …AGAIN 得方式重新打开此表。
Select 子句
SELECT 子句中指定要查询得结果字段,这些结果可以就是以下基本元素:
通配符 *
表中得字段名
FoxPro 函数
SELECT 中可用得列函数
用户自定义函数
常数(常用于作为占位符)
以上各元素在SELECT子句中以逗号分开,例如:
SELECT 销售地区,销售部门,业务员 ;
FROM 销售表
若 SELECT 子句中得字段名就是唯一得,则不必在字段名前加别名修饰,SQL 能自动找到这些字段。若字段名就是非唯一得(即同一字段名出现在 FROM 子句中得两个或两个以上表中),FoxPro 返回以下错误信息:
<字段>|<变量> 不唯一,必须加以限定
要纠正这一错误,用字段所在表得别名修饰该字段。
若在 SELECT 子句中加入了 FoxPro 函数,则意味着该语句不具备可移植性。
SQL 与用户自定义函数
FoxPro 支持在 Select 子句中使用用户定义函数。但就是由于在其它系统中不支持用户定义函数。因此在开发可能移植得系统时不要使用用户自定义函数。
Select 子句中得关键字与其它子句
Select 子句中得通配符
1、*(星号)
只能使用在字段串列参数中。代表来源数据库中所有得字段。如果 select 就是在二个以上得数据库中进行查询得,则要在号码前加上数据库别名。
* 就是 SQL 中得三个通配符之一(其它两个就是 ? 与 _ ,将在以后讨论),* 只能出现在 Select 子句中,通配符前可以用别名修饰,在此情况下表示要查询指定工作区中得全部字段、同时还可以给出其它字段名、如:
Select销售表、*,客户表、客户代码, 客户表、客户名称 ;
FROM 销售表, 客户表 ;
WHERE销售表、客户代码 =客户表、客户代码
2、%(百分号)
只能使用在 Where 参数中,代表一连串任意字符类似于 DOS 下得 IT*、PRG 中得 “*”。通常与运算符 LIKE 搭配使用:
SELECT 客户名 FROM客户表 WHERE客户名 LIKE “成都%公司”
以上查询选择所有公司名以”成都”开始,以”公司”结尾得客户,如:成都东方公司、成都西部发展公司等。
3、_(下划线)
也只能使用在 where 参数中,代表一个任意字符。与DOS 下得 DIR IT??、PRG中得”?”一样。例如 _a% 表示第二个字符为 a 得字串。通常与 LIKE 搭配使用:
SELECT 客户名 FROM客户表 WHERE客户名 LIKE “成都_公司”
以上查询选择类似于”成都分公司”, ”成都总公司”这样得公司名字。
另一种情况就是文字数据中正好包含有星号、百分号、下划线(例如:软件部__业务组),此时可使用关键字 ESCAPE 告诉 SQL 哪一个字符就是常数字符而非通配符。例如:
select company from customer
where company like"%\_%" escape "\"
此时 escape 所指定字符(即\)后得那一个字段(此处为下划线_)将被视为常数字符而非通配符。
字段别名(AS)
FoxPro 提供了给 Select 子句中得字段起别名得方法(这种方法不同于 ANSI/ISO 标准):即可用 AS 子句给出 Select 子句中字段得别名,在多当需要直接浏览查询结果或查询结果中含有计算列时,该方法很有用处、例如:
SELECT “物资部” AS 部门,SUM(工资) AS 工资汇总 ;
FROM 工资表 ;
WHERE 部门编号 = “0013”
用 DISTINCT 消去重复得记录
SELECT 中得 DISTINCT 关键字,可以消去查询结果中得重复行。例如要字段出所有购买了商得客户,可以用以下 Select 语句:
SELECT DISTINCT客户名称 ;
FROM 销售细节表
注意,这里消去得重复记录就是指 SELECT 子句中得所有字段值均相等得记录、
当 SQL 遇到 DISTINCT 关键字时,即建立一个中间表。然后以 SELECT 子句中得所有字段建立一个唯一索引,然后将索引用于中间表,并把索引中得记录放入查询结果中。这样就消去了重复记录,但就是当 SELECT 子句中得字段很多时,这一过程会很慢。
用 WHERE 子句说明选择记录得条件
通常查询并不希望取出所有记录,而只就是希望查出满足一定条件得记录,要做到这一点可以使用 WHERE 子句。WHERE 子句把满足条件得记录作为查询结果。
选择条件
在 WHERE 子句中可以包含多个选择条件,各条件间用 AND 或 OR 连接而构成较长得逻辑表达式。
连接条件
当一个查询涉及到多个表时,需要一个特殊类型得 WHERE 条件,称为连接条件,连接条件建立了 FROM 子句中得两表间得一对一或一对多得联系。
SQL 与宏
SELECT 子句中可以方便快捷地处理宏,在 SELECT 语句中,宏替换只能替换一次,在以下查询之前,需要由用户给出表名与相应得表达式
Select * ;
FROM (UserTable) ;
Where 公司代码 = &Company_code
宏还可以用在 LIKE 得模式匹配串:
Select Company,CompanyId,SaleName,SaleId ;
From Company,SalesMan ;
Where Company、SaleId = SalesMan、SaleId ;
AND Description LIKE "%TCL%"
以上查询中,用户输入一字符串以便在备注字段(Description)中作匹配查找、查询求出得就是包含该字符串得所有公司得住处
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 idx 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
在查询
展开阅读全文