5

我正在使用 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

4

2 回答 2

6

更新:

LATERALjoins 允许这样做,并在 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 上的演示。

于 2012-05-27T14:11:04.423 回答
0

没有索引的相关子查询无论如何都会进行交叉连接。因此,表达查询的另一种方式是:

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

这种方法的优点是没有交叉连接,也没有相关的子查询。

于 2012-05-28T01:22:21.790 回答