【问题标题】:MYSQL Alternative to UNION for same table reusing same columns selected as new nameMYSQL 替代 UNION 的相同表重用选择作为新名称的相同列
【发布时间】:2022-01-19 19:22:08
【问题描述】:

我正在尝试从具有有效唯一/主键的表生成结果集,例如 billyear、billmonth 和 type 以及成本和消耗。因此可能有 3 个账单年份和账单月份相同的条目,但类型可能是以下三个值之一:E、W 或 NG。

我需要创建一个每个账单年和账单月条目只有一行的结果集。

(
select month as billmonth, year as billyear, cost_estimate as eleccost, consumption_estimate as eleccons from tblbillforecast where buildingid=19 and type='E'
)
UNION  (
select month as billmonth, year as billyear, cost_estimate as gascost, consumption_estimate as gascons from tblbillforecast where buildingid=19 and type='NG'
)
UNION  (
select month as billmonth, year as billyear, cost_estimate as watercost, consumption_estimate as watercons from tblbillforecast where buildingid=19 and type='W'
)

这会生成一个仅包含 billmonth、billyear、eleccost 和 eleccons 列的结果集。我尝试了各种解决方案,但上面的示例最简单地说明了哪里出了问题。

此外,每个账单月/账单年的唯一组合仍然有 3 行,而不是合并为一个。

更新: 样本数据

SELECT month AS billmonth,
  year AS billyear,
  SUM(CASE type WHEN 'E' THEN cost_estimate END) AS eleccost,
  SUM(CASE type WHEN 'NG' THEN cost_estimate END) AS gascost,
  SUM(CASE type WHEN 'W' THEN cost_estimate END) AS watercost
FROM tblbillforecast
WHERE buildingid=19
GROUP BY billmonth, billyear;

结果:

预期结果,例如:

年 |月 |电费 |煤气费|水费

2018 | 1 | 32800 | 4460 | 4750

【问题讨论】:

标签: mysql sql


【解决方案1】:

这是正确的。 SQL 查询结果集的每列有一个名称,该名称适用于所有行。因此,如果您尝试在 UNION 的第二个或后续查询中重命名该列,这些新名称将被忽略。列名仅由 UNION 的第一个查询决定。

此外,每个账单月/账单年的唯一组合仍然有 3 行,而不是合并为一个。

根据您尝试的查询,这也是正确的行为。 UNION 不会将多行合并为一个,它只会追加行集。

正如 Akina 在上面的 cmets 中所暗示的,您可以使用多个列:

SELECT month AS billmonth,
  year AS billyear,
  SUM(CASE type WHEN 'E' THEN cost_estimate END) AS eleccost,
  SUM(CASE type WHEN 'NG' THEN cost_estimate END) AS gascost,
  SUM(CASE type WHEN 'W' THEN cost_estimate END) AS watercost
FROM tblbillforecast
WHERE buildingid=19
GROUP BY billmonth, billyear;

这使用GROUP BY 将行“合并”在一起,因此您每月/每年在结果中得到一行。

【讨论】:

  • 这更接近了!我仍然坚持使用 SUM。我编辑了我的问题以包含示例数据和使用您的示例的结果。我尝试删除 SUM(分组忽略 NG 和 W 全部为空)并添加限制 1,但这是不允许的。
  • 每栋楼/年/月表中有多少行?比尔的查询是正确的,但如果您有多个特定建筑物/年/月组合的行(您没有向我们展示),那么 SUM 会将它们全部聚合。如果你不明白我在问什么,请告诉我们select * from tblbillforecast where buildingid = 19 and billyear = 2018 and billmonth = 1的结果
  • 你能显示SELECT * FROM tblbillforecast WHERE buildingid=19 AND year=2018 AND month=1; 的结果吗?我怀疑你的表有不止一行符合这些条件。 (与 Ca​​ius Jard 的评论编辑同时发布)
  • 是的,我在上次编辑中展示了示例数据。每年+每月将有 3 行,E、NG、W 三种类型中的每一种。主键是年/月/类型
【解决方案2】:

关于 SQL 中各种数据整形操作的快速指南:

  • JOIN - 通过将表/结果集并排组合在一起,生成包含两个输入列集的所有列的输出行,从而使结果集更宽(更多列)
  • SELECT - 通常通过允许您指定您感兴趣和不感兴趣的列来缩小结果集;如果不提及可用列,它就会消失,这意味着您输出的列更少
  • UNION - 通过汇集结果集并在另一个之上输出一个结果集,使结果集更高(更多行)。由于列始终具有固定的数据类型和一个名称,因此您必须具有相同的列数、类型和顺序
  • WHERE - 通过允许您指定排除行的基于真值的过滤器,使结果集更短(行更少)

这并不难;您也可以使用 select 来创建更多列,但只是在非常基本的意义上,这些概念是正确的 - JOIN 用于加宽,UNION 用于更高,SELECT 用于更窄,WHERE 用于更短。您使用 SQL 所做的所有工作都是数据塑造练习;您要么在垂直或水平方向(或混合方向)上削减或扩展矩形数据块。

我不打算进行分组,因为这会混淆行,并且不是你在问题中尝试过的东西。我写这个的原因纯粹是因为你试图使用 UNION(高度-increasing) 操作,当你真正想要一个 widen 时,不管它是如何完成的(JOIN 或根据比尔的回答一个 SELECT+GROUP,它是有效的,但依赖于“混合行”分组方面),特别是没有使用 UNION。联合只会让东西变得更高。

举个例子,说明如何以 Bill 的方法的另一种方法来完成,你的这个任务有一个“太高”的大表 - 它使用 3 行,如果它是一个 1 就可以了有点宽。也就是说,如果只有 3 列用于电/气/水,那么我们就不需要 3 行,每行 1 个实用程序。

当然,我们有这个“每行一个实用程序”,因为它非常灵活。数据库表没有不同数量的列,但它们确实有不同数量的行。如果明天出现新的账单类型 - 互联网 - 不需要更改表格来适应它;添加一个新类型 I,然后离开,添加另一行。我们现在存储 1 个实用程序的 4 行,其中 1 行 4 列可以,但关键是我们不必更改表结构。我们可以有无限种不同的账单,而不需要无限的列,因为我们已经可以有无限的行

因此,您希望将数据从 4 行 x 1 列重塑为 1 行 x 4 列。它可以解决为:

  • 将表格缩小为年、月、建筑、类型、成本,并将其缩短为电力
  • 将表格单独缩小为年、月、建筑、类型、成本,并将其缩短为汽油
  • 将表格单独缩小为年、月、建筑、类型、成本,并将其缩短为水
  • 加入(扩大)所有这些新创建的结果集,然后缩小以删除重复的年、月、建筑物、类型列

看起来像:

SELECT e.year, e.month, e.building, e.cost, g.cost, w.cost 
FROM
  (SELECT year,month,building,cost FROM t WHERE type = 'E') e

  JOIN
  (SELECT year,month,building,cost FROM t WHERE type = 'NG') g
  ON
    e.year = g.year AND e.month = g.month AND e.building = g.building
  
  JOIN
  (SELECT year,month,building,cost FROM t WHERE type = 'W') w
  ON
    e.year = w.year AND e.month = w.month AND e.building = w.building

WHERE
  e.building = 19

您可以清楚地看到 3 种缩小和缩短操作,分别选择“仅汽油”、“仅电动”和“仅水” - 它们是 (SELECT year,month,building,cost FROM t WHERE type = 'NG'),这就是降低高度的原因原始表的长度,使其比每种情况下短三倍。如果我们在大表中有 999 行 X 5 列,它会变成 3 组,每组 333 x 5 行

您可以看到,然后我们将它们连接在一起以扩大结果 - 例如,我们的 3 组 333 x 5 行,每行在 JOIN 时扩大到 333 x 15..

然后在选择丢弃重复列时从 333x15 下降到 333 X 7

它可能并不完美(我可能会将所有 3 个加入到第 4 组数字中,以防某些公用事业在特定月份不存在),也许有些人会抱怨它的性能较低,因为它击中了表 3 次。所有这些都是我所说的关于 SQL 是重塑数据的练习的附属品 - 表是数据的起始块,然后你将它们切割得越来越窄,然后将它们并排或彼此叠放在一起,这将成为您的新数据块,可能更宽,更高,两者都......无论如何,它绝对是与您开始时不同的形状。然后你可以一次又一次地切割和塑造..

使用 Bill 的条件 agg(尽管如果每栋建筑/年/月有一行,这种方式会很好)但会带走关于这些常见操作(SELECT/ JOIN/WHERE/UNION) 重塑您的数据


关于 Bill 的条件聚合的脚注(我知道我说过我不会谈论它,但现在可能更有意义)。如果你有:

Type, Cost
E, 123
NG, 456
W, 789

然后你做了一个

SELECT 
  CASE WHEN Type = 'E' THEN Cost END as CostE,
  CASE WHEN Type = 'NG' THEN Cost END as CostG,
  CASE WHEN Type = 'W' THEN Cost END as CostW
...

它将数据分散到更多列 - 数据“从垂直变为对角线”

CostE, CostNG, CostW
123, NULL, NULL
NULL, 456, NULL
NULL, NULL, 789

但它仍然太高了。如果您随后运行 GROUP BY,它混合行并要求例如只是每列的 MAX,然后所有 NULL 将消失(因为列中的某处有一个非 null,如果有一个非 null,NULL 就会丢失,无论你在做什么)并且行折叠,混合合而为一:

CostE, CostNG, CostW
123, 456, 789

数据已从垂直转为水平 - 另一个数据整形。它被拉得更宽,压得更平

【讨论】:

  • 很好的建议/教训!为了确保包含所有年+月组合,我从 SELECT(联合所有表)LEFT JOIN 开始到上面的主要答案。
  • 对我来说很有意义; select distinct year, month from t 然后离开加入其中,因为它保证具有所有值的变化:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
  • 1970-01-01
  • 2020-01-03
  • 1970-01-01
  • 1970-01-01
  • 2019-02-10
相关资源
最近更新 更多