Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/80.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 一个表中以逗号分隔的值返回另一个表的结果_Sql_Sql Server_Csv_Tsql_Sql Server 2012 - Fatal编程技术网

Sql 一个表中以逗号分隔的值返回另一个表的结果

Sql 一个表中以逗号分隔的值返回另一个表的结果,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

我有两个表和一个相当复杂的SQL查询来从这些表中提取数据——这一切都可以正常工作,直到它在一个列中遇到一个具有多个id的值——逗号分隔。因此,为了简化我正在努力解决的问题,让我们假设如下

表1 T1

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的结果,但对于使用不同字段的人来说,重新运行它是一件很容易的事情。