【问题标题】:Joining multiple CTEs加入多个 CTE
【发布时间】:2021-03-24 13:25:21
【问题描述】:

我正在研究一家大型零售店的数据库。 我必须从多个表中查询数据以获取收入、原始收益等数字并比较不同的时间段。 其中大部分都很容易,但我一直在努力寻找一种加入多个 CTE 的方法。 我做了一个小提琴,所以你知道我在说什么。 我大大简化了结构,并在子查询中省略了很多列,因为在这种情况下它们无关紧要。

如您所见,每个表格中的每一行都包含国家和品牌。 最终查询必须按这些分组。 我首先尝试的是 FULL JOIN 所有表,但在某些情况下这不起作用,如您在此处看到的:SQLfiddle #1。注意最后两行没有正确分组。

Select Coalesce(incoming.country, revenue.country, revcompare.country,
  openord.country) As country,
  Coalesce(incoming.brand, revenue.brand, revcompare.brand,
  openord.brand) As brand,
  incoming.OrdersNet,
    openord.OpenOrdersNet,
    revenue.Revenue,
    revenue.RawProceeds,
    revcompare.RevenueCompare,
    revcompare.RawProceedsCompare
From incoming
  Full Join openord On openord.country = incoming.country And
    openord.brand = incoming.brand
  Full Join revenue On revenue.country = incoming.country And
    revenue.brand = incoming.brand
  Full Join revcompare On revcompare.country = incoming.country And
    revcompare.brand = incoming.brand
Group By incoming.OrdersNet,
    openord.OpenOrdersNet,
    revenue.Revenue,
    revenue.RawProceeds,
    revcompare.RevenueCompare,
    revcompare.RawProceedsCompare,
  incoming.country,
  revenue.country,
  openord.country,
  revcompare.country,
  incoming.brand,
  revenue.brand,
  revcompare.brand,
  openord.brand
Order By country,
  brand

然后我重写了保留所有 CTE 的查询。我添加了另一个 CTE(基础),它结合了所有可能的国家和品牌组合,并加入了那个组合。 现在它工作正常(在这里查看 -> SQLfiddle #2)但它看起来很复杂。难道没有更简单的方法来实现这一点吗?我唯一可能无法更改的是 CTE,因为在现实生活中它们要复杂得多。

WITH basis AS (
  SELECT Country, Brand FROM incoming
  UNION
  SELECT Country, Brand FROM openord
  UNION
  SELECT Country, Brand FROM revenue
  UNION
  SELECT Country, Brand FROM revcompare
)

SELECT 
    basis.Country,
    basis.Brand,
    incoming.OrdersNet,
    openord.OpenOrdersNet,
    revenue.Revenue,
    revenue.RawProceeds,
    revcompare.RevenueCompare,
    revcompare.RawProceedsCompare
FROM basis
LEFT JOIN incoming   On incoming.Country = basis.Country AND incoming.Brand = basis.Brand
LEFT JOIN openord        On openord.Country = basis.Country AND openord.Brand = basis.Brand
LEFT JOIN revenue      On revenue.Country = basis.Country AND revenue.Brand = basis.Brand
LEFT JOIN revcompare On revcompare.Country = basis.Country AND revcompare.Brand = basis.Brand

感谢大家的帮助!

【问题讨论】:

  • 所以只是为了获得您想要的内容的高级摘要。您想要一个所有 Country+Brand 组合的唯一列表,然后您想要加入来自 incomingopenordrevenuerecompare 的统计信息?
  • 另外,这个必须在单个查询中完成有什么原因吗?为清楚起见,如果您使用表变量或临时表,很多时候它会使代码更具可读性。
  • 如果您在使用 CTE 时遇到困难,请恢复到临时表
  • 注意最后两行没有正确分组是的,他们确实分组正确,因为这就是您编写查询的方式。最后的 group by 子句中有太多列。但这是一个有争议的问题,因为您的 CTE 只会为每个国家/品牌生产 1 行。最终查询根本不需要分组。
  • 注意 - CTE 传入和 openord 可以使用条件求和合并为 1 以提高效率。收入和 revcompare 相同。

标签: sql sql-server tsql common-table-expression


【解决方案1】:

由于您只使用两个表,ordersrev,因此考虑通过将 WHERE 条件移动到 CASE 逻辑来进行条件聚合以进行单个聚合查询。此外,对于两张表中LEFT JOIN 的所有可能的国家/品牌对,只考虑一个 CTE。

WITH cb AS (
  SELECT Country, Brand FROM orders
  UNION
  SELECT Country, Brand FROM rev
)

SELECT cb.Country
     , cb.Brand
     , SUM(o.netprice) AS OrdersNet
     , SUM(CASE 
               WHEN o.isopen = 1
               THEN o.netprice
           END) AS OpenOrdersNet
     , SUM(CASE 
               WHEN r.bdate BETWEEN '2020-12-01' AND '2020-12-31'
               THEN r.netprice
           END) AS Revenue
     , SUM(CASE 
               WHEN r.bdate BETWEEN '2020-12-01' AND '2020-12-31'
               THEN r.rpro
           END) AS RawProceeds
     , SUM(CASE 
              WHEN r.bdate BETWEEN '2020-11-01' AND '2020-11-30'
              THEN r.netprice
           END) AS RevenueCompare
     , SUM(CASE 
              WHEN r.bdate BETWEEN '2020-11-01' AND '2020-11-30'
              THEN r.rpro
           END) AS RawProceedsCompare    
FROM cb
LEFT JOIN orders o
 ON cb.Country = o.Country
 AND cb.Brand = o.Brand
LEFT JOIN rev r
 ON cb.Country = r.Country
 AND cb.Brand = r.Brand
GROUP BY cb.Country
       , cb.Brand

SQL Fiddle

【讨论】:

    猜你喜欢
    • 2019-10-05
    • 2015-06-03
    • 1970-01-01
    • 2019-08-28
    • 2012-04-25
    • 1970-01-01
    • 2020-06-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多