将行连接成具有多对多连接的字符串

时间:2014-04-17 20:55:18

标签: sql sql-server sql-server-2008 concatenation

我的查询几乎适用于所有数据场景......除了一个。我有一个主表,一个详细信息表(包含一到多个行项),以及许多连接到详细信息表的描述表(代码描述等)。我需要将所有详细记录连接成一个字符串值,以便每个主记录有一个记录。因此,如果主记录有3个详细信息,则需要将所有值连接到一个记录中以表示主记录。

我的问题出现在主记录有多个详细记录但细节记录与描述表1到1不匹配时。例如,在我的方案中,主记录有3个详细记录,其中只有一个详细记录中有描述记录。所以1到3比1,Master to Detail to Description。这导致了一个问题。当尝试连接记录时,代码不起作用,因为从Detail to Description连接创建了NULL值。我似乎能够让它工作的唯一方法是做一个不同的子查询,然后在外面做我的连接逻辑。我觉得必须有一个更好的方法,或者我只是缺少一些东西。我在下面提供了示例代码来说明我的问题。有3个选择可以运行。第一个是连接中所有记录的平坦结果。第二个是我原来的逻辑显示缺陷。第三个是工作版本,我希望有人知道如何做得更好。我非常感谢您对此问题的任何帮助。

DECLARE @Notification table(
    SystemID int NOT NULL,
    NotificationID int
    );

DECLARE @NotificationItems table(
    SystemID int NOT NULL,
    NotificationID VARCHAR(100),
    LineItem VARCHAR(100)
    );    

DECLARE @NotificationCauses table(
    SystemID int NOT NULL,
    NotificationID VARCHAR(100),
    LineItem VARCHAR(100),
    TestValue VARCHAR(100)
    );    

INSERT INTO @Notification 
SELECT 40,1 UNION 
SELECT 40,2 UNION 
SELECT 40,3 UNION 
SELECT 40,4
INSERT INTO @NotificationItems 
SELECT 40,1,1 UNION 
SELECT 40,1,2 UNION 
SELECT 40,1,3 UNION 
SELECT 40,2,1 UNION 
SELECT 40,2,2 UNION 
SELECT 40,3,1
INSERT INTO @NotificationCauses 
SELECT 40,1,1,'Code_A' UNION 
SELECT 40,2,1,'Code_B' UNION 
SELECT 40,2,2,'Code_C' UNION 
SELECT 40,3,1,'Code_D'

--SELECT  *
--FROM    @Notification
--SELECT  *
--FROM    @NotificationItems

SELECT *
FROM    @Notification AS n
LEFT OUTER JOIN @NotificationItems AS ni
    ON n.NotificationID = ni.NotificationID
    AND n.SystemID = ni.SystemID
LEFT OUTER JOIN @NotificationCauses AS nc
    ON ni.NotificationID = nc.NotificationID
    AND ni.SystemID = nc.SystemID
    AND ni.LineItem = nc.LineItem


SELECT DISTINCT n.SystemID, n.NotificationID
,SUBSTRING(
    (
        SELECT DISTINCT
         CASE WHEN LTRIM(RTRIM(ni1.LineItem)) <> ISNULL('','') THEN ', '+ni1.LineItem ELSE '' END AS [text()] 
        FROM @NotificationItems AS ni1
        WHERE ni1.SystemID = ni.SystemID AND ni1.NotificationID = ni.NotificationID --AND a1.LineItem = a.LineItem
        ORDER BY 1
        FOR XML PATH ('')
    ), 2, 1000) AS [LineItem]
,SUBSTRING(
    (
        SELECT DISTINCT
         CASE WHEN LTRIM(RTRIM(nc1.TestValue)) <> ISNULL('','') THEN ', '+nc1.TestValue ELSE '' END AS [text()] 
        FROM @NotificationCauses AS nc1
        WHERE nc1.SystemID = nc.SystemID AND nc1.NotificationID = nc.NotificationID --AND nc1.LineItem = nc.LineItem
        ORDER BY 1
        FOR XML PATH ('')
    ), 2, 1000) AS [TestValues]
FROM    @Notification AS n
LEFT OUTER JOIN @NotificationItems AS ni
    ON n.SystemID = ni.SystemID
    AND n.NotificationID = ni.NotificationID
LEFT OUTER JOIN @NotificationCauses AS nc
    ON ni.SystemID = nc.SystemID
    AND ni.NotificationID = nc.NotificationID
    AND ni.LineItem = nc.LineItem


SELECT DISTINCT SystemID, NotificationID
,SUBSTRING(
    (
        SELECT DISTINCT
         CASE WHEN LTRIM(RTRIM(a1.LineItem)) <> ISNULL('','') THEN ', '+a1.LineItem ELSE '' END AS [text()] 
        FROM @NotificationItems AS a1
        WHERE a1.SystemID = a.SystemID AND a1.NotificationID = a.NotificationID --AND a1.LineItem = a.LineItem
        ORDER BY 1
        FOR XML PATH ('')
    ), 2, 1000) AS [LineItem]
,SUBSTRING(
    (
        SELECT DISTINCT
         CASE WHEN LTRIM(RTRIM(a1.TestValue)) <> ISNULL('','') THEN ', '+a1.TestValue ELSE '' END AS [text()] 
        FROM @NotificationCauses AS a1
        WHERE a1.SystemID = a.SystemID AND a1.NotificationID = a.NotificationID --AND a1.LineItem = a.LineItem
        ORDER BY 1
        FOR XML PATH ('')
    ), 2, 1000) AS [TestValues]    
FROM    
(    
SELECT DISTINCT n.NotificationID, n.SystemID, ni.LineItem, nc.TestValue
FROM    @Notification AS n
LEFT OUTER JOIN @NotificationItems AS ni
    ON n.SystemID = ni.SystemID
    AND n.NotificationID = ni.NotificationID
LEFT OUTER JOIN @NotificationCauses AS nc
    ON ni.SystemID = nc.SystemID
    AND ni.NotificationID = nc.NotificationID
    AND ni.LineItem = nc.LineItem
) AS a

1 个答案:

答案 0 :(得分:2)

我已经清除了查询,结果是

SELECT n.SystemID, n.NotificationID
     , SUBSTRING((SELECT COALESCE(', ' + ni.LineItem, '') [text()] 
                  FROM   NotificationItems AS ni
                  WHERE  ni.SystemID = n.SystemID 
                    AND  ni.NotificationID = n.NotificationID
                  ORDER BY 1
                  FOR XML PATH ('')
                 ), 2, 1000) AS [LineItem]
     , SUBSTRING((SELECT COALESCE(', ' + nc.TestValue, '') [text()] 
                  FROM   NotificationItems AS ni
                         INNER JOIN NotificationCauses nc
                                 ON ni.SystemID = nc.SystemID 
                                AND ni.NotificationID = nc.NotificationID 
                                AND ni.LineItem = nc.LineItem
                  WHERE ni.SystemID = n.SystemID 
                    AND ni.NotificationID = n.NotificationID
                  ORDER BY 1
                  FOR XML PATH ('')
                 ), 2, 1000) AS [TestValues]
FROM   Notification n

那些是&#34;气味&#34;成立:

  • ISNULL('', ''),它实际上什么也没做,替换为空白字符串''(更多内容)

  • 主要选择的FROM,当只有一个真正使用时,有三个表,未使用的两个被删除,需要更新FROM和WHERE条件子查询

    < / LI>
  • 子查询的DISTINCT,再次没有做任何事情,被剥夺了

  • 子查询中的CASE在值之前添加逗号,如果修剪后的值不为空,那正是COALESCE(', ' + value, '')所做的(如果需要修剪则重新添加它) )

其他所有内容都只是以我的方式格式化查询,并且如果按照自己的方式进行格式化(例如通过重构进行审核),则更容易阅读

Here是使用提供的数据清除查询的SQLFiddle演示