【问题标题】:Could this query be optimized?这个查询可以优化吗?
【发布时间】:2019-02-24 13:50:36
【问题描述】:

我的目标是通过两个相互依赖的标准选择记录,然后按其他标准对其进行分组。

我找到了按单个条件选择记录并将其分组的解决方案

SELECT *
FROM "records"
NATURAL JOIN (
  SELECT "group", min("priority1") AS "priority1" 
  FROM "records" 
  GROUP BY "group") AS "grouped"

我想我理解这个搜索的概念——选择你关心的属性并在原始表中匹配它们——但是当我将这个概念用于两个优先级时,我得到了这个怪物

SELECT *
FROM "records"
  NATURAL JOIN (
    SELECT *
    FROM (
      SELECT "group", "priority1", min("priority2") AS "priority2"
      FROM "records"
      GROUP BY "group", "priority1") AS "grouped2"
    NATURAL JOIN (
      SELECT "group", min("priority1") AS "priority1"
      FROM "records"
      NATURAL JOIN (
        SELECT "group", "priority1", min("priority2") AS "priority2"
        FROM "records"
        GROUP BY "group", "priority1") AS "grouped2'"
      GROUP BY "group") AS "GroupNested") AS "grouped1"

我要问的只是它不能写得更好(优化和看起来更好)吗?

JSFIDDLE

----更新----

目标是我想通过priority1 为每个group 选择单个id,并且应该首先选择priority2,然后再选择优先级2)。

示例: 当我有表recordsidgrouppriority1priority2 有数据:

id , group , priority1 , priority2
56 ,     1 ,         1 ,         2  
34 ,     1 ,         1 ,         3  
78 ,     1 ,         3 ,         1

结果应该是56,1,1,2。对于每个组,首先搜索 min of priority1,然后搜索 min of priority2

我尝试将 maxmin 组合在一起(在一个查询中`,但它没有找到任何东西(我不再有这个查询)。

【问题讨论】:

  • 请告诉我们您的查询背后的逻辑。此外,在您的问题中直接包含示例数据和预期输出。小提琴没有问题,但你的问题应该站在这里。
  • 每个组的ID背后的逻辑是什么?
  • 我用主要目标和示例数据更新了问题
  • NATURAL JOIN 会吓到很多人。
  • 预期结果中的id = 56 来自哪里,它不在示例数据中?

标签: sql postgresql optimization


【解决方案1】:

EXISTS() 来救援! (我做了一些重命名以避免保留字)


SELECT *
FROM zrecords r
WHERE NOT EXISTS (
    SELECT *
    FROM zrecords nx
    WHERE nx.zgroup = r.zgroup
    AND ( nx.priority1 < r.priority1
        OR nx.priority1 = r.priority1 AND nx.priority2 < r.priority2
        )
    );

或者,为了避免AND / OR 逻辑,直接比较两个元组:


SELECT *
FROM zrecords r
WHERE NOT EXISTS (
    SELECT *
    FROM zrecords nx
    WHERE nx.zgroup = r.zgroup
    AND (nx.priority1, nx.priority2) < (r.priority1 , r.priority2)
    );

【讨论】:

  • 很好,我完全怀念这个功能!有机会我会试试的。
【解决方案2】:

也许这就是你所期望的

 with dat as (
SELECT "group" grp
, priority1, priority2, id
, row_number() over (partition by "group" order by priority1) +
      row_number() over (partition by "group" order by priority2) as lp
FROM "records")

select dt.grp, priority1, priority2, dt.id
from dat dt
join (select min(lp) lpmin, grp from dat group by grp) dt1 on (dt1.lpmin = dt.lp and dt1.grp =dt.grp)

【讨论】:

  • 这实际上返回了我需要的东西。这是因为添加,但这是个好主意,可以通过将正确的优先级乘以 10 来修改(如果优先级数不超过 10)。
【解决方案3】:

只需使用 row_number() 。 . .一次:

select r.*
from (select r.*,
             row_number() over (partition by "group" order by priority1, priority2) as seqnum
      from records r
     ) r
where seqnum = 1;

注意:我建议您避免使用natural join。您可以改用using(如果您不想明确包含相等比较)。

带有natural join 的查询很难调试,因为join 键未列出。更糟糕的是,“自然”连接不使用正确声明的外键关系。它们仅依赖于具有相同名称的列。

在我设计的表格中,它们无论如何都不会有用,因为几乎所有表格都有createdAtcreatedBy 列。

【讨论】:

  • 不错的解决方案。不幸的是,我需要更简单的解决方案,因为我使用的框架的 DSL 不支持row_number() 功能,既不支持over,也不支持partition by。你对natural join 是对的,我忘了说我会替换它,演示更简单。我可以询问not exists 解决方案的性能吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
  • 1970-01-01
  • 2018-01-11
  • 1970-01-01
  • 2022-10-30
  • 1970-01-01
相关资源
最近更新 更多