使用SQL对同一行数据进行列间比较很简单,只需要在WHERE子句里写上比较条件就可以了。但是,对不同行数据进行列间的比较却没那么简单。然而,这并不是说我们不能用SQL进行行与行之间的比较。
使用SQL进行行间比较时,发挥主要作用的技术是关联子查询,特别是与自连接结合的“关联子查询”。
year(年份) | sale(年营业额:亿日元) |
---|---|
1990 | 50 |
1991 | 51 |
1992 | 52 |
1993 | 52 |
1994 | 50 |
1995 | 50 |
1996 | 49 |
1997 | 55 |
-- 求出是增长了还是减少了,抑或是维持现状(1):使用关联子查询
SELECT S1.year,S1.sale,
CASE WHEN S1.sale < (SELECT sale FROM Sales AS S2 WHERE S2.year = S1.year - 1) THEN '↓'
WHEN S1.sale = (SELECT sale FROM Sales AS S2 WHERE S2.year = S1.year - 1) THEN '→'
WHEN S1.sale > (SELECT sale FROM Sales AS S2 WHERE S2.year = S1.year - 1) THEN '↑'
ELSE '--' END AS var
FROM Sales AS S1
ORDER BY S1.year;
注意:在关联子查询中,对于外部查询返回的每一行数据,内部查询都要执行一次。另外,在关联子查询中是信息流是双向的。外部查询的每行数据传递一个值给子查询,然后子查询为每一行数据执行一次并返回它的记录。然后,外部查询根据返回的记录做出决策。
-- 求出是增长了还是减少了,抑或是维持现状(2):使用自连接查询(最早的年份不会出现在结果里)
SELECT S1.year,S1.sale,
CASE WHEN S1.sale > S2.sale THEN '↑'
WHEN S1.sale = S2.sale THEN '→'
WHEN S1.sale < S2.sale THEN '↓'
ELSE '--' END AS var
FROM Sales AS S1,Sales AS S2
WHERE S2.year = S1.year - 1;
对某一年,“过去最临近的年份”需要满足下面两个条件
year(年份) | sale(年营业额) |
---|---|
1990 | 50 |
1992 | 50 |
1993 | 52 |
1994 | 55 |
1997 | 55 |
-- 查询与过去最邻近的年份营业额相同的年份
SELECT year,sale
FROM Sales2 S1
WHERE sale = (SELECT sale
FROM Sales S2
WHERE S2.year = (SELECT max(S3.year)
FROM Sales2 AS S3
WHERE S1.year > S3.year ))
ORDER BY year;
-- 查询与过去最邻近的年份营业额相同的年份:同时使用自连接
SELECT S1.year AS year,S1.sale AS sale
FROM Sales2 AS S1,Sales2 AS S2
WHERE S1.sale = S2.sale AND S2.year =
(SELECT MAX(year) FROM Sales2 S3 WHERE S1.year > S3.year)
ORDER BY year;
注意:再次强调,不清楚请按照SQL内部执行流程在纸上来一遍(关联子查询是从外部开始,将每一条记录带入内部然后返回)
prc_date(处理日期) | prc_amt(处理金额) |
---|---|
2006-10-26 | 12 000 |
2006–10-28 | 2 500 |
2006–10-31 | -15 000 |
2006–11-03 | 34 000 |
2006–11-04 | -5 000 |
2006–11-06 | 7 200 |
2006–11-11 | 11 000 |
--使用窗口函数
SELECT prc_date,prc_amt, sum(prc_amt) over(order by prc_date) AS onhand_amt
FROM accounts;
---求累计值,关联子查询
SELECT a1.prc_date,(SELECT sum(a2.prc_amt)
FROM accounts a2 WHERE a2.prc_date < a1.prc_date) AS onhand_amt
FROM accounts as a1
ORDER BY a1.prc_date;
prc_date(处理日期) | prc_amt(处理金额) |
---|---|
2006-10-26 | 12 000 |
2006–10-28 | 2 500 |
2006–10-31 | -15 000 |
2006–11-03 | 34 000 |
2006–11-04 | -5 000 |
2006–11-06 | 7 200 |
2006–11-11 | 11 000 |
-- 求移动累计值(1):使用窗口函数
SELECT prc_date, prc_amt, SUM(prc_amt) OVER (ORDER BY prc_date ROWS 2 PRECEDING) AS onhand_amt
FROM Accounts;
-- 求移动累计值(2):不满3行的时间区间也输出
SELECT prc_date, A1.prc_amt,
( SELECT SUM(prc_amt) FROM Accounts A2
WHERE A1.prc_date >= A2.prc_date
AND (SELECT COUNT(*) FROM Accounts A3 WHERE A3.prc_date BETWEEN A2.prc_date AND A1.prc_date ) <= 3
) AS mvg_sum
FROM Accounts A1
-- 移动累计值(3):不满3行的区间按无效处理
SELECT prc_date, A1.prc_amt,
( SELECT SUM(prc_amt) FROM Accounts A2 WHERE A1.prc_date >= A2.prc_date
AND (SELECT COUNT(*) FROM Accounts A3 WHERE A3.prc_date BETWEEN A2.prc_date AND A1.prc_date ) <= 3
HAVING COUNT(*) =3
) AS mvg_sum -- 不满3 行数据的不显示
FROM Accounts A1
ORDER BY prc_date;
--这里不能直接将“<=3”改成“=3”,会使不满三行的数据完全不显示,而不是不显示SUM列
reserver(入住客人) | start_date(入住日期) | end_date(离店日期) |
---|---|---|
木村 | 2006-10-26 | 2006-10-27 |
荒木 | 2006-10-28 | 2006-10-31 |
堀 | 2006-10-31 | 2006-11-01 |
山本 | 2006-11-03 | 2006-11-04 |
内田 | 2006-11-03 | 2006-11-05 |
水谷 | 2006-11-06 | 2006-11-06 |
SELECT reserver, start_date, end_date
FROM Reservations R1
WHERE EXISTS
(SELECT *
FROM Reservations R2
WHERE R1.reserver <> R2.reserver -- 与自己以外的客人进行比较
AND ( R1.start_date BETWEEN R2.start_date AND R2.end_date-- 条件(1):自己的入住日期在他人的住宿期间内
OR R1.end_date BETWEEN R2.start_date AND R2.end_date)-- 条件(2):自己的离店日期在他人的住宿期间内
);
--关联子查询存在记录,就返回相应的记录
SELECT reserver, start_date, end_date
FROM Reservations R1
WHERE EXISTS
(SELECT *
FROM Reservations R2
WHERE R1.reserver <> R2.reserver -- 与自己以外的客人进行比较
AND ( R1.start_date BETWEEN R2.start_date AND R2.end_date-- 条件(1):自己的入住日期在他人的住宿期间内
OR R1.end_date BETWEEN R2.start_date AND R2.end_date)-- 条件(2):自己的离店日期在他人的住宿期间内
OR (R2.start_date BETWEEN R1.start_date AND R1.end_date
AND
R2.end_date BETWEEN R1.start_date AND R1.end_date)--条件(3):自己的住宿期间完全包含了别人的住宿期间
);
本节要点: