Mar*_*tin 3 postgresql syntax subquery
我有一个相对复杂的查询,其子查询获取一个数组,如下所示:
...
ARRAY(SELECT category_id FROM category_schedule_con con
WHERE s.id = con.schedule_id ORDER BY category_id) AS cats,
...
Run Code Online (Sandbox Code Playgroud)
并希望在以后的 WHERE 条件中使用数组“cats”,例如
...
WHERE 4 = ANY(cats)
...
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为它指出“cats”列不存在。c/p'ing 子查询到ANY子句中会产生预期的结果。
根据SQL 标准(Postgres 实现的)中的定义,您可以在ORDER BYor 中引用输出列GROUP BY,但不能在WHEREorHAVING子句中引用。手册:
输出列的名称可用于在
ORDER BYandGROUP BY子句中引用列的值,但不能在WHEREorHAVING子句中引用 ;在那里你必须写出表达式。
有关的:
显然,您的子查询是列表中的相关子查询表达式(由于过度简化而隐藏在问题中)。SELECT
为了避免在WHERE子句中重复冗长/昂贵的表达式,您可以在FROM列表中使用子查询。SELECT列表中的相关子查询不能被子WHERE句中的别名引用,它只是一个像任何其他列一样的输出列。
很有可能,您的查询效率会更高......
应用上述内容,此查询将起作用:
SELECT s.*, con.cats
FROM some_table s -- guessing the missing query
JOIN (
SELECT schedule_id
, array_agg(category_id ORDER BY category_id) AS cats
FROM category_schedule_con
GROUP BY 1
) con ON con.schedule_id = s.id
WHERE 3 = ANY(cats);
Run Code Online (Sandbox Code Playgroud)
现在您可以cats在WHERE子句中引用列别名。但是由于多种原因,这个查询对于大表来说效率非常低。最重要的是,谓词不是sargable。
使用GROUP BY和聚集函数array_agg(),而不是ARRAY构造函数,因为我们是生产多在一个单一的查询数组。
您ORDER BY几乎可以将子句应用于任何聚合函数,但排序的子查询通常性能更好:
SELECT s.*, con.cats
FROM some_table s
JOIN (
SELECT id, array_agg(category_id) AS cats
FROM (
SELECT schedule_id AS id, category_id -- alias id for convenience
FROM category_schedule_con
ORDER BY 1, 2 -- to get ordered list per schedule_id
) con
GROUP BY 1
) con USING (id)
WHERE 3 = ANY(cats);
Run Code Online (Sandbox Code Playgroud)
更重要的是,将谓词(WHERE条件)下拉到子查询中,以便可以使用索引并提前排除不相关的行。大表要快得多:
SELECT s.*, con.cats
FROM some_table s
JOIN (
SELECT id, array_agg(category_id) AS cats
FROM (
SELECT schedule_id AS id, category_id
FROM category_schedule_con c
WHERE EXISTS (
SELECT 1 FROM category_schedule_con
WHERE schedule_id = c.schedule_id
AND category_id = 3
)
ORDER BY 1, 2
) con
GROUP BY 1
) con USING (id);
Run Code Online (Sandbox Code Playgroud)
根据数据分布,LATERAL连接(需要 Postgres 9.3+)可能更有效:
SELECT s.*, con.cats
FROM some_table s
, LATERAL (
SELECT ARRAY (
SELECT category_id
FROM category_schedule_con
WHERE schedule_id = s.id
ORDER BY 1
) AS cats
) con
WHERE EXISTS (
SELECT 1 FROM category_schedule_con
WHERE schedule_id = s.id
AND category_id = 3
);
Run Code Online (Sandbox Code Playgroud)
关于LATERAL:
但它应该是最快的,以反转的逻辑:通过测距启动schedule_id有category_id = 3,自联接category_schedule_con和聚集加入到其他表之前:
SELECT s.*, con.cats
FROM (
SELECT id, array_agg(c.category_id) AS cats
FROM (
SELECT schedule_id AS id
FROM category_schedule_con
WHERE category_id = 3
) x
JOIN category_schedule_con c USING (id)
GROUP BY id
) con
JOIN some_table s USING (id);
Run Code Online (Sandbox Code Playgroud)
确保有一个多列索引,如:
CREATE INDEX category_schedule_con_foo_idx ON category_schedule_con
(schedule_id, category_id);
Run Code Online (Sandbox Code Playgroud)
对于最后一个查询,我们需要列的倒序:
CREATE INDEX category_schedule_con_bar_idx ON category_schedule_con
(category_id, schedule_id);
Run Code Online (Sandbox Code Playgroud)
另一个有两列的再次切换回来。在只有两个指标(category_id),并(schedule_id)就工作速度快,太;
两列上的 PK 或 UNIQUE 约束也适用。