【问题标题】:The best SQL Code to get this Output获得此输出的最佳 SQL 代码
【发布时间】:2012-07-05 22:17:32
【问题描述】:

简单地说, 我有这些表。

Planning: 
  Date | Machine | Product | PlanningParts

OKParts: 
  Date | Machine | Product | OKParts

Scrap: 
  Date | Machine | Product | ScrapParts

Trials: 
  Date | Machine | Product | TrialParts

Breakdowns: 
  Date | Machine | Product | Minutes

这些表可以有两个或多个相同 [Date |机器 | Product],它们不是唯一的列。 获得此输出的最佳 SQL 代码(SQLite 或 SQL Server)是什么?

Output:
  Date | Machine | Product | Planning | OKParts | ScrapParts | TrialParts | Minutes

编辑: 在输出中,每个 [Date,Machine,Product] 我需要一行,其余列必须是 SUM (我不会说英语,对不起)

编辑: 例子: (我不会把表格“试验”缩短)

Planning:
     Date    | Machine | Product | PlanningParts
     1/6/12  |  Blower | A001    | 100
     2/6/12  |  Blower | A002    | 100
     2/6/12  | Assembly| B001    | 50

OKParts:
     Date   | Machine | Product  | OKParts
     1/6/12 | Blower  | A001     | 50
     1/6/12 | Blower  | A001     | 20
     1/6/12 | Blower  | A002     | 100

Scrap:
     Date   | Machine | Product  | ScrapParts | Reason
     1/6/12 | Blower  | A001     | 5          | Low Weight
     1/6/12 | Blower  | A001     | 3          | High Weight
     2/6/12 | Assembly| B001     | 4          | Bad Cut

Breakdowns
     Date   | Machine | Product  | Minutes    | Reason
     1/6/12 | Blower  | A001     | 100        | Manteinance
     1/6/12 | Blower  | A001     | 20         | Manteinance
     2/6/12 | Assembly| B001     | 100        | Quality approval


   OUTPUT:
         Date   | Machine | Product  | Planning | OKParts | ScrapParts | Breakdowns
         1/6/12 | Blower  | A001     | 100      | 70      | 8          | 120
         1/6/12 | Blower  | A002     | 100      | 100     | 0          | 0
         2/6/12 | Assembly| B001     | 50       | 0       | 4          | 100

【问题讨论】:

  • 请显示示例数据(包括几行,其中有多个值对单个 SUM 有贡献)和所需的结果。这样,我们就不必就结果提出 15 个后续问题,或者猜测您需要的查询。如果您提供样本数据和期望的结果,我们可以向您展示解决方案并让您因为您的要求不明确而拒绝它们之前对其进行测试和验证。
  • 如果一张表中有记录,那么同一个{Date,Machine,Product}总是会有至少条记录吗?其他表?或者他们有时可能有 0 条该 {Date,Machine,Product} 的记录?和/或是否有一张表总是至少一条记录用于该{Date,Machine,Product}?
  • @Aaron Bertrand - 对不起,我还没有阅读第一条评论
  • @Dems - 他们有时有该 {Date,Machine,Product} 的 0 条记录

标签: sql sql-server sqlite


【解决方案1】:

创建一个将所有表合并为一个的 CTE,然后使用 SUM 聚合函数,将每个日期、机器、产品组的列相加。有点像这样(未测试):

WITH AllParts AS (
SELECT Date, Machine, Product, PlanningParts, NULL AS OKParts, NULL AS ScrapParts, NULL AS TrialParts, NULL AS Breakdowns
FROM Planning
UNION ALL
SELECT Date, Machine, Product,NULL AS PlanningParts, OKParts, NULL AS ScrapParts, NULL AS TrialParts, NULL AS Breakdowns
FROM OKParts
UNION ALL
SELECT Date, Machine, Product,NULL AS PlanningParts, NULL AS OKParts, ScrapParts, NULL AS TrialParts, NULL AS Breakdowns
FROM Scrap
UNION ALL
SELECT Date, Machine, Product,NULL AS PlanningParts, NULL AS OKParts, NULL AS ScrapParts, TrialParts, NULL AS Breakdowns
FROM Trials
UNION ALL
SELECT Date, Machine, Product,NULL AS PlanningParts, NULL AS OKParts, NULL AS ScrapParts, TrialParts, Breakdowns
FROM BreakDowns
)
SELECT
Date, Machine, Product, SUM(OKParts) AS OKParts, SUM(ScrapParts) AS ScrapParts, SUM(TrialParts) AS TrialParts, SUM(BreakDowns) AS Breakdowns
FROM AllParts
GROUP BY Date, Machine, Product

【讨论】:

  • 不是最容易阅读的,但确实有效。并且不存在造成JOIN版本问题的重复。
  • 这就是我已经拥有的,但我看到代码,它看起来很脏。必须有更有效的方法
  • 显然,并非所有联合(仅最上面的联合)都需要别名,但我这样写是为了更清楚地说明发生了什么。删除不必要的别名会更容易阅读!这是我能想到的最有效的方法,因为UNION ALLs 只需要很少的工作。
  • @leinho 现在来吧,不要因为您的设计不是最优的或者因为您的查询要求不适合您的设计而责备该工具,反之亦然。
  • @leinho - 您的所有表是否都将 {Date,Machine,Product} 上的索引作为单个复合索引?
【解决方案2】:

我很想看看 UNION 之后的 SUMming 是否与先 SUM 的查询一样好:

WITH Vals AS (
   SELECT Date, Machine, Product, 'PlanningParts' Which, Sum(PlanningParts) Value FROM Planning GROUP BY Date, Machine, Product
   UNION ALL SELECT Date, Machine, Product, 'OKParts', Sum(OKParts) FROM OKParts GROUP BY Date, Machine, Product
   UNION ALL SELECT Date, Machine, Product, 'ScrapParts', Sum(ScrapParts) FROM Scrap GROUP BY Date, Machine, Product
   UNION ALL SELECT Date, Machine, Product, 'TrialParts', Sum(TrialParts) FROM Trials GROUP BY Date, Machine, Product
   UNION ALL SELECT Date, Machine, Product, 'Minutes', Sum(Minutes) FROM Breakdowns GROUP BY Date, Machine, Product
)
SELECT *
FROM Vals
PIVOT (Max(Value) FOR Which IN (PlanningParts, OKParts, ScrapParts, TrialParts, Minutes)) P;

是的,这很痛苦,但这就是您从给定的数据库设计中得到的。将这 5 个表重构为一个表,并用视图替换这些表并使用触发器 是可行的。

注意:PIVOT 适用于 SQL Server 2005 及更高版本。但你甚至不需要 PIVOT:

WITH Vals AS (
   SELECT Date, Machine, Product, 'PlanningParts' Which, Sum(PlanningParts) Value FROM Planning GROUP BY Date, Machine, Product
   UNION ALL SELECT Date, Machine, Product, 'OKParts', Sum(OKParts) FROM OKParts GROUP BY Date, Machine, Product
   UNION ALL SELECT Date, Machine, Product, 'ScrapParts', Sum(ScrapParts) FROM Scrap GROUP BY Date, Machine, Product
   UNION ALL SELECT Date, Machine, Product, 'TrialParts', Sum(TrialParts) FROM Trials GROUP BY Date, Machine, Product
   UNION ALL SELECT Date, Machine, Product, 'Minutes', Sum(Minutes) FROM Breakdowns GROUP BY Date, Machine, Product
)
SELECT
   Date, Machine, Product,
   Sum(CASE Which WHEN 'PlanningParts' THEN Value END) PlanningParts,
   Sum(CASE Which WHEN 'OKParts' THEN Value END) OKParts,
   Sum(CASE Which WHEN 'ScrapParts' THEN Value END) ScrapParts,
   Sum(CASE Which WHEN 'TrialParts' THEN Value END) TrialParts,
   Sum(CASE Which WHEN 'Minutes' THEN Value END) Minutes
FROM Vals;

将文本 Which 值切换为整数可能会提高速度。

关于我对表的评论/问题,该表具有您表中的日期、机器、产品值的不同列表,此查询将提供这样的列表。这不会很好,但它应该给你的想法。

WITH DistinctKeys AS ( --wishing we had this as a real table
   SELECT Date, Machine, Product FROM Planning
   UNION SELECT Date, Machine, Product FROM OKParts
   UNION SELECT Date, Machine, Product FROM Scrap
   UNION SELECT Date, Machine, Product FROM Trials
   UNION SELECT Date, Machine, Product FROM Breakdown
) -- because then we could do this:
SELECT
   K.Date, K.Machine, K.Part,
   (SELECT Sum(PlanningParts) FROM Planning X ON EXISTS (SELECT K.* INTERSECT SELECT X.Date, X.Machine, X.Product) PlanningParts
   (SELECT Sum(OKParts) FROM OKParts X ON EXISTS (SELECT K.* INTERSECT SELECT X.Date, X.Machine, X.Product) OKParts
   (SELECT Sum(ScrapParts) FROM Scrap X ON EXISTS (SELECT K.* INTERSECT SELECT X.Date, X.Machine, X.Product) Scrap
   (SELECT Sum(TrialParts) FROM Trials X ON EXISTS (SELECT K.* INTERSECT SELECT X.Date, X.Machine, X.Product) TrialParts
   (SELECT Sum(Minutes) FROM Breakdown X ON EXISTS (SELECT K.* INTERSECT SELECT X.Date, X.Machine, X.Product) Minutes
FROM
   DistinctKeys K;

但坦率地说,JOIN 的性能不如之前在我和 @jaypeagi 的答案中给出的 UNION。

还有一件事:不要假设查询的性能如何。甚至专家也会检查执行计划并收集真实的 IO 和 CPU 统计信息来确定这些信息。您对 NULL 会降低性能的担忧可能完全没有根据。

如果您在 [Date, Machine, Product] 上的表上有良好的索引,那么您可能会从 UNION 方法中获得相当好的性能。

【讨论】:

  • 更好!,不知道关键字“PIVOT”。但我还是喜欢看脏的。您知道如何使模型更好吗?我评论说,其中几个表有更多的列。示例:Scrap and Breakdowns 表多一列“原因”,因此您可以拥有: Scrap: 1/6/12 |鼓风机 |坦克 | 5 |体重不足 2012 年 1 月 6 日 |鼓风机 |坦克 | 3 |体重偏高
  • 如果您使用超类型/子类型模式,其中您的表具有不同的列,即使它们基本上代表相同种类的事物,那么您的数据库结构可能是好的。如果列的数量并没有真正那么有太大的不同,那么将所有内容放在同一个表中可能仍然是最好的,当它们不适用于项目类型时将列保留为 NULL。
  • @leinho 您是否有一个表格,其中包含将在这 5 个表格中找到的日期、机器和产品值的不同列表?
  • 对不起,我不明白这个问题。没有唯一的列。并且在五个表中 Date 的值可以是任何日期,Machine 和 Product 是五个表中相同的 JOIN 到 Machines.ID AND Products.ID (Other two tables) 。对不起我的英语
  • @leinho - 我们想知道如何获取您的数据中存在的 {Date,Machine,Prodcut} 的列表。您说每个表可能有 0 条记录存在于另一个表中的 {Date,Machine,Product}。在这种情况下,正常做法是有一个 Dimension Table,其中每个 {Date,Machine 都有 一个 记录,产品}。这可以使查询和约束更容易实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
  • 2019-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多