我想在我的数据库上做一个特殊的请求(PostgreSQL v9.4.5),但我没有设法做到这一点。
为了简单起见,假设我有下表 AvgTemperatures ,表示不同城市的不同平均温度,并根据不同的时间长度计算(以月计):
id | city | avg | months
----+-----------+------+--------
1 | New-York | 20 | 3 <--- average temperate over the last 3 months
2 | New-York | 19 | 6 <--- average temperate over the last 6 months
3 | New-York | 15 | 12 <--- etc
4 | New-York | 15 | 24
5 | Boston | 13 | 3
6 | Boston | 18 | 8
7 | Boston | 17 | 12
8 | Boston | 16 | 15
9 | Chicago | 12 | 2
10 | Chicago | 14 | 12
11 | Miami | 28 | 1
12 | Miami | 25 | 4
13 | Miami | 21 | 12
14 | Miami | 22 | 15
15 | Miami | 20 | 24
现在,想象一下,我想选择一个城市中至少有一个平均值超过19度的措施的所有行。在这种情况下,我想:
id | city | avg | months
----+-----------+------+--------
1 | New-York | 20 | 3
2 | New-York | 19 | 6
3 | New-York | 15 | 12
4 | New-York | 15 | 24
11 | Miami | 28 | 1
12 | Miami | 25 | 4
13 | Miami | 21 | 12
14 | Miami | 22 | 15
15 | Miami | 20 | 24
我可以这样做:
SELECT *
FROM AvgTemperatures
WHERE MIN(avg) OVER (PARTITION BY city) > 16
但是:
********** Erreur **********
ERROR: window functions not allowed in WHERE clause
更重要的是,我不能像以下那样使用GROUP BY
:
SELECT *
FROM AvtTemperatures
GROUP BY city
HAVING MIN(avg) > 16
因为我会因聚合而丢失信息(因为“SELECT *”因此查询无效)。
我很确定我可以使用OVER PARTITION BY
来解决这个问题,但我不知道怎么做。有人有想法吗?
答案 0 :(得分:5)
首先,它称为 all-at-once operation
“一次性操作”意味着所有表达式都相同 逻辑查询过程阶段在逻辑上同时进行评估。
很棒的章节对窗口函数的影响:
假设你有:
CREATE TABLE Test ( Id INT) ;
INSERT INTO Test VALUES ( 1001 ), ( 1002 ) ;
SELECT Id
FROM Test
WHERE Id = 1002
AND ROW_NUMBER() OVER(ORDER BY Id) = 1;
All-at-Once操作告诉我们在同一时间点逻辑评估这两个条件。因此,SQL Server可以 基于的任意顺序评估WHERE子句中的条件 估计执行计划。所以这里的主要问题是哪个条件 首先评估。
案例1:
If ( Id = 1002 ) is first, then if ( ROW_NUMBER() OVER(ORDER BY Id) = 1 )
结果:1002
案例2:
If ( ROW_NUMBER() OVER(ORDER BY Id) = 1 ), then check if ( Id = 1002 )
结果:空
所以我们有一个悖论。
此示例说明了为什么我们不能在WHERE子句中使用Window Functions。 您可以更多地考虑这个并找到Window Functions的原因 允许仅在 SELECT 和 ORDER BY 子句中使用!
为了得到你想要的东西你可以用Gordon answer中的CTE/subquery
包装窗口函数:
;WITH cte AS
(
SELECT t.*, MAX(AVG) OVER (PARTITION BY city) AS average
FROM avgTemperatures t
)
SELECT *
FROM cte
where average > 19
ORDER BY id;
的 LiveDemo
强>
输出:
╔═════╦══════════╦═════╦═════════╗
║ id ║ city ║ avg ║ months ║
╠═════╬══════════╬═════╬═════════╣
║ 1 ║ New-York ║ 20 ║ 3 ║
║ 2 ║ New-York ║ 19 ║ 6 ║
║ 3 ║ New-York ║ 15 ║ 12 ║
║ 4 ║ New-York ║ 15 ║ 24 ║
║ 11 ║ Miami ║ 28 ║ 1 ║
║ 12 ║ Miami ║ 25 ║ 4 ║
║ 13 ║ Miami ║ 21 ║ 12 ║
║ 14 ║ Miami ║ 22 ║ 15 ║
║ 15 ║ Miami ║ 20 ║ 24 ║
╚═════╩══════════╩═════╩═════════╝
答案 1 :(得分:2)
最简单的解决方案是使用bool_or
aggregate function
select id, city, avg, months
from avttemperatures
where city in (
select city
from avttemperatures
group by 1
having bool_or(avg > 19)
)
order by 2, 4
;
id | city | avg | months
----+----------+-----+--------
11 | Miami | 28 | 1
12 | Miami | 25 | 4
13 | Miami | 21 | 12
14 | Miami | 22 | 15
15 | Miami | 20 | 24
1 | New-York | 20 | 3
2 | New-York | 19 | 6
3 | New-York | 15 | 12
4 | New-York | 15 | 24
测试表:
create table avttemperatures (
id int, city text, avg int, months int
);
insert into avttemperatures (id, city, avg, months) values
( 1,'New-York',20,3),
( 2,'New-York',19,6),
( 3,'New-York',15,12),
( 4,'New-York',15,24),
( 5,'Boston',13,3),
( 6,'Boston',18,8),
( 7,'Boston',17,12),
( 8,'Boston',16,15),
( 9,'Chicago',12,2),
( 10,'Chicago',14,12),
( 11,'Miami',28,1),
( 12,'Miami',25,4),
( 13,'Miami',21,12),
( 14,'Miami',22,15),
( 15,'Miami',20,24);
答案 2 :(得分:2)
如果您只想知道是否存在至少一个,则无需汇总:
SELECT id, city, avg, months
FROM avgtemperatures t
WHERE EXISTS ( SELECT 42
FROM avgtemperatures x
WHERE x.city = t.city
AND x.avg > 19
)
ORDER BY city,months DESC
;
注意:avg
是列的错误名称。
答案 3 :(得分:1)
您需要将它包装在派生表中才能在where子句中使用:
select *
from (
SELECT t.*, MIN(avg) OVER (PARTITION BY city) as city_avg
FROM AvgTemperatures t
) x
WHERE city_avg > 16
答案 4 :(得分:1)
使用子查询获取最大值,然后使用where
:
select t.*
from (select t.*, max(avg) over (partition by city) as maxavg
from avgTemperatures t
) t
where maxavg > 19;
另一种方法是在where
子句中执行此操作:
select t.*
from avgTemperatures t
where t.city in (select t2.city from avgTemperatures t2 where t2.avg > 19);