Sql 从连接的字符串中删除重复值
我有下表:Sql 从连接的字符串中删除重复值,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我有下表: Object Field Values ------------------------------------ 1 1 A;A;A;B;A;A 2 1 A;B;C;C 2 2 X 3 1 X;Y;Z 3 2 V;V;V;V;V;V;V;V;V;V;V 如何在此表中仅从串联的值中选择唯一值?因此: Object Field Values --------------
Object Field Values
------------------------------------
1 1 A;A;A;B;A;A
2 1 A;B;C;C
2 2 X
3 1 X;Y;Z
3 2 V;V;V;V;V;V;V;V;V;V;V
如何在此表中仅从串联的值中选择唯一值?因此:
Object Field Values
---------------------
1 1 A;B
2 1 A;B;C
2 2 X
3 1 X;Y;Z
3 2 V
在任何脚本语言中,我都会循环使用值中的值,在上分解编码>并使用一些逻辑过滤掉重复项来循环该数组。但是,我只需要使用SQL(Server 2008)来完成这项工作
有谁能告诉我这是否可以以及如何做到
非常感谢您的帮助:-)要完成此操作,请先创建一个拆分函数。这是我使用的一个,但是如果您在internet上搜索(甚至如此)SQL Server Split Function(SQL Server拆分函数),如果您不喜欢,您会发现许多备选方案:
ALTER FUNCTION [dbo].[Split](@StringToSplit NVARCHAR(MAX), @Delimiter NCHAR(1))
RETURNS TABLE
AS
RETURN
(
SELECT ID = ROW_NUMBER() OVER(ORDER BY n.Number),
Position = Number,
Value = SUBSTRING(@StringToSplit, Number, CHARINDEX(@Delimiter, @StringToSplit + @Delimiter, Number) - Number)
FROM ( SELECT TOP (LEN(@StringToSplit) + 1) Number = ROW_NUMBER() OVER(ORDER BY a.object_id)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
) n
WHERE SUBSTRING(@Delimiter + @StringToSplit + @Delimiter, n.Number, 1) = @Delimiter
);
然后您可以拆分字段,以便运行:
SELECT t.Object, t.Field, s.Value
FROM T
CROSS APPLY dbo.Split(t.[Values], ';') AS s
将改变这一点:
Object Field Values
------------------------------------
1 1 A;A;A;B;A;A
进入:
然后可以应用DISTINCT
运算符:
SELECT DISTINCT t.Object, t.Field, s.Value
FROM T
CROSS APPLY dbo.Split(t.[Values], ';') AS s;
给予:
Object Field Values
------------------------------------
1 1 A
1 1 B
然后,您可以将行连接回一个列,给出最终查询:
SELECT t.Object, t.Field, [Values] = STUFF(x.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM T
CROSS APPLY
( SELECT DISTINCT ';' + s.Value
FROM dbo.Split(t.[Values], ';') AS s
FOR XML PATH(''), TYPE
) AS s (x)
SQL Fiddle似乎已关闭,但创建拆分函数后,下面是一个完整的工作示例:
CREATE TABLE #T (Object INT, Field INT, [Values] VARCHAR(MAX));
INSERT #T
VALUES
(1, 1, 'A;A;A;B;A;A'),
(2, 1, 'A;B;C;C'),
(2, 2, 'X'),
(3, 1, 'X;Y;Z'),
(3, 2, 'V;V;V;V;V;V;V;V;V;V;V');
SELECT t.Object, t.Field, [Values] = STUFF(x.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM #T AS T
CROSS APPLY
( SELECT DISTINCT ';' + s.Value
FROM dbo.Split(t.[Values], ';') AS s
FOR XML PATH(''), TYPE
) AS s (x);
编辑
根据您关于不能创建表或修改DDL的评论,我想我可以解释一下您也不能创建函数的情况。您可以将上述拆分函数扩展到查询中,因此实际上不需要创建函数:
CREATE TABLE #T (Object INT, Field INT, [Values] VARCHAR(MAX));
INSERT #T
VALUES
(1, 1, 'A;A;A;B;A;A'),
(2, 1, 'A;B;C;C'),
(2, 2, 'X'),
(3, 1, 'X;Y;Z'),
(3, 2, 'V;V;V;V;V;V;V;V;V;V;V');
SELECT t.Object,
t.Field,
[Values] = STUFF(x.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM #T AS T
CROSS APPLY
( SELECT DISTINCT ';' + SUBSTRING(t.[Values], Number, CHARINDEX(';', t.[Values] + ';', Number) - Number)
FROM ( SELECT TOP (LEN(t.[Values]) + 1) Number = ROW_NUMBER() OVER(ORDER BY a.object_id)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
) n
WHERE SUBSTRING(';' + t.[Values] + ';', n.Number, 1) = ';'
FOR XML PATH(''), TYPE
) AS s (x);
为此,首先创建一个拆分函数。这是我使用的一个,但是如果您在internet上搜索(甚至如此)SQL Server Split Function(SQL Server拆分函数),如果您不喜欢,您会发现许多备选方案:
ALTER FUNCTION [dbo].[Split](@StringToSplit NVARCHAR(MAX), @Delimiter NCHAR(1))
RETURNS TABLE
AS
RETURN
(
SELECT ID = ROW_NUMBER() OVER(ORDER BY n.Number),
Position = Number,
Value = SUBSTRING(@StringToSplit, Number, CHARINDEX(@Delimiter, @StringToSplit + @Delimiter, Number) - Number)
FROM ( SELECT TOP (LEN(@StringToSplit) + 1) Number = ROW_NUMBER() OVER(ORDER BY a.object_id)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
) n
WHERE SUBSTRING(@Delimiter + @StringToSplit + @Delimiter, n.Number, 1) = @Delimiter
);
然后您可以拆分字段,以便运行:
SELECT t.Object, t.Field, s.Value
FROM T
CROSS APPLY dbo.Split(t.[Values], ';') AS s
将改变这一点:
Object Field Values
------------------------------------
1 1 A;A;A;B;A;A
进入:
然后可以应用DISTINCT
运算符:
SELECT DISTINCT t.Object, t.Field, s.Value
FROM T
CROSS APPLY dbo.Split(t.[Values], ';') AS s;
给予:
Object Field Values
------------------------------------
1 1 A
1 1 B
然后,您可以将行连接回一个列,给出最终查询:
SELECT t.Object, t.Field, [Values] = STUFF(x.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM T
CROSS APPLY
( SELECT DISTINCT ';' + s.Value
FROM dbo.Split(t.[Values], ';') AS s
FOR XML PATH(''), TYPE
) AS s (x)
SQL Fiddle似乎已关闭,但创建拆分函数后,下面是一个完整的工作示例:
CREATE TABLE #T (Object INT, Field INT, [Values] VARCHAR(MAX));
INSERT #T
VALUES
(1, 1, 'A;A;A;B;A;A'),
(2, 1, 'A;B;C;C'),
(2, 2, 'X'),
(3, 1, 'X;Y;Z'),
(3, 2, 'V;V;V;V;V;V;V;V;V;V;V');
SELECT t.Object, t.Field, [Values] = STUFF(x.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM #T AS T
CROSS APPLY
( SELECT DISTINCT ';' + s.Value
FROM dbo.Split(t.[Values], ';') AS s
FOR XML PATH(''), TYPE
) AS s (x);
编辑
根据您关于不能创建表或修改DDL的评论,我想我可以解释一下您也不能创建函数的情况。您可以将上述拆分函数扩展到查询中,因此实际上不需要创建函数:
CREATE TABLE #T (Object INT, Field INT, [Values] VARCHAR(MAX));
INSERT #T
VALUES
(1, 1, 'A;A;A;B;A;A'),
(2, 1, 'A;B;C;C'),
(2, 2, 'X'),
(3, 1, 'X;Y;Z'),
(3, 2, 'V;V;V;V;V;V;V;V;V;V;V');
SELECT t.Object,
t.Field,
[Values] = STUFF(x.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM #T AS T
CROSS APPLY
( SELECT DISTINCT ';' + SUBSTRING(t.[Values], Number, CHARINDEX(';', t.[Values] + ';', Number) - Number)
FROM ( SELECT TOP (LEN(t.[Values]) + 1) Number = ROW_NUMBER() OVER(ORDER BY a.object_id)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
) n
WHERE SUBSTRING(';' + t.[Values] + ';', n.Number, 1) = ';'
FOR XML PATH(''), TYPE
) AS s (x);
就像没有CTE的标量值函数一样
ALTER FUNCTION [SplitRemoveDupes] (
@String VARCHAR(MAX)
,@Delimiter VARCHAR(5)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @SplitLength INT
DECLARE @DedupedValues VARCHAR(MAX)
DECLARE @SplittedValues TABLE
(
OccurenceId SMALLINT IDENTITY(1,1),
SplitValue VARCHAR(200)
)
WHILE LEN(@String) > 0
BEGIN
SELECT @SplitLength = (
CASE CHARINDEX(@Delimiter, @String)
WHEN 0
THEN LEN(@String)
ELSE CHARINDEX(@Delimiter, @String) - 1
END
)
INSERT INTO @SplittedValues
SELECT SUBSTRING(@String, 1, @SplitLength)
SELECT @String = (
CASE (LEN(@String) - @SplitLength)
WHEN 0
THEN ''
ELSE RIGHT(@String, LEN(@String) - @SplitLength - 1) END)
END
SET @DedupedValues=(SELECT DISTINCT STUFF((
SELECT DISTINCT (@Delimiter + SplitValue)
FROM @SplittedValues s
ORDER BY (@Delimiter + SplitValue)
FOR XML PATH('')
), 1, 1, '') AS a
FROM @SplittedValues ss)
RETURN @DedupedValues
END
称之为内联
SELECT Object, Field, [dbo].[SplitRemoveDupes](Values,';') From Table
就像没有CTE的标量值函数一样
ALTER FUNCTION [SplitRemoveDupes] (
@String VARCHAR(MAX)
,@Delimiter VARCHAR(5)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @SplitLength INT
DECLARE @DedupedValues VARCHAR(MAX)
DECLARE @SplittedValues TABLE
(
OccurenceId SMALLINT IDENTITY(1,1),
SplitValue VARCHAR(200)
)
WHILE LEN(@String) > 0
BEGIN
SELECT @SplitLength = (
CASE CHARINDEX(@Delimiter, @String)
WHEN 0
THEN LEN(@String)
ELSE CHARINDEX(@Delimiter, @String) - 1
END
)
INSERT INTO @SplittedValues
SELECT SUBSTRING(@String, 1, @SplitLength)
SELECT @String = (
CASE (LEN(@String) - @SplitLength)
WHEN 0
THEN ''
ELSE RIGHT(@String, LEN(@String) - @SplitLength - 1) END)
END
SET @DedupedValues=(SELECT DISTINCT STUFF((
SELECT DISTINCT (@Delimiter + SplitValue)
FROM @SplittedValues s
ORDER BY (@Delimiter + SplitValue)
FOR XML PATH('')
), 1, 1, '') AS a
FROM @SplittedValues ss)
RETURN @DedupedValues
END
称之为内联
SELECT Object, Field, [dbo].[SplitRemoveDupes](Values,';') From Table
以下是一个独立的解决方案:
DECLARE @t table(Object int, Field int, [Values] varchar(max))
INSERT @t values
(1, 1, 'A;A;A;B;A;A'),
(2, 1, 'A;B;C;C'),
(3, 1, 'X'),
(4, 1, 'X;Y;Z'),
(5, 1, 'V;V;V;V;V;V;V;V;V;V;V')
SELECT t.Object, t.Field, x.[NewValues]
FROM @t t
CROSS APPLY
(
SELECT STUFF((
SELECT distinct ';'+t.c.value('.', 'VARCHAR(2000)') value
FROM (
SELECT x = CAST('<t>' +
REPLACE([Values], ';', '</t><t>') + '</t>' AS XML)
) a
CROSS APPLY x.nodes('/t') t(c)
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [NewValues]
) x
根据@GarethD的评论,这可能执行缓慢
测试数据:
create table #t(Object int identity(1,1), Field int, [Values] varchar(max))
INSERT #t values
(1, 'A;A;A;B;A;A'),(1, 'A;B;C;C'), (1, 'X'), (1, 'X;Y;Z'),(1, 'V;V;V;V;V;V;V;V;V;V;V')
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
性能测试我的脚本:
SELECT t.Object, t.Field, x.[NewValues]
FROM #t t
CROSS APPLY
(
SELECT STUFF((
SELECT distinct ';'+t.c.value('.', 'VARCHAR(2000)') value
FROM (
SELECT x = CAST('<t>' +
REPLACE([Values], ';', '</t><t>') + '</t>' AS XML)
) a
CROSS APPLY x.nodes('/t') t(c)
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [NewValues]
) x
结果6秒
如果任何行的值为空,此脚本也将崩溃。这里有一个独立的解决方案:
DECLARE @t table(Object int, Field int, [Values] varchar(max))
INSERT @t values
(1, 1, 'A;A;A;B;A;A'),
(2, 1, 'A;B;C;C'),
(3, 1, 'X'),
(4, 1, 'X;Y;Z'),
(5, 1, 'V;V;V;V;V;V;V;V;V;V;V')
SELECT t.Object, t.Field, x.[NewValues]
FROM @t t
CROSS APPLY
(
SELECT STUFF((
SELECT distinct ';'+t.c.value('.', 'VARCHAR(2000)') value
FROM (
SELECT x = CAST('<t>' +
REPLACE([Values], ';', '</t><t>') + '</t>' AS XML)
) a
CROSS APPLY x.nodes('/t') t(c)
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [NewValues]
) x
根据@GarethD的评论,这可能执行缓慢
测试数据:
create table #t(Object int identity(1,1), Field int, [Values] varchar(max))
INSERT #t values
(1, 'A;A;A;B;A;A'),(1, 'A;B;C;C'), (1, 'X'), (1, 'X;Y;Z'),(1, 'V;V;V;V;V;V;V;V;V;V;V')
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
insert #t select field, [values] from #t union all select field, [values] from #t union all select field, [values] from #t
性能测试我的脚本:
SELECT t.Object, t.Field, x.[NewValues]
FROM #t t
CROSS APPLY
(
SELECT STUFF((
SELECT distinct ';'+t.c.value('.', 'VARCHAR(2000)') value
FROM (
SELECT x = CAST('<t>' +
REPLACE([Values], ';', '</t><t>') + '</t>' AS XML)
) a
CROSS APPLY x.nodes('/t') t(c)
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [NewValues]
) x
结果6秒
如果任何一行的值为空,这个脚本也会崩溃。你应该规范化你的表,不要有这样的数据。谢谢,但这是我必须处理的。这是我得到的输入,我对DB设计没有影响,也不能创建表。我建议使用标量函数。你需要编写SQL函数并传递值,该函数必须有代码来删除重复项。如何创建值?这可能是删除重复项的正确位置。你应该规范化你的表,不要有那样的数据。谢谢,但这是我必须处理的。这是我得到的输入,我对DB设计没有影响,也不能创建表。我建议使用标量函数。你需要编写SQL函数并传递值,该函数必须有代码来删除重复项。如何创建值?这可能是删除重复项的正确位置。这是有史以来最棒的答案。非常感谢:这是有史以来最棒的答案。多谢各位:D@Anon它必须有一个价值。我想varchar(max)可能会更好。这是一个很好的整洁解决方案,但也有可能,而且它不处理特殊的xml字符。i、 e试图拆分”>;>@谢谢你的评论。这可能会在特殊字符上失败。我比较了我们的脚本,发现了性能问题。但是,它不在我的脚本中。关于我的性能问题,你是对的,但这不是由于拆分函数。这是因为我一步一步地构建查询来解释它,而不是选择最有效的方法。我已经用更有效的方法更新了我的答案,使用相同的分割函数。然后重复您的测试,在初始数据中添加一行:(1,REPLICATE('A;B;C;D;',1000))
。导致-XML拆分:~90秒。数字分割:~6秒。这就是我所说的性能差异,这是由于XML在块中的传输方式造成的。@Anon它必须有一个值。我想varchar(max)可能会更好。这是一个很好的整洁解决方案,但也有可能,而且它不处理特殊的xml字符。i、 e试图拆分”>;>@谢谢你的评论。这可能会在特殊字符上失败。我比较了我们的脚本,发现了性能问题。但是,它不在我的脚本中。关于我的性能问题,你是对的,但这不是由于拆分函数。这是因为我一步一步地构建查询来解释它,而不是选择最有效的方法。我已经用更有效的方法更新了我的答案,使用相同的分割函数。然后重复您的测试,在初始数据中添加一行:(1,REPLICATE('A;B;C;D;',1000))
。导致-XML拆分:~90秒。数字分割:~6秒。这就是我所说的性能差异,这是由于XML在块中的传输方式。