Ben*_*cka 14 sql-server-2005 sql-server query
customer_comments由于数据库设计,我将一些拆分为多行,对于报告,我需要将comments每个唯一的数据id合并为一行。我以前尝试过使用SELECT 子句和 COALESCE技巧中的此分隔列表进行一些操作,但我不记得它并且一定没有保存它。在这种情况下,我似乎也无法让它工作,似乎只能在一行上工作。
数据如下所示:
id row_num customer_code comments
-----------------------------------
1 1 Dilbert Hard
1 2 Dilbert Worker
2 1 Wally Lazy
Run Code Online (Sandbox Code Playgroud)
我的结果需要如下所示:
id customer_code comments
------------------------------
1 Dilbert Hard Worker
2 Wally Lazy
Run Code Online (Sandbox Code Playgroud)
所以对于每row_num一个实际上只有一行结果;注释应按 的顺序组合row_num。上面链接的SELECT技巧可用于将特定查询的所有值作为一行获取,但我无法弄清楚如何使其作为SELECT将所有这些行吐出的语句的一部分工作。
我的查询必须自己遍历整个表并输出这些行。我没有将它们组合成多列,每一行一个,所以PIVOT似乎不适用。
Aar*_*and 18
这对于相关子查询来说相对简单。您不能使用您提到的博客文章中突出显示的 COALESCE 方法,除非您将其提取到用户定义的函数中(或者除非您一次只想返回一行)。以下是我通常这样做的方式:
DECLARE @x TABLE
(
id INT,
row_num INT,
customer_code VARCHAR(32),
comments VARCHAR(32)
);
INSERT @x SELECT 1,1,'Dilbert','Hard'
UNION ALL SELECT 1,2,'Dilbert','Worker'
UNION ALL SELECT 2,1,'Wally','Lazy';
SELECT id, customer_code, comments = STUFF((SELECT ' ' + comments
FROM @x AS x2 WHERE id = x.id
ORDER BY row_num
FOR XML PATH('')), 1, 1, '')
FROM @x AS x
GROUP BY id, customer_code
ORDER BY id;
Run Code Online (Sandbox Code Playgroud)
如果您遇到注释中的数据可能包含对 XML 不安全的字符 ( >, <, &) 的情况,您应该更改以下内容:
FOR XML PATH('')), 1, 1, '')
Run Code Online (Sandbox Code Playgroud)
对于这种更精细的方法:
FOR XML PATH(''), TYPE).value(N'(./text())[1]', N'varchar(max)'), 1, 1, '')
Run Code Online (Sandbox Code Playgroud)
(确保使用正确的目标数据类型,varchar或nvarchar,和正确的长度,N如果使用,则在所有字符串文字前加上前缀nvarchar。)
如果您被允许在您的环境中使用 CLR,那么这是为用户定义的聚合量身定制的案例。
特别是,如果源数据非常大和/或您需要在应用程序中大量执行此类操作,这可能是一种可行的方法。我强烈怀疑Aaron 解决方案的查询计划不会随着输入大小的增长而很好地扩展。(我尝试向临时表添加索引,但这没有帮助。)
这个解决方案,像许多其他事情一样,是一种权衡:
编辑:嗯,我去尝试看看这是否真的更好,结果表明当前无法使用聚合函数满足评论按特定顺序排列的要求。:(
请参阅SqlUserDefinedAggregateAttribute.IsInvariantToOrder。基本上,你需要做的是什么OVER(PARTITION BY customer_code ORDER BY row_num),但ORDER BY中不支持OVER子句汇总时。我假设将这个功能添加到 SQL Server 会打开一堆蠕虫,因为需要在执行计划中更改的内容是微不足道的。前面提到的链接说这是保留供将来使用,因此可以在将来实施(不过,在 2005 年,您可能不走运)。
这可能仍然通过打包和解析来实现row_num价值为聚集串,然后该干嘛CLR对象中的那种......这似乎是相当的hackish。
无论如何,下面是我使用的代码,以防其他人发现即使有限制也很有用。我将把黑客部分留给读者作为练习。请注意,我使用 AdventureWorks (2005) 作为测试数据。
聚合组装:
using System;
using System.IO;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
namespace MyCompany.SqlServer
{
[Serializable]
[SqlUserDefinedAggregate
(
Format.UserDefined,
IsNullIfEmpty = false,
IsInvariantToDuplicates = false,
IsInvariantToNulls = true,
IsInvariantToOrder = false,
MaxByteSize = -1
)]
public class StringConcatAggregate : IBinarySerialize
{
private string _accum;
private bool _isEmpty;
public void Init()
{
_accum = string.Empty;
_isEmpty = true;
}
public void Accumulate(SqlString value)
{
if (!value.IsNull)
{
if (!_isEmpty)
_accum += ' ';
else
_isEmpty = false;
_accum += value.Value;
}
}
public void Merge(StringConcatAggregate value)
{
Accumulate(value.Terminate());
}
public SqlString Terminate()
{
return new SqlString(_accum);
}
public void Read(BinaryReader r)
{
this.Init();
_accum = r.ReadString();
_isEmpty = _accum.Length == 0;
}
public void Write(BinaryWriter w)
{
w.Write(_accum);
}
}
}
Run Code Online (Sandbox Code Playgroud)
用于测试的 T-SQL(CREATE ASSEMBLY,并sp_configure省略启用 CLR):
CREATE TABLE [dbo].[Comments]
(
CustomerCode int NOT NULL,
RowNum int NOT NULL,
Comments nvarchar(25) NOT NULL
)
INSERT INTO [dbo].[Comments](CustomerCode, RowNum, Comments)
SELECT
DENSE_RANK() OVER(ORDER BY FirstName),
ROW_NUMBER() OVER(PARTITION BY FirstName ORDER BY ContactID),
Phone
FROM [AdventureWorks].[Person].[Contact]
GO
CREATE AGGREGATE [dbo].[StringConcatAggregate]
(
@input nvarchar(MAX)
)
RETURNS nvarchar(MAX)
EXTERNAL NAME StringConcatAggregate.[MyCompany.SqlServer.StringConcatAggregate]
GO
SELECT
CustomerCode,
[dbo].[StringConcatAggregate](Comments) AS AllComments
FROM [dbo].[Comments]
GROUP BY CustomerCode
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
167765 次 |
| 最近记录: |