“Stuff and 'For Xml Path'” 或 UNION 以排除重复行

问题描述

这是问题的进一步进展:how to use "Stuff and 'For Xml Path'" to unite rows in table

我有 3 个表 - 发票、头寸和帐户,它们通过 SELECT 构建通用表,如下面的结果所示。我需要排除由于相同发票的不同帐户而出现的重复行。我认为它可以通过“Stuff and 'For Xml Path'”和/或 UNION 来解决,但我真的不知道该怎么做。

请协助解决此问题。提前致谢。

这是包含 DL 和样本数据群的 dbfiddlehttps://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=6401c2886a24b21239dade27e8c549ec

使用基于 sql Server 2016 的 DevExpress 仪表板设计器。

查询

-- DDL and sample data population,start
CREATE TABLE invoices
(
invoice VARCHAR(20) NOT NULL,id INT NOT NULL,);
INSERT invoices(invoice,id) 
VALUES ('ty20210110',1);
INSERT invoices(invoice,id) 
VALUES ('ty20210111',2);
INSERT invoices(invoice,id) 
VALUES ('ty20210112',3);

CREATE TABLE positions
(
position VARCHAR(20) NOT NULL,quantity INT NOT NULL,price INT NOT NULL,summ INT NOT NULL,invoice INT NOT NULL,);
INSERT positions(position,quantity,price,summ,invoice) 
VALUES ('p1000001',2,100,200,1);
INSERT positions(position,invoice) 
VALUES ('p1000002',3,300,2);
INSERT positions(position,invoice) 
VALUES ('p1000003',1,250,invoice) 
VALUES ('p1000004',120,240,invoice) 
VALUES ('p1000005',4,400,invoice) 
VALUES ('p1000006',1001);
INSERT positions(position,invoice) 
VALUES ('p1000007',5,80,3);
INSERT positions(position,invoice) 
VALUES ('p1000008',500,3);

CREATE TABLE accounts
(
account INT NOT NULL,);
INSERT accounts(account,invoice) 
VALUES (01,2);
INSERT accounts(account,invoice) 
VALUES (02,invoice) 
VALUES (03,1);
INSERT accounts(account,invoice) 
VALUES (04,3);
INSERT accounts(account,invoice) 
VALUES (05,invoice) 
VALUES (06,3);
-- DDL and sample data population,end


SELECT
positions.position,positions.quantity,positions.price,positions.summ,invoices.invoice,accounts.account

FROM
positions
INNER JOIN
invoices invoices ON invoices.id = positions.invoice
INNER JOIN
accounts accounts ON invoices.id = accounts.invoice

结果:

位置 数量 价格 总和 发票 帐号
p1000001 2 100 200 in20210110 3
p1000001 2 100 200 in20210110 5
p1000002 3 100 300 in20210111 1
p1000002 3 100 300 in20210111 2
p1000003 1 250 250 in20210111 1
p1000003 1 250 250 in20210111 2
p1000004 2 120 240 in20210110 3
p1000004 2 120 240 in20210110 5
p1000005 4 100 400 in20210110 3
p1000005 4 100 400 in20210110 5
p1000006 3 100 300 in20210110 3
p1000006 3 100 300 in20210110 5
p1000007 5 80 400 in20210112 4
p1000007 5 80 400 in20210112 6
p1000008 5 100 500 in20210112 4
p1000008 5 100 500 in20210112 6

要求的结果 1:

位置 数量 价格 总结 发票 帐号
p1000001 2 100 200 in20210110 3、5
p1000004 2 120 240 in20210110 3、5
p1000005 4 100 400 in20210110 3、5
p1000006 3 100 300 in20210110 3、5
p1000002 3 100 300 in20210111 1、2
p1000003 1 250 250 in20210111 1、2
p1000007 5 80 400 in20210112 4、6
p1000008 5 100 500 in20210112 4、6

要求的结果 2:

位置 数量 价格 总和 发票 帐号 1 帐号 2
p1000001 2 100 200 in20210110 3 5
p1000004 2 120 240 in20210110 3 5
p1000005 4 100 400 in20210110 3 5
p1000006 3 100 300 in20210110 3 5
p1000002 3 100 300 in20210111 1 2
p1000003 1 250 250 in20210111 1 2
p1000007 5 80 400 in20210112 4 6
p1000008 5 100 500 in20210112 4 6

解决方法

您的第一个结果集可以通过两种方式处理,具体取决于您的 SQL Server 版本。在 SSMS 中尝试以下示例:

创建数据

DECLARE @invoices table (
    invoice VARCHAR(20) NOT NULL,id INT NOT NULL 
);

INSERT INTO @invoices (invoice,id) VALUES 
    ( 'ty20210110',1 ),( 'ty20210111',2 ),( 'ty20210112',3 );

DECLARE @positions table (
    position VARCHAR(20) NOT NULL,quantity INT NOT NULL,price INT NOT NULL,summ INT NOT NULL,invoice INT NOT NULL
);

INSERT INTO @positions ( position,quantity,price,summ,invoice ) VALUES
    ( 'p1000001',2,100,200,( 'p1000002',3,300,( 'p1000003',1,250,( 'p1000004',120,240,( 'p1000005',4,400,( 'p1000006',( 'p1000007',5,80,3 ),( 'p1000008',500,3 );

DECLARE @accounts table (
    account INT NOT NULL,invoice INT NOT NULL
);

INSERT INTO @accounts ( account,invoice ) VALUES 
    ( 1,( 2,( 3,( 4,( 5,( 6,3 );

如果您使用的是 SQL Server 2017+

SELECT
    positions.position,positions.quantity,positions.price,positions.summ,invoices.invoice,STRING_AGG ( accounts.account,',' ) AS account
FROM @positions positions
INNER JOIN @invoices invoices 
    ON invoices.id = positions.invoice
INNER JOIN @accounts accounts 
    ON invoices.id = accounts.invoice
GROUP BY
    position,invoices.invoice
ORDER BY
    invoice;

对于 Pre-SQL Server 2017

SELECT
    positions.position,acct.account
FROM @positions positions
INNER JOIN @invoices invoices 
    ON invoices.id = positions.invoice
INNER JOIN @accounts accounts 
    ON invoices.id = accounts.invoice
OUTER APPLY (
    SELECT STUFF ( (
        SELECT ',' + CAST ( a.account AS varchar(50) ) AS "text()"
        FROM @accounts a
        WHERE
            a.invoice = invoices.id
        FOR XML PATH ( '' )
    ),'' ) AS account
) AS acct
GROUP BY
    position,acct.account
ORDER BY
    invoice;

两个查询都返回

+----------+----------+-------+------+------------+---------+
| position | quantity | price | summ |  invoice   | account |
+----------+----------+-------+------+------------+---------+
| p1000001 |        2 |   100 |  200 | ty20210110 | 3,5     |
| p1000004 |        2 |   120 |  240 | ty20210110 | 3,5     |
| p1000005 |        4 |   100 |  400 | ty20210110 | 3,5     |
| p1000006 |        3 |   100 |  300 | ty20210110 | 3,5     |
| p1000002 |        3 |   100 |  300 | ty20210111 | 1,2     |
| p1000003 |        1 |   250 |  250 | ty20210111 | 1,2     |
| p1000007 |        5 |    80 |  400 | ty20210112 | 4,6     |
| p1000008 |        5 |   100 |  500 | ty20210112 | 4,6     |
+----------+----------+-------+------+------------+---------+

您请求的第二个结果集变得相当复杂,并且需要使用 XML 数据类型。以下示例对您的数据进行了自由假设,最明显的是只需要两个帐户。话虽如此,您可以根据需要扩展 [account n] 中的 PIVOT 列,而不必求助于动态 SQL。

SELECT DISTINCT
    positions.position,x.*
FROM @positions positions
INNER JOIN @invoices invoices 
    ON invoices.id = positions.invoice
INNER JOIN @accounts accounts 
    ON invoices.id = accounts.invoice
OUTER APPLY (

    -- Create an XML account list --
    SELECT CAST ( (
        SELECT 
            'account ' + CAST ( ROW_NUMBER() OVER ( ORDER BY a.invoice ) AS varchar(50) ) AS id,a.account
        FROM @accounts a
        WHERE
            a.invoice = invoices.id
        FOR XML PATH ( 'data' ),ROOT ( 'accounts' )
    ) AS xml ) AS account_xml

) AS acct
OUTER APPLY (

    -- PIVOT the account details --
    SELECT
        *
    FROM (
        SELECT
            x.f.value( 'id[1]','varchar(50)' ) AS id,x.f.value( 'account[1]','varchar(50)' ) AS act
        FROM acct.account_xml.nodes( '//accounts/data' ) x(f)
    ) AS d
    PIVOT (
        MAX ( act ) FOR id IN ( [account 1],[account 2] )
    ) AS p

) AS x
ORDER BY
    invoice;

退货

+----------+----------+-------+------+------------+-----------+-----------+
| position | quantity | price | summ |  invoice   | account 1 | account 2 |
+----------+----------+-------+------+------------+-----------+-----------+
| p1000001 |        2 |   100 |  200 | ty20210110 |         3 |         5 |
| p1000004 |        2 |   120 |  240 | ty20210110 |         3 |         5 |
| p1000005 |        4 |   100 |  400 | ty20210110 |         3 |         5 |
| p1000006 |        3 |   100 |  300 | ty20210110 |         3 |         5 |
| p1000002 |        3 |   100 |  300 | ty20210111 |         1 |         2 |
| p1000003 |        1 |   250 |  250 | ty20210111 |         1 |         2 |
| p1000007 |        5 |    80 |  400 | ty20210112 |         4 |         6 |
| p1000008 |        5 |   100 |  500 | ty20210112 |         4 |         6 |
+----------+----------+-------+------+------------+-----------+-----------+