如何将多列“爆破”(反规范化/合并)成一列?
Posted
技术标签:
【中文标题】如何将多列“爆破”(反规范化/合并)成一列?【英文标题】:How to "Implode" (de-normalize/concat) multiple columns into a single column? 【发布时间】:2011-12-26 11:11:33 【问题描述】:我有一个查询,它输出如下内容:
+-------+----+--------------+
| F_KEY | EV | OTHER_COLUMN |
+-------+----+--------------+
| 100 | 1 | ... |
| 100 | 2 | ... |
| 150 | 2 | ... |
| 100 | 3 | ... |
| 150 | 4 | ... |
+-------+----+--------------+
我确信我已经看到了一个聚合函数,它将它(使用GROUP BY F_KEY
)变成了这样的东西:
+-------+------------+--------------+
| F_KEY | ? | OTHER_COLUMN |
+-------+------------+--------------+
| 100 | (1, 2, 3) | ... |
| 150 | (2, 4) | ... |
+-------+------------+--------------+
意味着,它以某种方式将EV
的值“内爆”到一个字段中。我怎样才能做到这一点?不幸的是,我不记得函数的名称了。
我正在使用 SQL Server。
这是我的查询的简化:
SELECT
F_KEY,
EV,
OTHER_COLUMN
FROM
TABLE1
JOIN
TABLE2 ON F_KEY = TABLE2.ID
WHERE
EVENT_TIME BETWEEN '2011-01-01 00:00:00.000' AND '2011-12-31 23:59:59.999'
ORDER BY
EVENT_TIME ASC
任何想法都值得赞赏!
【问题讨论】:
遗憾的是,SQL Server 中没有内置这样的功能。 PIVOT 操作符的行为是这样的,但每个值最终会出现在自己的列中,而不是连接在一起。您需要编写一个相当复杂的函数来执行您所描述的内容。 @Philip Kelley Aww,最多只有一两个光标;-) arr... 我知道它存在。但不是在 SQL Server 中......它在 mysql 中并称为GROUP_CONCAT
,它在 SQL Server 中不存在。必须找到解决方法=(
确实,只是一个游标和插入/附加到临时表。不幸的是,我从来没有写过游标,所以我不能写出来:D
Simulating group_concat MySQL function in MS SQL Server 2005? 的可能重复项(以及 很多 个其他项)
【参考方案1】:
您可能可以使用here 中描述的技术。 (完全公开:那是我的博客。)它谈到在 T-SQL 中生成连接字符串,而不使用游标。
【讨论】:
这很聪明也很简单:ISNULL(str1 + ' ', '') + ISNULL(str2 + ' ', '') + ISNULL(str3, '')【参考方案2】:这里是最好的连接方法,它不会像其他 XML 方法那样扩展特殊字符:
--Concatenation with FOR XML & eliminating control/encoded char expansion "& < >"
set nocount on;
declare @YourTable table (RowID int, HeaderValue int, ChildValue varchar(5))
insert into @YourTable VALUES (1,1,'CCC')
insert into @YourTable VALUES (2,2,'B<&>B')
insert into @YourTable VALUES (3,2,'AAA')
insert into @YourTable VALUES (4,3,'<br>')
insert into @YourTable VALUES (5,3,'A & Z')
set nocount off
SELECT
t1.HeaderValue
,STUFF(
(SELECT
', ' + t2.ChildValue
FROM @YourTable t2
WHERE t1.HeaderValue=t2.HeaderValue
ORDER BY t2.ChildValue
FOR XML PATH(''), TYPE
).value('.','varchar(max)')
,1,2, ''
) AS ChildValues
FROM @YourTable t1
GROUP BY t1.HeaderValue
输出:
HeaderValue ChildValues
----------- ---------------
1 CCC
2 AAA, B<&>B
3 <br>, A & Z
(3 row(s) affected)
【讨论】:
【参考方案3】:您可以使用以下函数轻松完成: 假设您将一直搜索相同的列/表。如果您希望能够改变列/表,则需要动态 SQL
CREATE FUNCTION [dbo].[fn_recursion]
(@F_KEY int)
RETURNS varchar(2000) AS
BEGIN
DECLARE @ReturnVal Varchar(2000)
SELECT @ReturnVal = COALESCE(@ReturnVal + ', ', '') + EV
FROM TABLE2
WHERE @F_KEY = @F_KEY
RETURN ISNULL(@ReturnVal,'')
END
GO
SELECT
F_KEY,
EV = [dbo].[fn_recursion](F_KEY),
OTHER_COLUMN
FROM
TABLE1
JOIN
TABLE2 ON F_KEY = TABLE2.ID
WHERE
EVENT_TIME BETWEEN '2011-01-01 00:00:00.000' AND '2011-12-31 23:59:59.999'
ORDER BY
EVENT_TIME ASC
GO
DROP FUNCTION [dbo].[fn_recursion]
GO
【讨论】:
【参考方案4】:查看使用 FOR XML PATH,您可以像 varchar 一样使用 xml 字符串转换它,这也支持分隔字符/表达式。它会在您的查询结束时执行,但很可能需要您使用子查询结构。
【讨论】:
以上是关于如何将多列“爆破”(反规范化/合并)成一列?的主要内容,如果未能解决你的问题,请参考以下文章