如何加入到列有多个值的表?(How to JOIN to a table that has mult

2019-09-29 18:59发布

我有一个包含一个错误代码字段可以包含多个错误代码(001,002,003 ...)的人表。 我知道这是一个架构问题,但这是一个供应商的应用程序,我有超过架构无法控制,所以我有我有什么工作。

还存在包含错误码(炭(3))和一个DESCRIPT误差表(炭(1000))。 在我的查询Person.ErrorCode结合到Error.ErrorCode得到相应的说明的值。

对于人的记录,其中只有一个错误代码,我可以没有问题得到相应的DESCRIPT。 我想要做的就是以某种方式对的concat那里有多个错误记录的DESCRIPT值。

例如,这里是从错误中的示例数据:

ErrorCode     Descript
001           Problem with person file
002           Problem with address file
003           Problem with grade

下面是我对人造成SELECT与JOIN上的错误列:

Person.RecID   Person.ErrorCode  Error.Descript
12345          001               Problem with person file
12346          003               Problem with grade
12347          002,003

我试图让是这样的:

Person.RecID   Person.ErrorCode  Error.Descript
12345          001               Problem with person file
12346          003               Problem with grade
12347          002,003           Problem with address file, Problem with grade

建议表示赞赏!

Answer 1:

您应该看到: “数组和列表在SQL Server 2005及以后,当表值参数不剪”的厄兰Sommarskog ,那么有很多方式在SQL Server中拆分字符串。 本文介绍了几乎每一个方法的优点和缺点。 在一般情况下,你需要创建一个分裂的功能。 这是一个分裂的功能如何被用于加入行:

SELECT
    * 
    FROM dbo.yourSplitFunction(@Parameter) b
        INNER JOIN YourCodesTable          c ON b.ListValue=c.CodeValue

我更喜欢数字表方法将拆分TSQL字符串 ,但也有许多方法来拆分在SQL Server中的字符串,见前面的链接,这说明各的优点和缺点。

对于数字表的方法来工作,你需要做的这一次表的设置,这将创建一个表Numbers包含行从1到10000:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

一旦Numbers表格设置,创建此分割功能:

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(

    ----------------
    --SINGLE QUERY-- --this will not return empty rows
    ----------------
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''

);
GO 

现在,您可以为CSV字符串容易分裂成表,并加入就可以了:

DECLARE @ErrorCode table (ErrorCode varchar(20), Description varchar(30))
INSERT @ErrorCode VALUES ('001','Problem with person file')
INSERT @ErrorCode VALUES ('002','Problem with address file')
INSERT @ErrorCode VALUES ('003','Problem with grade')

DECLARE @Person table (RecID int, ErrorCode varchar(20))
INSERT @Person VALUES (12345 ,'001'    )
INSERT @Person VALUES (12346 ,'003'    )
INSERT @Person VALUES (12347 ,'002,003')


SELECT
    p.RecID,c.ListValue,e.Description
    FROM @Person                                        p
        CROSS APPLY dbo.FN_ListToTable(',',p.ErrorCode) c
        INNER JOIN @ErrorCode                           e ON c.ListValue=e.ErrorCode

OUTPUT:

RecID       ListValue     Description              
----------- ------------- -------------------------
12345       001           Problem with person file 
12346       003           Problem with grade       
12347       002           Problem with address file
12347       003           Problem with grade       

(4 row(s) affected)

您可以使用XML招行串连到一起:

SELECT
    t1.RecID,t1.ErrorCode
        ,STUFF(
                   (SELECT
                        ', ' + e.Description
                        FROM @Person                                        p
                            CROSS APPLY dbo.FN_ListToTable(',',p.ErrorCode) c
                            INNER JOIN @ErrorCode                           e ON c.ListValue=e.ErrorCode
                        WHERE t1.RecID=p.RecID
                        ORDER BY p.ErrorCode
                        FOR XML PATH(''), TYPE
                   ).value('.','varchar(max)')
                   ,1,2, ''
              ) AS ChildValues
    FROM @Person t1
    GROUP BY t1.RecID,t1.ErrorCode

OUTPUT:

RecID       ErrorCode            ChildValues
----------- -------------------- -----------------------------------------------
12345       001                  Problem with person file
12346       003                  Problem with grade
12347       002,003              Problem with address file, Problem with grade

(3 row(s) affected)

这将返回相同的结果如上设置,但可能有更好的表现:

SELECT
    t1.RecID,t1.ErrorCode
        ,STUFF(
                   (SELECT
                        ', ' + e.Description
                        FROM (SELECT ListValue FROM dbo.FN_ListToTable(',',t1.ErrorCode)) c
                            INNER JOIN @ErrorCode e ON c.ListValue=e.ErrorCode
                        ORDER BY c.ListValue
                        FOR XML PATH(''), TYPE
                   ).value('.','varchar(max)')
                   ,1,2, ''
              ) AS ChildValues
    FROM @Person t1
    GROUP BY t1.RecID,t1.ErrorCode


Answer 2:

非规范化person.errorcode前与error.errorcode加盟

我不是说非规范化桌子上的水平,我指的是一个视图或SQL代码。



Answer 3:

您可以使用公用表表达式假装的人表是正常的:

;WITH PersonPrime as (
    SELECT RecID,ErrorCode,CAST(null as varchar(100)) as Remain from Person where Value not like '%,%'
    UNION ALL
    SELECT RecID,SUBSTRING(ErrorCode,1,CHARINDEX(',',ErrorCode)-1),SUBSTRING(ErrorCode,CHARINDEX(',',ErrorCode)+1,100) from Person where Value like '%,%'
    UNION ALL
    SELECT RecID,Remain,null FROM PersonPrime where Remain not like '%,%'
    UNION ALL
    SELECT RecID,SUBSTRING(Remain,1,CHARINDEX(',',Remain)-1),SUBSTRING(Remain,CHARINDEX(',',Remain)+1,100) from PersonPrime where Remain like '%,%'
)
SELECT RecID,ErrorCode from PersonPrime

而现在使用PersonPrime在那里你会在你的原始查询已使用的人。 你要的是空宽转换为VARCHAR列错误代码的人表



Answer 4:

拼接错误的描述可能不是要走的路。 它增加了不必要的复杂的SQL语句,这将是极大的问题进行调试。 今后的任何添加或更改SQL也将是困难的。 最好的办法是产生一个结果就是标准化的,即使你的模式是没有的。

SELECT Person.RecID, Person.ErrorCode, Error.ErrorCode, Error.Descript
  FROM Person INNER JOIN Error
    ON REPLACE(Person.ErrorCode, ' ', '') LIKE '%,' + CONVERT(VARCHAR,Error.ErrorCode) + ',%'

如果一个人有多个错误代码设置,那么这将返回每个错误指定的(忽略重复)一行。 使用你的榜样,它会返回这一点。

Person.RecID   Person.ErrorCode   Error.ErrorCode   Error.Descript
12345          001                001               Problem with person file
12346          003                003               Problem with grade
12347          002,003            002               Problem with address file
12347          002,003            003               Problem with grade


Answer 5:

通过分组错误一起,它们串联为一个选项:

SELECT *, GROUP_CONCAT(Person.ErrorCode) FROM Person
GROUP BY Person.RecID


文章来源: How to JOIN to a table that has multiple values in the column?