经过多次搜索,我找不到如何做到这一点。
我的网络搜索得到 Pivots 和 Concats 以及 Cases 和 Subqueries 等,但没有一个能很好地解决我的问题。多行到单行的问题对我没有帮助。
问题:
一张桌子上有个人。另一个表中有这些个人的地址(有时是多个)。我需要一个查询来将每个个人的多个地址放在一行(在适当的列中)。
这是一个带有表和查询的 MySQL Fiddle:
在那个 SQL Fiddle 中,结果有 6 个独特个体的 9 条记录:
Number | Name | EyeColor | HairColor | Street | City | State | Zip | Street2 | City2 | State2| Zip2 | Street3 | ...
1 | John Smith | blue | red | 100 Pine Street | New York | NY | 10019 | | | | 0 | | ...
2 | Nancy Jones | green | red | 200 Pine Street | New York | NY | 10018 | | | | 0 | | ...
3 | Bobby Joe | blue | black | 310 Oak Street | New York | NY | 10018 | | | | 0 | | ...
7 | Little Lebowski | green | blond | 100 Apple Street | New York | NY | 10018 | | | | 0 | | ...
7 | Little Lebowski | green | blond | 200 Hickory Street | New York | NY | 10018 | | | | 0 | | ...
7 | Little Lebowski | green | blond | 1234 Pineapple Street | New York | NY | 10018 | | | | 0 | | ...
2 | Nancy Jones | green | red | 230 Golden Street | New York | NY | 10018 | | | | 0 | | ...
8 | Sarah Shepard | brown | brown | (null) | (null) | (null) | (null)| (null) | (null)| (null)| (null)| (null) | ...
Run Code Online (Sandbox Code Playgroud)
这是我需要最终结果的 SQL Fiddle:
最终结果 SQL Fiddle 总共有 6 个记录,用于 6 个唯一的个人及其在个人行的列中的多个地址:
Number | Name | EyeColor | HairColor | Street | City | State | Zip | Street2 | City2 | State2| Zip2 | Street3 | City3 | State3 | Zip3 | Street4 | City4 | State4 | Zip4 | Street5 | City5 | State5 | Zip5 | Street6 | City6 | State6 | Zip6
1 | John Smith | blue | red | 100 Pine Street | New York | NY | 10019 | | | | 0 | | | | 0 | | | | 0 | | | | 0 | | | | 0
2 | Nancy Jones | green | red | 200 Pine Street | New York | NY | 10018 | 230 Golden Street | New York | NY | 10018 | | | | 0 | | | | 0 | | | | 0 | | | | 0
3 | Bobby Joe | blue | black | 310 Oak Street | New York | NY | 10018 | | | | 0 | | | | 0 | | | | 0 | | | | 0 | | | | 0
7 | Little Lebowski | green | blond | 100 Apple Street | New York | NY | 10018 | 200 Hickory Street| New York | NY | 10018 | 1234 Pineapple Street | New York | NY | 10018 | | | | 0 | | | | 0 | | | | 0
8 | Sarah Shepard | brown | brown | | | | 0 | | | | 0 | | | | 0 | | | | 0 | | | | 0 | | | | 0
9 | Joe Profigliani | brown | brown | | | | 0 | | | | 0 | | | | 0 | | | | 0 | | | | 0 | | | | 0
Run Code Online (Sandbox Code Playgroud)
顺便说一下,我正在使用我得到的 MySQL 表,尽管对为解决方案构建临时表的方法持开放态度,但问题是如何组合最终结果中显示的数据,而不是关于原始表。
可能有数千条记录,尽管我不希望任何个人拥有超过 6 个地址。(如果可用的地址字段多的话,让它优雅地失败会很方便,但这不是问题的核心。)
我希望这是一些简单的事情,我只是没有提出正确的问题。
您如何决定哪种(姓名、地址)组合是“正确的”组合?
好吧,我认为这可能是我问题的前 50%。所有地址都是“正确的”,因为它们都被写入个人的行。例如,IndividualNumber 7 是 LittleLebowski,他有 3 个地址,所有三个地址都应出现在最终结果中该个人的行中。我的假设是第一个“出现”的地址作为第一个地址。(具有自动递增值的临时表?)一旦我回答了有关记录顺序的问题,那么另外 50% 的问题就是将它们写入该个人行上的相应列?
该问题可以分为 3 个主要操作:
IndividualNumber
(使用变量)对行进行分区NULL
您可以在此处查看示例:SQL Fiddle
IndividualNumber
:此查询的行为类似于 Oracle (>= 10g)、PostgreSQL (>= 8.4) 和 SQL Server (>= 2012) 中可用的 ROW_NUMBER() 窗口函数。
MySQL 没有实现它,它必须使用变量和CASE
语句来完成:
SELECT @row := CASE WHEN inf.IndividualNumber = @id
THEN @row + 1 ELSE 1 END as row
, @id := inf.IndividualNumber as IndividualNumber
, inf.IndividualAddressStreet
, inf.IndividualCity
, inf.IndividualState
, inf.IndividualZip
FROM (SELECT @row := 0, @id := 0) v
, InfoaboutThemTable as inf
ORDER BY inf.IndividualNumber
Run Code Online (Sandbox Code Playgroud)
它row
为 中的每个IndividualNumber
(分区)返回一个从 1 到 n的唯一值InfoaboutThemTable
:
row IndividualNumber IndividualAddressStreet IndividualCity IndividualState IndividualZip
1 1 100 Pine Street New York NY 10019
1 2 200 Pine Street New York NY 10018
2 2 201 Pine Street New York NY 10018
3 2 230 Golden Street New York NY 10018
4 2 456 Golden Street New York NY 10018
1 3 310 Oak Street New York NY 10018
1 7 100 Apple Street New York NY 10018
2 7 200 Hickory Street New York NY 10018
3 7 1234 Pineapple Street New York NY 10018
Run Code Online (Sandbox Code Playgroud)
因为您还不知道如何订购Address1
, Address2
, ... 它仅用于ORDER BY inf.IndividualNumber
按IndividualNumber
无特定顺序进行分区。
ORDER BY inf.IndividualNumber, inf.IndividualAddressStreet
如果您要分区IndividualNumber
并为分区的成员提供编号,则可以替换为IndividualAddressStreet
。
一旦每个分区的每一行IndividualNumber
都有一个唯一的row
值,它就可以用于将行转置/旋转到列。
MySQL 没有实现该PIVOT
运算符。数据可以使用 a 移动和旋转到 XGROUP BY d.IndividualNumber
列,对于每个转置的列, aCASE WHEN row = X THEN ... END
与聚合 ( MAX
) 一起使用:
MAX(CASE WHEN row = 1 THEN d.IndividualAddressStreet END) AS Street1
MAX(CASE WHEN row = 1 THEN d.IndividualCity END) AS City1
...
MAX(CASE WHEN row = 2 THEN d.IndividualAddressStreet END) AS Street2
...
Run Code Online (Sandbox Code Playgroud)
它只包含 3 个组,但您可以轻松地将其扩展到 6 个或 N 个组。
最后IndividualsTable
是LEFT JOIN
旋转子查询,以便将名称和颜色添加到所需的输出。
NULL
使用 将值替换为空字符串COALESCE
。
SELECT idt.IndividualNumber
, idt.Name
, idt.IndividualEyeColor
, idt.IndividualHairColor
, COALESCE(grp.Street1, '') as Street1
, COALESCE(grp.City1, '') as City1
, COALESCE(grp.State1, '') as State1
, COALESCE(grp.Zip1, '') as Zip1
, COALESCE(grp.Street2, '') as Street2
, COALESCE(grp.City2, '') as City2
, COALESCE(grp.State2, '') as State2
, COALESCE(grp.Zip2, '') as Zip2
, COALESCE(grp.Street3, '') as Street3
, COALESCE(grp.City3, '') as City3
, COALESCE(grp.State3, '') as State3
, COALESCE(grp.Zip3, '') as Zip3
FROM IndividualsTable idt
LEFT JOIN (
SELECT d.IndividualNumber as IndividualNumber
, MAX(CASE WHEN row = 1 THEN d.IndividualAddressStreet END) AS Street1
, MAX(CASE WHEN row = 1 THEN d.IndividualCity END) AS City1
, MAX(CASE WHEN row = 1 THEN d.IndividualState END) AS State1
, MAX(CASE WHEN row = 1 THEN d.IndividualZip END) AS Zip1
, MAX(CASE WHEN row = 2 THEN d.IndividualAddressStreet END) AS Street2
, MAX(CASE WHEN row = 2 THEN d.IndividualCity END) AS City2
, MAX(CASE WHEN row = 2 THEN d.IndividualState END) AS State2
, MAX(CASE WHEN row = 2 THEN d.IndividualZip END) AS Zip2
, MAX(CASE WHEN row = 3 THEN d.IndividualAddressStreet END) AS Street3
, MAX(CASE WHEN row = 3 THEN d.IndividualCity END) AS City3
, MAX(CASE WHEN row = 3 THEN d.IndividualState END) AS State3
, MAX(CASE WHEN row = 3 THEN d.IndividualZip END) AS Zip3
FROM
(
SELECT @row := CASE WHEN inf.IndividualNumber = @id
THEN @row + 1 ELSE 1 END as row
, @id := inf.IndividualNumber as IndividualNumber
, inf.IndividualAddressStreet
, inf.IndividualCity
, inf.IndividualState
, inf.IndividualZip
FROM (SELECT @row := 0, @id := 0) v
, InfoaboutThemTable as inf
ORDER BY inf.IndividualNumber
) d
GROUP BY d.IndividualNumber
) grp
ON grp.IndividualNumber = idt.IndividualNumber
;
Run Code Online (Sandbox Code Playgroud)