【问题标题】:Why is the LIMIT in my lateral join being ignored?为什么我的横向连接中的 LIMIT 被忽略了?
【发布时间】:2020-10-22 18:50:13
【问题描述】:

我有一张这样的桌子:

create table cars (
    id serial primary key,
    au_rating integer,
    year integer
);

insert into cars (au_rating, year) VALUES 
  (1,2019),
  (1,2020),
  (1,2016),
  (2,2016),
  (2,2019),
  (3,2020),
  (3,2018),
  (3,2017),
  (3,2019),
  (3,2020),
  (0,2020),
  (0,2006)
;

我想使用JOIN LATERAL(类似于:https://stackoverflow.com/a/37862028/1002814)在每个组中选择 N 个第一个元素:

SELECT cars_outer.id, cars_top.au_rating, cars_top.year
FROM cars cars_outer
JOIN LATERAL (
    SELECT * FROM cars cars_inner
    WHERE cars_inner.au_rating = cars_outer.au_rating
    ORDER BY ABS(cars_inner.year - 2019)
    LIMIT 2
) cars_top on true
ORDER BY cars_outer.au_rating DESC

但是,由于我只希望每组 au_rating 最多包含 2 行,因此我为横向连接提供了 LIMIT 2,但它似乎被忽略了 - 为什么?

http://sqlfiddle.com/#!17/c0704/5

【问题讨论】:

  • 顺便说一句 join lateral (...) on true 等于 cross join lateral (...)

标签: sql postgresql select greatest-n-per-group


【解决方案1】:

如果您想要每个评级组的前 2 条记录,那么我建议使用窗口函数进行过滤。

你似乎想要的逻辑是:

select (c.c).*
from (
    select c, row_number() over(partition by au_rating order by year) rn
    from cars c
) c
where rn <= 2

我不确定您如何定义“前 2 名”记录。这会按年份对记录进行排序,因此您可以获得每个au_rating 的两个最旧 记录。您可以将order by 子句更改为您的实际需求。

Demo on DB Fiddle

编号 | au_rating |年 -: | --------: | ---: 12 | 0 | 2006年 11 | 0 | 2020 3 | 1 | 2016 年 1 | 1 | 2019 4 | 2 | 2016 年 5 | 2 | 2019 8 | 3 | 2017 7 | 3 | 2018

【讨论】:

    【解决方案2】:

    您接受的窗口函数的 GMB 答案是最好的。

    LIMIT 在您的LATERAL JOIN 查询中得到认可。问题是记录重复并且您省略了DISTINCT

    更新

    我没有提到你想要cars_top.id 而不是cars_outer.id,DISTINCT 意味着order_by 列必须在结果中。

    SELECT DISTINCT cars_top.id, cars_top.au_rating, cars_top.year
        FROM cars cars_outer
        JOIN LATERAL (
            SELECT * FROM cars cars_inner
            WHERE cars_inner.au_rating = cars_outer.au_rating
            ORDER BY ABS(cars_inner.year - 2019)
            LIMIT 2
        ) cars_top on true
      ORDER BY cars_top.au_rating DESC
    

    【讨论】:

    • 很有趣,您能否详细说明一下,我有兴趣看到它的实际效果,因为仅添加 DISTINCT 似乎并不能做到这一点。
    • @NielsKristian 更新了我的答案。抱歉,我漏掉了。
    【解决方案3】:

    您的查询正在执行正是您所要求的。对于您表中的每辆汽车,它会返回匹配的汽车。

    您可以通过让第一个“表格”仅标识您想要的评级来做您想做的事情:

    SELECT cars_top.au_rating, cars_top.year
    FROM (SELECT DISTINCT au_rating FROM cars
         ) cars_outer CROSS JOIN LATERAL
         (SELECT *
          FROM cars cars_inner
          WHERE cars_inner.au_rating = cars_outer.au_rating
          ORDER BY ABS(cars_inner.year - 2019)
          LIMIT 2
         ) cars_top 
    ORDER BY cars_outer.au_rating DESC;
    

    请注意,如果您使用CROSS JOIN LATERAL,则不需要ON 子句。

    【讨论】:

      猜你喜欢
      • 2013-09-30
      • 2016-06-20
      • 1970-01-01
      • 2018-03-12
      • 1970-01-01
      • 2019-03-31
      • 1970-01-01
      • 2022-12-23
      • 2021-08-23
      相关资源
      最近更新 更多