Sql 一个表中以逗号分隔的值返回另一个表的结果
我有两个表和一个相当复杂的SQL查询来从这些表中提取数据——这一切都可以正常工作,直到它在一个列中遇到一个具有多个id的值——逗号分隔。因此,为了简化我正在努力解决的问题,让我们假设如下 表1 T1Sql 一个表中以逗号分隔的值返回另一个表的结果,sql,sql-server,csv,tsql,sql-server-2012,Sql,Sql Server,Csv,Tsql,Sql Server 2012,我有两个表和一个相当复杂的SQL查询来从这些表中提取数据——这一切都可以正常工作,直到它在一个列中遇到一个具有多个id的值——逗号分隔。因此,为了简化我正在努力解决的问题,让我们假设如下 表1 T1 ID First Name Last Name Active -------------------------------------------- 101 Fred Bloggs 1 102 John
ID First Name Last Name Active
--------------------------------------------
101 Fred Bloggs 1
102 John Smith 0
103 Elizabeth Dawson 1
104 Amy Johnson 1
表2 T2
ID Postcode HouseNo
-----------------------------------
101 TS15 9AZ 42
102 TQ1 4TF 3
103, 104 WA1 4AA 7
所以,假设我想返回居住在哪个地址的人的结果,我将ID上的表连接起来,并编写一个相当简单的查询,如
select
T1.FirstName + ' ' + T1.Lastname as fullname, T2.Postcode, T2.HouseNo
from
T1
join
t2 on t1.id = t2.id
where
t1.active = 1
此查询工作正常,直到返回错误时遇到逗号分隔的值:
将varchar值“103104”转换为数据类型int时,转换失败
它应该返回的是
Fullname PostCode HouseNo
-------------------------------------------------------
Fred Blogs TS15 9AZ 42
Elizabeth Dawson Amy Johnson TQ1 4TF 3
关于如何实现这一点,您有什么想法吗?您的查询失败了,因为您的表返回的数据在返回类型中似乎不一致,因为它既有整数值103、104,也有非整数值,就像您在所需输出中提到的那样 这里的解决方案是将它们全部转换为单个类型。我认为字符串结果类型可能是这里的最佳选项: 选择 T1.FirstName+''+T1.Lastname作为全名, CONVERTNVARCHAR10,T2.邮政编码,-您可以将值10更改为任何其他值 CONVERTNVARCHAR10,T2.HouseNo 从…起 T1 参加 t1.id上的t2=t2.id 哪里 t1.active=1 希望这会有所帮助 更新
@marc_s在这里绝对正确,请尽量避免在表列中使用逗号分隔的值。它们违反了官方文档中的SQL规范化规则。首先:不要在一列中存储多个值;不要使用字符串来存储数字。您可以查看更多有关为什么不鼓励这样做的详细信息 也就是说,在CSV列表中搜索值的简单但低效的解决方案是:
select t1.FirstName + ' ' + t1.Lastname as fullname, t2.Postcode, t2.HouseNo
from t1
join t2 on concat(', ', t2.id, ', ') like concat('%, ', t1.id, ', %')
where t1.active = 1
这假设您始终使用逗号+空格','作为列表元素之间的分隔符。有时它可能是我们继承的数据,您只需要修复报告。我不久前在互联网上发现了这个splitstring函数,很感谢编写它的人,如果您使用的是SQL Server 2016之前的数据库版本,而该版本不提供STRING_split,那么它会将您的逗号分隔ID值拆分,您可以将其放入另一个表中使用吗?不过,我同意其他评论,在字段中保留单个值将是100%的好做法
CREATE or alter FUNCTION [dbo].[SplitString]
(
@Input NVARCHAR(MAX),
@Character CHAR(1)
)
RETURNS @Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE @StartIndex INT, @EndIndex INT
SET @StartIndex = 1
IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
BEGIN
SET @Input = @Input + @Character
END
WHILE CHARINDEX(@Character, @Input) > 0
BEGIN
SET @EndIndex = CHARINDEX(@Character, @Input)
INSERT INTO @Output(Item)
SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)
SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
END
RETURN
END
GO
--=======================================================
DROP TABLE IF EXISTS newTable;
SELECT * INTO newTable
FROM
(
SELECT '123,456' as id, 'TS15 9AZ' AS postcode UNION
SELECT '456,789' as id, 'TQ1 4TF' AS postcode
) AS IDS
CROSS APPLY
DBO.[SPLITSTRING](ID, ',') AS SPLIT;
SELECT * FROM newTable;
--=======================================================
正如上面多次建议的那样,最好将这些值分别存储在ID列中。也就是说,在SQL Server中,您可以这样做:
select
T1.FirstName + ' ' + T1.Lastname as fullname, T2.Postcode, T2.HouseNo
from
T1
join
(
select t2.*, value as id_new
from t2
CROSS APPLY STRING_SPLIT(id, ',')
) t2 on t1.id = t2.id_new
where
t1.active = 1
您可以尝试下面的查询
SELECT T1.FirstName + ' ' + T1.LastName AS FullName, T2.PostCode, T2.HouseNo
FROM T1
JOIN (
SELECT LTRIM(RTRIM(ID)), PostCode, HouseNo
FROM T2
CROSS APPLY STRING_SPLIT(ID, ',')
) T2 ON T2.ID = T1.ID
WHERE T1.active = 1
在单个数据库单元中存储逗号分隔的值列表是一个很大的禁忌,正如您在这里看到的,这只会让您感到悲伤和心痛。您应该遵循数据库设计的第一种标准形式—单个单元格最多包含一个原子值—以适当的关系方式处理多个值—我想您将在这里看到关于表2设计的注释。您收到的错误是由于T1.ID是INT,T2.ID是带有“,”示例的字符串。连接这两列将失败。将T2更改为ID为103和104的两个单独的行-然后联接将工作。这将工作。但问题被标记为SQL Server 2012:字符串分割仅在2016年版开始时可用。问题被标记为SQL Server 2012:字符串分割仅在2016年版开始时可用。感谢大家的回复-非常感谢。我使用这个解决方案是因为尽管它排除了带有多个ID的结果,但对于使用不同字段的人来说,重新运行它是一件很容易的事情。