如何使用 GROUP BY 以连接一列中的数据,但筛选另一列中的特定数据的方式

Nos*_*iki 4 sql-server group-by group-concatenation sql-server-2014

我正在运行 SQL Server 2014

我有一个看起来像这样的表:

 ID   | Name | AddressType | Address       | Features
 ========================================================
 1    | Bob  | Home        | 123 Nope St   | JP 
 2    | John | Work        | 555 Fake St   | MNGF 
 2    | John | Home        | 654 Madeup Ln | IMP JP 
 3    | Kim  | Work        | 92 Nadda Blvd | MP 
Run Code Online (Sandbox Code Playgroud)

我正在尝试编写一个 SQL Server 查询来查找重复的 ID,并始终返回包含“工作”地址的行,但我希望它将两行中的功能连接起来,以便得到如下所示的结果:

 ID   | Name | AddressType | Address       | Features
 ========================================================
 1    | Bob  | Home        | 123 Nope St   | JP 
 2    | John | Work        | 555 Fake St   | MNGF IMP JP 
 3    | Kim  | Work        | 92 Nadda Blvd | MP 
Run Code Online (Sandbox Code Playgroud)

我能弄清楚如何得到的最接近的看起来像这样:

SELECT ID, Name, MAX(AddressType), MAX(Address), CONCAT(Features) FROM 
  (SELECT * FROM myTable ORDER BY ID, AddressType DESC)
GROUP BY ID, NAME
Run Code Online (Sandbox Code Playgroud)

...但是 MAX(AddressType) 和 MAX(Address) 是分开拉取的,所以有时我得到工作地址,有时我得到家庭地址。我真正需要的是 MAX(AddressType) 以及与该结果位于同一行的任何地址。

我尝试过的另一件事是:

SELECT ID, Name, AddressType, Address, CONCAT(Features), COUNT(ID) OVER(PARTITION BY ID) AS IDcount
FROM myTable 
GROUP BY ID, NAME
WHERE AddressType = 'Work' OR IDcount = 1
Run Code Online (Sandbox Code Playgroud)

...这总是为我提供所有正确的地址,但过滤掉“Home”AddressType 的功能,以便我无法将它们连接起来。

Han*_*dyD 7

如果您使用的是 SQL Server 2017 或更高版本,则可以使用 STRING_AGG 函数来连接组内的值。

然后,您可以使用 CTE 单独获取功能列表以简化查询,然后将其连接回数据以获得所需的行以及每个 ID 的完整功能列表。

脚本

;WITH AddressFeatures AS 
(
  SELECT ID, STRING_AGG(Features, ' ') AS Features
  FROM Addresses
  GROUP BY ID
)

SELECT s1.ID, s1.Name, s1.AddressType, s1.Address, af.Features
FROM
(
  SELECT ID, Name, AddressType, Address
    , ROW_NUMBER() 
      OVER (PARTITION BY ID 
            ORDER BY 
            CASE WHEN AddressType = 'Work' THEN 1 ELSE 2 END) 
      AS AddressTypeOrder
  FROM Addresses
) s1
INNER JOIN AddressFeatures af ON af.ID = s1.ID
WHERE AddressTypeOrder = 1
Run Code Online (Sandbox Code Playgroud)

输出

ID 姓名 地址类型 地址 特征
1 鲍勃 123 诺普街 J.P
2 约翰 工作 第555章假圣 MNGF IMP 日本
3 工作 纳达大道92号 国会议员

您可以在此db<>fiddle中看到一个工作示例。

此方法的优点是,您可以使用多个地址类型值,并通过简单地将 WHEN 子句添加到内部 SELECT 语句中 ROW_NUMBER() 函数中的 CASE 语句来根据您所需的逻辑对它们进行排序。

更新:对于 SQL 2017 之前的版本,您可以使用 STUFF 和 FOR XML 的组合来复制 STRING_AGG 的行为:

;WITH AddressFeatures AS 
(
  SELECT ID, 
     (SELECT STUFF((
       SELECT ' ' + a2.[Features]
       FROM Addresses a2
       WHERE a1.ID = a2.ID
       FOR XML PATH ('')), 1, 1, '')) AS Features
  FROM Addresses a1
  GROUP BY ID
)

SELECT s1.ID, s1.Name, s1.AddressType, s1.Address, af.Features
FROM
(
  SELECT ID, Name, AddressType, Address
    , ROW_NUMBER() OVER 
      (PARTITION BY ID 
       ORDER BY CASE WHEN AddressType = 'Work' THEN 1 ELSE 2 END) 
      AS AddressTypeOrder
  FROM Addresses
) s1
INNER JOIN AddressFeatures af ON af.ID = s1.ID
WHERE AddressTypeOrder = 1
Run Code Online (Sandbox Code Playgroud)

请参阅更新的数据库<>摆弄此解决方案。从功能上讲,其行为相同,但性能可能会受到影响。