Sql server 如何连接到列中有多个值的表?
我有一个person表,它包含一个错误代码字段,可以包含多个错误代码001、002、003。。。。我知道这是一个模式问题,但这是一个供应商应用程序,我无法控制模式,因此我必须使用现有的模式 还有一个错误表,其中包含错误代码char3和描述符char1000。在我的查询中,Person.ErrorCode与Error.ErrorCode相连,以获得相应描述的值 对于只有一个错误代码的个人记录,我可以毫无问题地获得相应的描述。我试图做的是,在存在多个错误的记录中,以某种方式计算描述符值 例如,以下是错误表中的一些示例数据:Sql server 如何连接到列中有多个值的表?,sql-server,sql-server-2005,tsql,Sql Server,Sql Server 2005,Tsql,我有一个person表,它包含一个错误代码字段,可以包含多个错误代码001、002、003。。。。我知道这是一个模式问题,但这是一个供应商应用程序,我无法控制模式,因此我必须使用现有的模式 还有一个错误表,其中包含错误代码char3和描述符char1000。在我的查询中,Person.ErrorCode与Error.ErrorCode相连,以获得相应描述的值 对于只有一个错误代码的个人记录,我可以毫无问题地获得相应的描述。我试图做的是,在存在多个错误的记录中,以某种方式计算描述符值 例如,以下是
ErrorCode Descript
001 Problem with person file
002 Problem with address file
003 Problem with grade
以下是“我的选择对象”和“加入对象”错误导致的列:
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
感谢您的建议 通过将错误分组在一起并将它们连接在一起是一种选择:
SELECT *, GROUP_CONCAT(Person.ErrorCode) FROM Person
GROUP BY Person.RecID
通过将错误分组在一起并将它们连接在一起是一种选择:
SELECT *, GROUP_CONCAT(Person.ErrorCode) FROM Person
GROUP BY Person.RecID
在使用error.errorcode联接之前,取消person.errorcode的规范化
我不是指在表级别上反规范化,我是指使用视图或sql代码。在连接之前使用error.errorcode反规范化person.errorcode 我不是指在表级别上进行非规范化,我是指使用视图或sql代码。您应该看到:,那么在sql Server中有很多方法可以拆分字符串。本文介绍了几乎每种方法的优缺点。通常,您需要创建一个拆分函数。以下是使用拆分函数连接行的方式:
SELECT
*
FROM dbo.yourSplitFunction(@Parameter) b
INNER JOIN YourCodesTable c ON b.ListValue=c.CodeValue
但是在SQLServer中有很多方法可以分割字符串,请参见前面的链接,其中解释了每种方法的优缺点
要使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)
设置数字表后,创建此拆分函数:
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
输出:
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)
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)
您可以使用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
输出:
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)
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
您应该看到:,那么在SQL Server中有很多方法可以拆分字符串。本文介绍了几乎每种方法的优缺点。通常,您需要创建一个拆分函数。以下是使用拆分函数连接行的方式:
SELECT
*
FROM dbo.yourSplitFunction(@Parameter) b
INNER JOIN YourCodesTable c ON b.ListValue=c.CodeValue
但是在SQLServer中有很多方法可以分割字符串,请参见前面的链接,其中解释了每种方法的优缺点
要使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)
设置数字表后,创建此拆分函数:
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
输出:
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)
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)
您可以使用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
输出:
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)
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
您可以使用公共表表达式来假装person表是正常的:
;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,在原始查询中使用Person。您需要将该null强制转换为与Person表中的ErrorCode一样宽的varchar列您可以使用公共表表达式来假装Person表是正常的:
;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,在原始查询中使用Person。您可能希望将该null强制转换为与Person表中的ErrorCode一样宽的varchar列连接错误描述可能不是一种方法。它给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
连接错误描述可能不是一个好办法。它给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
SQL Server没有组\u CONCAT。组\u CONCAT不是SQL Server中的关键字。您可能认为MySQLSQL Server没有GROUP_CONCAT。GROUP_CONCAT不是SQL Server中的关键字。你可能会想到MySQLOP在问题中说的,我知道这是一个模式问题,但这是一个供应商应用程序,我对模式没有控制权,所以我必须使用我得到的。OP在问题中说,我知道这是一个模式问题,但 is是一个供应商应用程序,我对模式没有控制权,因此我必须使用现有的模式。