需要在选择条件的地方使用列别名。 找到了可能的解决方案here。
假设我们有一对一的关系(用户对角色),我们希望获得如下结果:
SELECT u.name AS u_name, r.name AS r_name
FROM users AS u
INNER JOIN roles AS r
ON u.role_id = r.role_id
WHERE u.name = 'John'
我们有user.name
的相应idex(仅举例)。
如果此查询与EXPLAIN
一起运行,则会显示选择期间使用的所有索引(包括名称索引)。
现在,我们想在WHERE
子句中使用别名,基于提出的解决方案,我们可以重写查询:
SELECT * FROM (
SELECT u.name AS u_name, r.name AS r_name
FROM users AS u
INNER JOIN roles AS r
ON u.role_id = r.role_id
) AS temp
WHERE u_name = 'John'
如您所见,嵌套选择中没有WHERE
子句。使用EXPLAIN
运行此查询会得到相同的结果(只是承认,我不是分析'解释'的结果的专家,但仍然):
我对此结果感到有点困惑:确信至少会使用用户名索引。
Q1: postgres是否以这种方式使用索引?
Q2:是否存在性能问题?
答案 0 :(得分:5)
不需要子查询,因此可以展开/折叠。
以下查询将生成 flat 计划(且索引不相关)
\i tmp.sql
CREATE TABLE t
(a integer not null primary key
);
insert into t(a)
select v from generate_series(1,10000) v
;
ANALYZE t;
EXPLAIN
SELECT * from (
select d AS e from (
select c as d from (
select b AS c from (
select a AS b from t
) s
) r
) q
) p
where e =95
;
结果计划:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 10000
ANALYZE
QUERY PLAN
---------------------------------------------------------------------
Index Only Scan using t_pkey on t (cost=0.17..2.38 rows=1 width=4)
Index Cond: (a = 95)
(2 rows)
在OP的片段中,最里面的查询(表表达式)是一个双表连接 ,但机制是相同的:所有外层都可以剥离(并重命名结果列)
是的:连接将受益于连接字段的索引,最终也可以使用索引。
答案 1 :(得分:3)
SQL是描述性语言,而不是过程语言。 SQL查询描述了正在生成的结果集。它没有指定如何创建它 - 在没有编译器选项或提示的Postgres中更是如此。
实际运行的是有向无环操作图(DAG)。编译步骤创建DAG。 Postgres很聪明地意识到子查询没有意义,所以这两个版本都针对同一个DAG进行了优化。
让我补充一点,我认为Postgres通常会实现CTE,因此使用CTE可能会阻止索引的使用。