有啥方法可以避免一个查询中的多个联接

Posted

技术标签:

【中文标题】有啥方法可以避免一个查询中的多个联接【英文标题】:Is there any way to avoid multiple joins in one query有什么方法可以避免一个查询中的多个联接 【发布时间】:2021-12-31 21:09:43 【问题描述】:

我有 2 张桌子。产品表包含我的 sku,其中包含以下列:id、name、sku、image

我的另一个表是我的 warehouse_in_and_out,它有:id、date_time、sku_id、inbound_outbound、adjustment。 date_time 和 sku_id 一起是唯一的,所以我每天每个 sku 只有 1 行。我的 inbound_outbound 和调整也是一个整数数组。

我想在每个 sku 的日期范围内获取每一天的 inbound_outbound,并且还希望获得每个 sku 的所有行的所有 inbound_outbound 的总和

我想出了这样的东西:

SELECT P.ID ,SKU,
    MAX(CASE
    WHEN DATE_TIME = '12/31/2021' THEN WARE.INBOUND_OUTBOUND
        ELSE NULL
    END) AS INBOUND_OUTBOUND_1,
    MAX(CASE
    WHEN DATE_TIME = '12/31/2021' THEN WARE.ADJUSTMENT
        ELSE NULL
    END) AS ADJUSTMENT_1,
    MAX(CASE
    WHEN DATE_TIME = '12/30/2021' THEN WARE.INBOUND_OUTBOUND
        ELSE NULL
    END) AS INBOUND_OUTBOUND_2,
    MAX(CASE
    WHEN DATE_TIME = '12/30/2021' THEN WARE.ADJUSTMENT
        ELSE NULL
    END) AS ADJUSTMENT_2
FROM PRODUCTS AS P
LEFT JOIN WAREHOUSE_IN_AND_OUT AS WARE ON WARE.SKU_ID = P.ID
WHERE P.STORE_ID IN ('1',
        '2',
        '3',
        '4',
        '5')
GROUP BY P.ID, SKU
ORDER BY SKU

我在 json 中的结果是:


    id: '1',
    sku: 'ABC',
    inbound_outbound_1: null,
    adjustment_1: null,
    inbound_outbound_2: [
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      5000, 4960
    ],
    adjustment_2: null
  

当我想将我的总数添加到这个 sql 中时,我得到 2 行相同的行:

SELECT P.ID ,SKU,
    MAX(CASE
    WHEN DATE_TIME = '12/31/2021' THEN WARE.INBOUND_OUTBOUND
        ELSE NULL
    END) AS INBOUND_OUTBOUND_1,
    MAX(CASE
    WHEN DATE_TIME = '12/31/2021' THEN WARE.ADJUSTMENT
        ELSE NULL
    END) AS ADJUSTMENT_1,
    MAX(CASE
    WHEN DATE_TIME = '12/30/2021' THEN WARE.INBOUND_OUTBOUND
        ELSE NULL
    END) AS INBOUND_OUTBOUND_2,
    MAX(CASE
    WHEN DATE_TIME = '12/30/2021' THEN WARE.ADJUSTMENT
        ELSE NULL
    END) AS ADJUSTMENT_2,
    COALESCE(
                    (SELECT SUM(I)
                        FROM UNNEST(WARE.INBOUND_OUTBOUND) AS I),0)::INTEGER AS TOTAL
FROM PRODUCTS AS P
LEFT JOIN WAREHOUSE_IN_AND_OUT AS WARE ON WARE.SKU_ID = P.ID
WHERE P.STORE_ID IN ('1',
        '2',
        '3',
        '4',
        '5')
GROUP BY P.ID, SKU, INBOUND_OUTBOUND
ORDER BY SKU

我的结果是:

[
  
    id: '1',
    sku: 'ABC',
    total: -4,
    inbound_outbound_1: [ 52, -56 ],
    adjustment_1: null,
    inbound_outbound_2: null,
    adjustment_2: null
  ,
  
    id: '1',
    sku: 'ABC',
    total: 188520,
    inbound_outbound_1: null,
    adjustment_1: null,
    inbound_outbound_2: [
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      5000, 4960
    ],
    adjustment_2: null
  
]

但我只想为每个 sku 获取一行。我的预期结果是:

[
  
    id: '1',
    sku: 'ABC',
    total: 188516,
    inbound_outbound_1:  [ 52, -56 ],
    adjustment_1: null,
    inbound_outbound_2: [
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      4960, 4960, 4960, 4960, 4960, 4960,
      5000, 4960
    ],
    adjustment_2: null
  
]

感谢 Jon,我在 dbfiddle 中实现了我的架构

如果可能的话,我希望 6 和 8 得到一个结果。我不想像 7 parag 那样对每个 sku 有很多结果。我想要每个 sku 的总数

【问题讨论】:

GROUP BY P.ID, SKU, INBOUND_OUTBOUND 的结果不保证每个 SKU 包含一行(组)。您需要对问题提供更完整的逻辑描述,以及您提出的所有其他问题中要求的所有详细信息。我不会在这里重复。解释您的整个目标,并将其与您的 SQL 中使用的逻辑联系起来,尤其是您的 GROUP BY 子句。 您是否尝试过从您的GROUP BY 中删除INBOUND_OUTBOUND @JonArmstrong 我还需要解释什么?你能具体说明一下吗?我很乐意解释。我的意思是我说了我的表格列和我想要的结果。 1) 没有导致这些结果的精确输入数据的结果通常是不够的。 2) 显示每个表中的约束很重要,因此,例如,我们可以判断SKU 在表中的行中是否保证唯一。 3) 包含精确类型的实际模式,以便可以测试您的 SQL(和任何解决方案)。请记住,我为您提供了这个,但我不得不猜测。请更新我提供的小提琴以更正任何类型问题并调整数据以允许测试您的 SQL。 从您针对同一问题提出的一个问题中,这是您应该提供的测试用例,显示方案、数据(通过插入)和实际结果:dbfiddle.uk/… 你会想要还提供预期结果以及我们如何(从逻辑上)从您的数据中得出该结果。你不需要知道 SQL。但是你确实需要从逻辑上理解你自己的问题。 【参考方案1】:

我会在您的 SELECT 中使用横向连接而不是子查询...

SELECT P.ID, P.SKU,
    MAX(CASE
    WHEN DATE_TIME = '12/31/2021' THEN WARE.INBOUND_OUTBOUND
        ELSE NULL
    END) AS INBOUND_OUTBOUND_1,
    MAX(CASE
    WHEN DATE_TIME = '12/31/2021' THEN WARE.ADJUSTMENT
        ELSE NULL
    END) AS ADJUSTMENT_1,
    MAX(CASE
    WHEN DATE_TIME = '12/30/2021' THEN WARE.INBOUND_OUTBOUND
        ELSE NULL
    END) AS INBOUND_OUTBOUND_2,
    MAX(CASE
    WHEN DATE_TIME = '12/30/2021' THEN WARE.ADJUSTMENT
        ELSE NULL
    END) AS ADJUSTMENT_2,
       MAX(CASE
    WHEN DATE_TIME = '12/29/2021' THEN WARE.INBOUND_OUTBOUND
        ELSE NULL
    END) AS INBOUND_OUTBOUND_3,
    MAX(CASE
    WHEN DATE_TIME = '12/29/2021' THEN WARE.ADJUSTMENT
        ELSE NULL
    END) AS ADJUSTMENT_3,
    COALESCE(SUM(I.SUM_I),0)::INTEGER AS TOTAL
FROM PRODUCTS AS P
LEFT JOIN WAREHOUSE_IN_AND_OUT AS WARE ON WARE.SKU_ID = P.ID
CROSS JOIN LATERAL (SELECT SUM(I) SUM_I FROM UNNEST(WARE.INBOUND_OUTBOUND) I) I
GROUP BY P.ID, SKU
ORDER BY SKU

https://dbfiddle.uk/?rdbms=postgres_14&fiddle=9f94ecd3f1c38ab01eca06699c980424

【讨论】:

【参考方案2】:

虽然@MatBailie 提供了一个非常好的解决方案,但我想我会添加一个您的两个语句的直接/简单组合的示例。这在许多其他情况下也很有帮助。

The fiddle, with both solutions

WITH cte1 AS (
         SELECT P.ID, P.SKU
              , MAX(CASE
                      WHEN DATE_TIME = '12/31/2021' THEN WARE.INBOUND_OUTBOUND
                          ELSE NULL
                      END
                   ) AS INBOUND_OUTBOUND_1
              , MAX(CASE
                      WHEN DATE_TIME = '12/31/2021' THEN WARE.ADJUSTMENT
                          ELSE NULL
                      END
                   ) AS ADJUSTMENT_1
              , MAX(CASE
                      WHEN DATE_TIME = '12/30/2021' THEN WARE.INBOUND_OUTBOUND
                          ELSE NULL
                      END
                   ) AS INBOUND_OUTBOUND_2
              , MAX(CASE
                     WHEN DATE_TIME = '12/30/2021' THEN WARE.ADJUSTMENT
                          ELSE NULL
                      END
                   ) AS ADJUSTMENT_2
              , MAX(CASE
                      WHEN DATE_TIME = '12/29/2021' THEN WARE.INBOUND_OUTBOUND
                          ELSE NULL
                      END
                   ) AS INBOUND_OUTBOUND_3
              , MAX(CASE
                     WHEN DATE_TIME = '12/29/2021' THEN WARE.ADJUSTMENT
                          ELSE NULL
                      END
                   ) AS ADJUSTMENT_3
           FROM PRODUCTS AS P
           LEFT JOIN WAREHOUSE_IN_AND_OUT AS WARE ON WARE.SKU_ID = P.ID
          GROUP BY P.ID, SKU
          ORDER BY SKU
     )
   , cte2 AS (
         SELECT sku, SUM(total) as total
           FROM (
                  SELECT P.SKU
                       , COALESCE(
                                    (SELECT SUM(I)
                                       FROM UNNEST(WARE.INBOUND_OUTBOUND) AS I
                                    )
                                    , 0
                                 )::INTEGER AS TOTAL
                    FROM PRODUCTS AS P
                    LEFT JOIN WAREHOUSE_IN_AND_OUT AS WARE ON WARE.SKU_ID = P.ID
                   ORDER BY SKU
                ) AS test
          GROUP BY SKU
     )
SELECT cte1.*, cte2.total
  FROM cte1
  JOIN cte2 
    ON cte1.SKU = cte2.SKU
;

【讨论】:

以上是关于有啥方法可以避免一个查询中的多个联接的主要内容,如果未能解决你的问题,请参考以下文章

避免用于派生选择中的列的多个重复子查询

有啥方法可以避免 C++ 中的“调试断言失败”窗口?

为啥要避免动态 SQL 查询?有啥建议可以删除坏的部分并使用这些吗?

有啥方法可以让浏览器页面避免 Pyqt5 中的重定向?

有啥方法可以避免 axios 中的 linkpreview.net 出现 CORS 错误,例如 Angular 中的 trustAsResourceUrl()?

BigQuery 避免多个子查询