【问题标题】:in sql, best syntax for query using subquery more than once在 sql 中,多次使用子查询的最佳语法
【发布时间】:2013-04-14 01:10:57
【问题描述】:

从桌子上

create table seeplai.item(
  itm_id  serial  primary key,
  itp_id  int     not null references seeplai.item_type,
  itm_sq  int2    not null default 10,
  ... )
create index item_itp_sq on seeplai.item( itp_id, itm_sq );

使用item 记录作为基础,我希望找到具有相同itp_id 并且具有更少itm_sq 的最大itp_sq。现在使用两个子查询,在语法和逻辑上都是正确的,但是感觉不对。

select  max(itm_sq)
into    v_prev_sq
from    seeplai.item itm
where   itm.itp_id = (select itp_id from seeplai.item where itm_id=p_itm_id)
and     itm.itm_sq < (select itm_sq from seeplai.item where itm_id=p_itm_id);

知道我使用了更好的方法,但是它的语法已经融入了我的脑海中

【问题讨论】:

  • itm_tp 不在您的定义中?
  • itm_tp => itp_id - 已修复

标签: sql postgresql subquery


【解决方案1】:

要获得一个最大的itm_sq,它小于你所在行中的那个:

SELECT  i2.itm_sq
FROM    seeplai.item i
JOIN    seeplai.item i2 USING (itp_id)
WHERE   i.itm_id = p_itm_id
AND     i2.itm_sq < i.itm_sq 
ORDER   BY i2.itm_sq DESC
LIMIT   1;

要打破平局(具有相同itm_sq 的多行),您可以ORDER BY 附加列。

这样您就不必重复使用任何东西。如果必须,可以使用子查询或CTE


如果您需要之前的itm_sq 来为每个 共享相同itp_id 的项目,则窗口函数lag()(或倒序的lead())将很有帮助:

SELECT  i2.*, lag(i2.itm_sq) OVER (ORDER BY i2.itm_sq) AS previous_itm_sq
FROM    seeplai.item i
JOIN    seeplai.item i2 USING (itp_id)
WHERE   i.itm_id = p_itm_id
ORDER   BY i2.itm_sq DESC;

【讨论】:

  • 现在才 8:30,你又逼我喝第二杯咖啡
  • @ccyoung:有什么能让你跟上进度。 ;)
  • 好的,这很酷,像往常一样。又是我的钦佩。也感谢你没有这么早就把 CTE 强加给我。
  • 只要unique( itp_id, itm_sq ) 那么第一个查询就可以工作 - 对吧? (你的第二个解决方案是我用过很多次但今天想不起来的那个。)
  • 我最初的(现在是第二个)查询是针对一个稍微不同的问题(起初被误解)。它获取共享相同itp_id所有 个项目的前一行。由于您 SELECT INTO 是一个变量(显然在 plpgsql 代码中),因此这恰好“起作用”。第一行被使用,其余的被丢弃。它也可能恰好是正确的行。但是您不应该使用它,结果不可靠且效率不高。您想使用我以后的查询(现在是第一个)。我澄清了答案以避免混淆。
【解决方案2】:

您需要将表连接到自身:

select max(itm_sq)
into v_prev_sq
from seeplai.item itm
join seeplai.item itm2 on itm.itm_tp = itm2.itm_tp and itm2.itm_id=p_itm_id
join seeplai.item itm3 on itm.itm_sq < itm3.itm_sq and itm3.itm_id=p_itm_id

这是您查询的“直接翻译”,但我认为这种简化也可以:

select max(itm_sq)
into v_prev_sq
from seeplai.item itm
join seeplai.item itm2
    on itm.itm_tp = itm2.itm_tp 
    and itm2.itm_id = p_itm_id
    and itm.itm_sq < itm2.itm_sq

【讨论】:

  • 你的第二个版本对我来说很有意义。
【解决方案3】:

我不确定,但也许你可以使用 PostgreSQL 的 WITH Queries (Common Table Expressions)

【讨论】:

    【解决方案4】:

    也许您正在寻找公用表表达式? http://www.postgresql.org/docs/9.2/static/queries-with.html

    虽然我认为实际上使用连接更容易做到这一点......

    select  max(itm_0.itm_sq)
    into    v_prev_sq
    from    seeplai.item itm_0,
    join itm_1
    on itm_0.itm_tp = itm_1.itm_tp
    and itm_0.itm_sq < itm_1.itm_sq
    and itm_0.itm_id = itm_1.p_itm_id
    group by itm_1.p_itm_id
    

    如果需要,可能使用DISTINCT ON

    【讨论】:

    • 你为什么使用group by
    • @ccyoung:您在查询中使用了最大值,所以我假设会有超过 1 行。如果没有,您可以将分组排除在外。
    【解决方案5】:

    只需将子查询的值存储在一个变量中:

    declare @retVal varchar(20);
    set @retVal = select itm_tp from seeplai.item where itm_id=p_itm_id
    
    select  max(itm_sq)
    into    v_prev_sq
    from    seeplai.item itm
    where   itm.itm_tp = @retVal
    and     itm.itm_sq < @retVal;
    

    【讨论】:

    • 当然可以。但实际上有一种非常巧妙的方法可以在单个 SQL 语句中执行此操作 - 不幸的是 - 现在完全逃避了我。
    • 这不是有效的 Postgres 语法。看起来像 t-SQL?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多