标量全查询是在括号中的全查询,该查询返回的一行只包含一个列值。标量全查询对从数据库中检索数据值供表达式使用是很有用的。
- 下列示例列出其薪水超过全部雇员平均薪水的雇员的姓名:
SELECT LASTNAME, FIRSTNME
FROM EMPLOYEE
WHERE SALARY > (SELECT AVG(SALARY)
FROM EMPLOYEE)
- 下列示例在两个不同的表中查寻雇员的平均薪水:
SELECT AVG(SALARY) AS "Average_Employee",
(SELECT AVG(SALARY) AS "Average_Staff" FROM STAFF)
FROM EMPLOYEE
有时可能需要将一些值从一种数据类型转换成另一种数据类型,例如,从数字值转换成字符串。要将一个值转换成另一个不同的类型,使用 CAST 说明。
转换说明的另一个可能用途是截断很长的字符串。在 EMP_RESUME 表中,RESUME 列是 CLOB(5K)。您可能只想显示包含应聘者个人信息的前 370 个字符。要从 EMP_RESUME 表中显示简历的 ASCII 格式的前 370 个字符,发出下列请求:
SELECT EMPNO, CAST(RESUME AS VARCHAR(370))
FROM EMP_RESUME
WHERE RESUME_FORMAT = 'ascii'
会发出一个警告,通知您超过 370 个字符的值被截断。
可将空值转换为更便于在查询中进行处理的其他数据类型。 公共表表达式是一个为此目的使用转换的示例。
可在 SQL 语句中使用 CASE 表达式以便于处理表的数据表示。这提供了一种功能强大的条件表达式能力,在概念上与某些程序设计语言中的 CASE 语句类似。
- 要从 ORG 表中的 DEPTNAME 列将部门编号更改为有意义的词,输入下列查询:
SELECT DEPTNAME,
CASE DEPTNUMB
WHEN 10 THEN 'Marketing'
WHEN 15 THEN 'Research'
WHEN 20 THEN 'Development'
WHEN 38 THEN 'Accounting'
ELSE 'Sales'
END AS FUNCTION
FROM ORG
结果为:
DEPTNAME FUNCTION
-------------- -----------
Head Office Marketing
New England Research
Mid Atlantic Development
South Atlantic Accounting
Great Lakes Sales
Plains Sales
Pacific Sales
Mountain Sales
- 可使用 CASE 表达式来防止出现异常情况,如被零除等。在下列示例中,如果雇员没有奖金或佣金报酬,则语句条件通过避免除法运算来防止出错:
SELECT LASTNAME, WORKDEPT FROM EMPLOYEE
WHERE(CASE
WHEN BONUS+COMM=0 THEN NULL
ELSE SALARY/(BONUS+COMM)
END ) > 10
- 可在单个语句中使用 CASE 表达式,根据一个列中值的子集的总和与该列中所有值的总和的比来产生一个比率。使用 CASE 表达式的语句只需要传送数据一次。在没有 CASE 表达式的情况下,执行同样的计算至少需要传送两次。
下列示例使用 CASE 表达式计算部门 20 的薪水之和与全部薪水总额的比率:
SELECT CAST(CAST (SUM(CASE
WHEN DEPT = 20 THEN SALARY
ELSE 0
END) AS DECIMAL(7,2))/
SUM(SALARY) AS DECIMAL (3,2))
FROM STAFF
结果为 0.11。注意:CAST 函数确保结果的精度得到保持。
- 可使用 CASE 表达式来计算简单的函数,而不必调用函数本身,调用函数将需要额外开销。例如:
CASE
WHEN X<0 THEN -1
WHEN X=0 THEN 0
WHEN X>0 THEN 1
END
此表达式与 SYSFUN 模式中 SIGN 用户定义函数有相同的结果。
如果只需要单个查询的视图定义,可使用表表达式。
表表达式是临时的,只在 SQL 语句的使用期限内有效;表表达式不能共享,但它们比视图更灵活。任何授权的用户都可共享视图定义。
本节描述如何在查询中使用公共表表达式和嵌套表表达式。
嵌套表表达式是一个临时视图,其中的定义被嵌套(直接定义)在主查询的 FROM子 句中。
下列查询使用嵌套表表达式来寻找那些教育级别超过 16 的雇员的平均总收入、教育级别以及雇用年份:
SELECT EDLEVEL, HIREYEAR, DECIMAL(AVG(TOTAL_PAY), 7,2)
FROM (SELECT YEAR(HIREDATE) AS HIREYEAR, EDLEVEL,
SALARY+BONUS+COMM AS TOTAL_PAY
FROM EMPLOYEE
WHERE EDLEVEL > 16 ) AS PAY_LEVEL
GROUP BY EDLEVEL, HIREYEAR
ORDER BY EDLEVEL, HIREYEAR
结果如下:
EDLEVEL HIREYEAR 3
------- ----------- ---------
17 1967 28850.00
17 1973 23547.00
17 1977 24430.00
17 1979 25896.50
18 1965 57970.00
18 1968 32827.00
18 1973 45350.00
18 1976 31294.00
19 1958 51120.00
20 1975 42110.00
此查询使用嵌套表表达式,首先从 HIREDATE 列中抽取雇用年份,以便可以在后续的 GROUP BY 子句中使用该雇用年份。您可能不想将此查询作为视图来创建,因为您打算用不同的 EDLEVEL 值来执行相似查询。
此示例中使用了标量内部函数 DECIMAL。 DECIMAL 返回数字或字符串的十进制表示。有关函数的更多详情,参考 SQL Reference。
公共表表达式是在全查询的开头使用 WITH 关键字定义的命名结果表。公共表表达式是您创建以在复杂查询之中使用的表表达式。在查询的开头使用 WITH 子句定义并命名公共表表达式。对公共表表达式的重复引用使用同一个结果集。相比之下,如果使用嵌套表表达式或视图,则每次都将重新生成结果集,其结果可能各不相同。
下列示例列出公司中教育级别大于 16、平均工资比那些同时雇用的且有同样教育级别的人低的所有人。在该查询后面会更详细地描述查询的各个部分。
(1)
WITH
PAYLEVEL AS
(SELECT EMPNO, YEAR(HIREDATE) AS HIREYEAR, EDLEVEL,
SALARY+BONUS+COMM AS TOTAL_PAY
FROM EMPLOYEE
WHERE EDLEVEL > 16),
(2) PAYBYED (EDUC_LEVEL, YEAR_OF_HIRE, AVG_TOTAL_PAY) AS
(SELECT EDLEVEL, HIREYEAR, AVG(TOTAL_PAY)
FROM PAYLEVEL
GROUP BY EDLEVEL, HIREYEAR)
(3)
SELECT EMPNO, EDLEVEL, YEAR_OF_HIRE, TOTAL_PAY, DECIMAL(AVG_TOTAL_PAY,7,2)
FROM PAYLEVEL, PAYBYED
WHERE EDLEVEL=EDUC_LEVEL
AND HIREYEAR = YEAR_OF_HIRE
AND TOTAL_PAY < AVG_TOTAL_PAY
- (1)
- 这是名为 PAYLEVEL 的公共表表达式。此结果表包括雇用某个人的年份、该雇员的总收入以及他(或她)的教育级别。只包括雇员的教育级别大于 16 的那些行。
- (2)
- 这是名为 PAYBYED(或 PAY by education)的公共表表达式。该表达式使用在前一个公共表表达式中创建的 PAYLEVEL 表来确定每个教育级别同一年雇用的雇员的教育级别、雇用年份以及平均收入。此表返回的列被赋予的名称与选择列表中所使用的列名不同(如 EDUC_LEVEL)。这会生成命名为 PAYBYED 的结果集,与嵌套表表达式示例中产生的结果相同。
- (3)
- 最后,我们获得能产生期望结果的实际查询。连接这两个表(PAYLEVEL,PAYBYED)以确定总收入比同年雇用的人的平均收入低的那些人。注意:PAYBYED 是以 PAYLEVEL 为基础。所以在完整语句中有效地存取了 PAYLEVEL 两次。两次都使用同一组行来计算查询。
最终结果如下:
EMPNO EDLEVEL YEAR_OF_HIRE TOTAL_PAY 5
------ ------- ------------ ------------- ---------
000210 17 1979 20132.00 25896.50
相关名是用于识别一个对象的多种用途的标识符。可在查询的 FROM 子句中和 UPDATE 或 DELETE 语句的第一个子句中定义相关名。相关名可与表、视图或嵌套表表达式关联,但只限于定义相关名的上下文中。
例如,子句 FROM STAFF S、ORG O 分别指定 S 和 O 作为 STAFF 和 ORG 的相关名。
SELECT NAME, DEPTNAME
FROM STAFF S, ORG O
WHERE O.MANAGER = S.ID
一旦定义了相关名,则只可以使用相关名来限定对象。例如,上例中如果写成 ORG.MANAGER=STAFF.ID 的话,则该语句就会失效。
也可以使用相关名作为表示数据库对象的简称。只输入 S 比输入 STAFF 更容易。
通过使用相关名,可复制对象。这在需要将表中各项与自己本身作比较时很有用。在下列示例中,EMPLOYEE 表与它自己的另一个实例比较以寻找所有雇员的经理。该示例显示非设计员的雇员的姓名、这些雇员的经理的姓名以及部门编号。
SELECT E2.FIRSTNME, E2.LASTNAME,
E2.JOB, E1.FIRSTNME, E1.LASTNAME, E1.WORKDEPT
FROM EMPLOYEE E1, EMPLOYEE E2
WHERE E1.WORKDEPT = E2.WORKDEPT
AND E1.JOB = 'MANAGER'
AND E2.JOB <> 'MANAGER'
AND E2.JOB <> 'DESIGNER'
此语句产生下列结果:
FIRSTNME LASTNAME JOB FIRSTNME LASTNAME WORKDEPT
------------ --------------- -------- ------------ --------------- --------
DOLORES QUINTANA ANALYST SALLY KWAN C01
HEATHER NICHOLLS ANALYST SALLY KWAN C01
JAMES JEFFERSON CLERK EVA PULASKI D21
MARIA PEREZ CLERK EVA PULASKI D21
SYBIL JOHNSON CLERK EVA PULASKI D21
DANIEL SMITH CLERK EVA PULASKI D21
SALVATORE MARINO CLERK EVA PULASKI D21
ETHEL SCHNEIDER OPERATOR EILEEN HENDERSON E11
MAUDE SETRIGHT OPERATOR EILEEN HENDERSON E11
PHILIP SMITH OPERATOR EILEEN HENDERSON E11
JOHN PARKER OPERATOR EILEEN HENDERSON E11
RAMLAL MEHTA FIELDREP THEODORE SPENSER E21
JASON GOUNOT FIELDREP THEODORE SPENSER E21
WING LEE FIELDREP THEODORE SPENSER E21
允许引用先前提到的任何表的子查询称为相关子查询。我们也说该子查询具有对主查询中表的相关引用。
下列示例是一个不相关子查询,该子查询列出部门 'A00' 中薪水超过该部门平均薪水的雇员的雇员编号和姓名:
SELECT EMPNO, LASTNAME
FROM EMPLOYEE
WHERE WORKDEPT = 'A00'
AND SALARY > (SELECT AVG(SALARY)
FROM EMPLOYEE
WHERE WORKDEPT = 'A00')
如果想要知道每个部门的平均薪水,则需要对每个部门计算一次子查询。对在外层查询中标识的表的每一行,各使用一次 SQL 的相关功能(该能力允许您编写重复执行的子查询),就可做到这一点。此类型的相关子查询用来计算外层表的每一行的某个特性,该特性是在子查询中计算谓词所需要的。
此示例显示薪水高于部门平均薪水的所有雇员:
SELECT E1.EMPNO, E1.LASTNAME, E1.WORKDEPT
FROM EMPLOYEE E1
WHERE SALARY > (SELECT AVG(SALARY)
FROM EMPLOYEE E2
WHERE E2.WORKDEPT = E1.WORKDEPT)
ORDER BY E1.WORKDEPT
在此查询中,对每个部门计算一次子查询。结果为:
EMPNO LASTNAME WORKDEPT
------ --------------- --------
000010 HAAS A00
000110 LUCCHESSI A00
000030 KWAN C01
000060 STERN D11
000220 LUTZ D11
000200 BROWN D11
000170 YOSHIMURA D11
000150 ADAMSON D11
000070 PULASKI D21
000270 PEREZ D21
000240 MARINO D21
000090 HENDERSON E11
000280 SCHNEIDER E11
000100 SPENSER E21
000340 GOUNOT E21
000330 LEE E21
要编写带有相关子查询的查询,使用与带有子查询的普通外部查询相同的基本格式。然而,在外部查询的 FROM 子句中,只是在表名后面放置一个相关名。于是子查询可能包含由该相关名限定的列引用。例如,如果 E1 是相关名,则 E1.WORKDEPT 表示外部查询中表的当前行的工作部门值。在外部查询中对表的每一行(概念上)重新计算子查询。
通过使用相关子查询,可以使系统为您工作并减少需要在应用程序中编写的代码量。
DB2 中允许非限定相关引用。例如,表 EMPLOYEE 有一个命名为 LASTNAME 的列,表 SALES 有一个命名为 SALES_PERSON 的列,但没有命名为 LASTNAME 的列。
SELECT LASTNAME, FIRSTNME, COMM
FROM EMPLOYEE
WHERE 3 > (SELECT AVG(SALES)
FROM SALES
WHERE LASTNAME = SALES_PERSON)
在此示例中,系统检查最内层的 FROM 子句,以获取 LASTNAME 列。如果未找到 LASTNAME 列,则系统检查次最内层的 FROM 子句(此情况下为外部 FROM 子句)。虽然不总是必要的,还是建议限定相关引用以改进查询的可读性并确保获取想要的结果。
想何时使用相关子查询?列函数的使用有时是一条线索。
假定您想要列出教育级别高于部门平均值的雇员。
首先,您必须确定选择列表项。问题为 "List the employees"。这隐含着来自 EMPLOYEE 表中的 EMPNO 应该足以唯一标识雇员。该问题也将 EDLEVEL 和雇员的部门 WORKDEPT 说明为条件。当问题未明确要求显示列时,在选择列表中包括这些列将会有助于说明解法。现在可构造查询的一部分:
SELECT LASTNAME, WORKDEPT, EDLEVEL
FROM EMPLOYEE
接着需要搜索条件(WHERE子句)。问题语句为 "...whose level of education is higher than the average for that employee's department"。这意味着对于表中每个雇员,必须计算该雇员所在部门的平均教育级别。此语句适合相关子查询的说明。正在对每行计算某个特性(当前雇员所在部门的平均教育级别)。 EMPLOYEE 表需要相关名:
SELECT LASTNAME, WORKDEPT, EDLEVEL
FROM EMPLOYEE E1
需要的子查询较简单。该子查询计算每个部门的平均教育级别。完整的 SQL 语句为:
SELECT LASTNAME, WORKDEPT, EDLEVEL
FROM EMPLOYEE E1
WHERE EDLEVEL > (SELECT AVG(EDLEVEL)
FROM EMPLOYEE E2
WHERE E2.WORKDEPT = E1.WORKDEPT)
结果为:
LASTNAME WORKDEPT EDLEVEL
--------------- -------- -------
HAAS A00 18
KWAN C01 20
PULASKI D21 16
HENDERSON E11 16
LUCCHESSI A00 19
PIANKA D11 17
SCOUTTEN D11 17
JONES D11 17
LUTZ D11 18
MARINO D21 17
JOHNSON D21 16
SCHNEIDER E11 17
MEHTA E21 16
GOUNOT E21 16
假定不列出雇员的部门编号,则应列出部门名称。需要的信息(DEPTNAME)在独立表(DEPARTMENT)中。定义相关变量的外层查询也可以是连接查询(参见从多个表中选择数据以了解详情)。
当在外层查询中使用连接时,列出要在 FROM 子句中连接的表,并将相关名放在这些表名的任何一个表名旁边。
要修改查询以列出部门名称而不是部门编号,在选择列表中用 DEPTNAME 替换 WORKDEPT。 FROM 子句现在也必须包括 DEPARTMENT 表,并且 WHERE 子句必须表示适当的连接条件。
以下是修改的查询:
SELECT LASTNAME, DEPTNAME, EDLEVEL
FROM EMPLOYEE E1, DEPARTMENT
WHERE E1.WORKDEPT = DEPARTMENT.DEPTNO
AND EDLEVEL > (SELECT AVG(EDLEVEL)
FROM EMPLOYEE E2
WHERE E2.WORKDEPT = E1.WORKDEPT)
上例显示,必须在包含相关子查询的某个查询的 FROM 子句中定义用于子查询中的相关名。然而,这种包含可能涉及若干层嵌套。
假定某些部门只有几个雇员,因此这些部门的平均教育级别可能是错误的。可以决定,为了使平均教育级别在用于比较雇员时是有意义的数字,一个部门中必须至少有 5 个雇员。因此现在必须列出教育级别高于雇员所在部门平均值的雇员,并只考虑至少有 5 个雇员的部门。
该问题暗含另一个子查询,因为对于外层查询中每个雇员来说,必须计算该雇员所在部门的雇员总数:
SELECT COUNT(*)
FROM EMPLOYEE E3
WHERE E3.WORKDEPT = E1.WORKDEPT
仅当计数大于或等于 5 时才计算平均值:
SELECT AVG(EDLEVEL)
FROM EMPLOYEE E2
WHERE E2.WORKDEPT = E1.WORKDEPT
AND 5 <= (SELECT COUNT(*)
FROM EMPLOYEE E3
WHERE E3.WORKDEPT = E1.WORKDEPT)
最后,只包括其教育级别高于部门平均值的那些雇员:
SELECT LASTNAME, DEPTNAME, EDLEVEL
FROM EMPLOYEE E1, DEPARTMENT
WHERE E1.WORKDEPT = DEPARTMENT.DEPTNO
AND EDLEVEL >
(SELECT AVG(EDLEVEL)
FROM EMPLOYEE E2
WHERE E2.WORKDEPT = E1.WORKDEPT
AND 5 <=
(SELECT COUNT(*)
FROM EMPLOYEE E3
WHERE E3.WORKDEPT = E1.WORKDEPT))
此语句产生下列结果:
LASTNAME DEPTNAME EDLEVEL
--------------- ----------------------------- -------
PIANKA MANUFACTURING SYSTEMS 17
LUTZ MANUFACTURING SYSTEMS 18
JONES MANUFACTURING SYSTEMS 17
SCOUTTEN MANUFACTURING SYSTEMS 17
PULASKI ADMINISTRATION SYSTEMS 16
JOHNSON ADMINISTRATION SYSTEMS 16
MARINO ADMINISTRATION SYSTEMS 17
HENDERSON OPERATIONS 16
SCHNEIDER OPERATIONS 17