我正在使用 PostgreSQL 9,我想在表RP
中找到所有元组的最近邻居RQ
,比较日期(t
),但我收到此错误:
错误:FROM 中的子查询不能引用相同查询级别的其他关系
使用此查询:
SELECT *
FROM RQ, (SELECT * FROM RP ORDER BY ABS(RP.t - RQ.t) LIMIT 1) AS RA
RQ.t
在子查询中似乎是问题所在。我怎样才能避免这个错误?如何从子查询访问到RQ
?
我正在使用 PostgreSQL 9,我想在表RP
中找到所有元组的最近邻居RQ
,比较日期(t
),但我收到此错误:
错误:FROM 中的子查询不能引用相同查询级别的其他关系
使用此查询:
SELECT *
FROM RQ, (SELECT * FROM RP ORDER BY ABS(RP.t - RQ.t) LIMIT 1) AS RA
RQ.t
在子查询中似乎是问题所在。我怎样才能避免这个错误?如何从子查询访问到RQ
?
LATERAL
joins 允许这样做,并在 Postgres 9.3 中引入。细节:
原因在错误消息中。列表的一个元素FROM
不能引用FROM
同一级别的列表的另一个元素。对于同一级别的对等方来说,它是不可见的。您可以使用相关子查询来解决此问题:
SELECT *, (SELECT t FROM rp ORDER BY abs(rp.t - rq.t) LIMIT 1) AS ra
FROM rq
显然,您不在乎RP
从一组同样接近的行中选择哪一行,所以我也这样做。
但是,SELECT
列表中的子查询表达式只能返回一列。如果您想要 table 中的多个列或所有列RP
,请使用类似这样的子查询构造:
我假设两个表中都存在一个主键id
。
SELECT id, t, (ra).*
FROM (
SELECT *, (SELECT rp FROM rp ORDER BY abs(rp.t - rq.t) LIMIT 1) AS ra
FROM rq
) x;
相关子查询因性能不佳而臭名昭著。这种查询——虽然显然是计算你想要的——会特别糟糕,因为表达式rp.t - rq.t
不能使用索引。更大的表性能会急剧下降。
这个重写的查询应该能够利用索引RP.t
,它应该对大表执行得更快。
WITH x AS (
SELECT *
,(SELECT t
FROM rp
WHERE rp.t < rq.t
ORDER BY rp.t DESC
LIMIT 1) AS t_pre
,(SELECT t
FROM rp
WHERE rp.t >= rq.t
ORDER BY rp.t
LIMIT 1) AS t_post
FROM rq
)
SELECT id, t
,CASE WHEN (t_post - t) < (t - t_pre)
THEN t_post
ELSE COALESCE(t_pre, t_post) END AS ra
FROM x;
同样,如果你想要整行:
WITH x AS (
SELECT *
,(SELECT rp
FROM rp
WHERE rp.t < rq.t
ORDER BY rp.t DESC
LIMIT 1) AS t_pre
,(SELECT rp
FROM rp
WHERE rp.t >= rq.t
ORDER BY rp.t
LIMIT 1) AS t_post
FROM rq
), y AS (
SELECT id, t
,CASE WHEN ((t_post).t - t) < (t - (t_pre).t)
THEN t_post
ELSE COALESCE(t_pre, t_post) END AS ra
FROM x
)
SELECT id AS rq_id, t AS rq_t, (ra).*
FROM y
ORDER BY 2;
注意复合类型使用括号!这里没有多余的括号。更多关于这里和这里的手册。
使用 PostgreSQL 9.1 测试。sqlfiddle 上的演示。
没有索引的相关子查询无论如何都会进行交叉连接。因此,表达查询的另一种方式是:
select rp.*, min(abs(rp.t - rq.t))
from rp cross join
rq
group by <rp.*> -- <== need to replace with all columns
还有另一种方法,它有点复杂。这需要使用累积和。
这是想法。将所有 rp 和 rq 值组合在一起。现在,用最接近的 rp 值枚举它们。也就是说,为 rp 创建一个标志并获取累积和。因此,两个 rp 值之间的所有 rq 值都具有相同的 rp 索引。
与给定 rq 值最接近的值具有与 rq 值相同或更多的 rp 索引。计算 rq_index 使用累积和。
以下查询将其放在一起:
with rqi as (select t.*, sum(isRQ) over (order by t) as rq_index
from (select rq.t, 0 as isRP, <NULL for each rp column>
from rq
union all
select rq.t, 1 as isRP, rp.*
from rp
) t
) t
select rp.*,
(case when abs(rqprev.t - rp.t) < abs(rqnext.t - rp.t)
then abs(rqprev.t - rp.t)
else abs(rqnext.t - rp.t)
end) as closest_value
from (select *
from t
where isRP = 0
) rp join
(select *
from t
where isRP = 1
) rqprev
on rp.rp_index = rqprev.rp_index join
(select *
from t
where isRP = 1
) rqnext
on rp.rp_index+1 = rpnext.rq_index
这种方法的优点是没有交叉连接,也没有相关的子查询。