B.M*_*thy 9 xml sql-server sql-server-2008-r2 concat
我正在尝试查询两个表并获得如下结果:
Section Names
shoes AccountName1, AccountName2, AccountName3
books AccountName1
Run Code Online (Sandbox Code Playgroud)
这些表是:
CREATE TABLE dbo.TableA(ID INT, Section varchar(64), AccountId varchar(64));
INSERT dbo.TableA(ID, Section, AccountId) VALUES
(1 ,'shoes','A1'),
(2 ,'shoes','A2'),
(3 ,'shoes','A3'),
(4 ,'books','A1');
CREATE TABLE dbo.TableB(AccountId varchar(20), Name varchar(64));
INSERT dbo.TableB(AccountId, Name) VALUES
('A1','AccountName1'),
('A2','AccountName2'),
('A3','AccountNAme3');
Run Code Online (Sandbox Code Playgroud)
我看到一些回答说使用“XML PATH”和“STUFF”来查询数据以获得我正在寻找的结果,但我认为缺少一些东西。我尝试了以下查询并收到错误消息:
列“a.AccountId”在选择列表中无效,因为它既不包含在聚合函数中也不包含在 GROUP BY 子句中。
我在任一查询的 SELECT 子句中都没有它,但我认为错误是因为 AccountId 在 TableA 中不是唯一的。
这是我目前正在尝试正常工作的查询。
SELECT section, names= STUFF((
SELECT ', ' + Name FROM TableB as b
WHERE AccountId = b.AccountId
FOR XML PATH('')), 1, 1, '')
FROM TableA AS a
GROUP BY a.section
Run Code Online (Sandbox Code Playgroud)
Aar*_*and 15
抱歉,我错过了关系中的一步。试试这个版本(虽然Martin 的也能用):
SELECT DISTINCT o.section, names= STUFF((
SELECT ', ' + b.Name
FROM dbo.TableA AS a
INNER JOIN dbo.TableB AS b
ON a.AccountId = b.AccountId
WHERE a.Section = o.Section
FOR XML PATH, TYPE).value(N'.[1]', N'varchar(max)'), 1, 2, '')
FROM dbo.TableA AS o;
Run Code Online (Sandbox Code Playgroud)
一种至少同样好但有时更好的方法是从 切换DISTINCT到GROUP BY:
SELECT o.section, names= STUFF((
SELECT ', ' + b.Name
FROM dbo.TableA AS a
INNER JOIN dbo.TableB AS b
ON a.AccountId = b.AccountId
WHERE a.Section = o.Section
FOR XML PATH, TYPE).value(N'.[1]', N'varchar(max)'), 1, 2, '')
FROM dbo.TableA AS o
GROUP BY o.section;
Run Code Online (Sandbox Code Playgroud)
在较高级别,原因DISTINCT适用于整个列列表。因此,对于任何重复项,它必须在应用之前为每个重复项执行汇总工作DISTINCT。如果您使用,GROUP BY那么它可能会在执行任何聚合工作之前删除重复项。此行为可能因计划而异,具体取决于包括索引、计划策略等在内的各种因素。并且GROUP BY可能并非在所有情况下都可以直接切换到。
无论如何,我在SentryOne Plan Explorer 中运行了这两种变体。这些计划在一些次要的、无趣的方面有所不同,但与底层工作表相关的 I/O 是有说服力的。这是DISTINCT:
这里是GROUP BY:
当我将表做得更大(14,000 多行映射到 24 个潜在值)时,这种差异更加明显。DISTINCT:
GROUP BY:
在 SQL Server 2017 中,您可以使用STRING_AGG:
SELECT a.section, STRING_AGG(b.Name, ', ')
FROM dbo.TableA AS a
INNER JOIN dbo.TableB AS b
ON a.AccountId = b.AccountId
WHERE a.Section = a.Section
GROUP BY a.section;
Run Code Online (Sandbox Code Playgroud)
这里的 I/O 几乎没有:
但是,如果您不在 SQL Server 2017(或 Azure SQL 数据库)上,并且不能使用STRING_AGG,我必须在信用到期的地方给予信用......下面 Paul White 的回答很少有 I/O 并且很兴奋关闭FOR XML PATH上述两种解决方案。
这些帖子的其他改进:
另见:
Pau*_*ite 12
我想我会尝试使用 XML 的解决方案。
DECLARE @TableA AS table
(
ID integer PRIMARY KEY,
Section varchar(10) NOT NULL,
AccountID char(2) NOT NULL
);
DECLARE @TableB AS table
(
AccountID char(2) PRIMARY KEY,
Name varchar(20) NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
INSERT @TableA
(ID, Section, AccountID)
VALUES
(1, 'shoes', 'A1'),
(2, 'shoes', 'A2'),
(3, 'shoes', 'A3'),
(4, 'books', 'A1');
INSERT @TableB
(AccountID, Name)
VALUES
('A1', 'AccountName1'),
('A2', 'AccountName2'),
('A3', 'AccountName3');
Run Code Online (Sandbox Code Playgroud)
DECLARE @x xml =
(
SELECT
TA.Section,
CA.Name
FROM @TableA AS TA
JOIN @TableB AS TB
ON TB.AccountID = TA.AccountID
CROSS APPLY
(
VALUES(',' + TB.Name)
) AS CA (Name)
ORDER BY TA.Section
FOR XML AUTO, TYPE, ELEMENTS, ROOT ('Root')
);
Run Code Online (Sandbox Code Playgroud)
变量中的 XML 如下所示:
DECLARE @TableA AS table
(
ID integer PRIMARY KEY,
Section varchar(10) NOT NULL,
AccountID char(2) NOT NULL
);
DECLARE @TableB AS table
(
AccountID char(2) PRIMARY KEY,
Name varchar(20) NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
最后的查询将 XML 分解为多个部分,并连接每个部分中的名称:
SELECT
Section =
N.n.value('(./Section/text())[1]', 'varchar(10)'),
Names =
STUFF
(
-- Consecutive text nodes collapse
N.n.query('./CA/Name/text()')
.value('./text()[1]', 'varchar(8000)'),
1, 1, ''
)
-- Shred per section
FROM @x.nodes('Root/TA') AS N (n);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
66725 次 |
| 最近记录: |