“Stuff and 'For Xml Path'”或 UNION 以排除重复行
“Stuff and 'For Xml Path'” or UNION to exclude duplicate rows
这里是问题的进一步进展:
我有 3 个 tables - 发票、头寸和账户,它们由 SELECT 建立共同的 table,如下面的结果所示。我需要排除由于相同发票的不同帐户而出现的重复行。我认为它可以通过 UNION 的“Stuff and 'For Xml Path'” and/or 来解决,但我真的不知道该怎么做。
请协助解决此问题。提前谢谢你。
这是带有 DL 和样本数据填充的 dbfiddle:https://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, quantity, price, summ, invoice)
VALUES ('p1000002', 3, 100, 300, 2);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000003', 1, 250, 250, 2);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000004', 2, 120, 240, 1);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000005', 4, 100, 400, 1);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000006', 3, 100, 300, 1001);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000007', 5, 80, 400, 3);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000008', 5, 100, 500, 3);
CREATE TABLE accounts
(
account INT NOT NULL,
invoice INT NOT NULL,
);
INSERT accounts(account, invoice)
VALUES (01, 2);
INSERT accounts(account, invoice)
VALUES (02, 2);
INSERT accounts(account, invoice)
VALUES (03, 1);
INSERT accounts(account, invoice)
VALUES (04, 3);
INSERT accounts(account, invoice)
VALUES (05, 1);
INSERT accounts(account, 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
结果:
position
quantity
price
summ
invoice
account
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:
position
quantity
price
summ
invoice
account
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:
position
quantity
price
summ
invoice
account 1
account 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 服务器版本。在 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, 1 ),
( 'p1000002', 3, 100, 300, 2 ),
( 'p1000003', 1, 250, 250, 2 ),
( 'p1000004', 2, 120, 240, 1 ),
( 'p1000005', 4, 100, 400, 1 ),
( 'p1000006', 3, 100, 300, 1 ),
( 'p1000007', 5, 80, 400, 3 ),
( 'p1000008', 5, 100, 500, 3 );
DECLARE @accounts table (
account INT NOT NULL, invoice INT NOT NULL
);
INSERT INTO @accounts ( account, invoice ) VALUES
( 1, 2 ), ( 2, 2 ), ( 3, 1 ), ( 4, 3 ), ( 5, 1 ), ( 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, quantity, price, summ, invoices.invoice
ORDER BY
invoice;
SQL 服务器 2017 之前
SELECT
positions.position,
positions.quantity,
positions.price,
positions.summ,
invoices.invoice,
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 ( '' )
), 1, 1, '' ) AS account
) AS acct
GROUP BY
position, quantity, price, summ, invoices.invoice, acct.account
ORDER BY
invoice;
两个查询return
+----------+----------+-------+------+------------+---------+
| 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 数据类型。以下示例对您的数据进行了自由假设,最值得注意的是预计只有两个帐户。话虽如此,您可以根据需要扩展 PIVOT
中的 [account n]
列,而不必求助于动态 SQL.
SELECT DISTINCT
positions.position,
positions.quantity,
positions.price,
positions.summ,
invoices.invoice
, 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;
Returns
+----------+----------+-------+------+------------+-----------+-----------+
| 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 |
+----------+----------+-------+------+------------+-----------+-----------+
这里是问题的进一步进展:
我有 3 个 tables - 发票、头寸和账户,它们由 SELECT 建立共同的 table,如下面的结果所示。我需要排除由于相同发票的不同帐户而出现的重复行。我认为它可以通过 UNION 的“Stuff and 'For Xml Path'” and/or 来解决,但我真的不知道该怎么做。
请协助解决此问题。提前谢谢你。
这是带有 DL 和样本数据填充的 dbfiddle:https://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, quantity, price, summ, invoice)
VALUES ('p1000002', 3, 100, 300, 2);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000003', 1, 250, 250, 2);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000004', 2, 120, 240, 1);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000005', 4, 100, 400, 1);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000006', 3, 100, 300, 1001);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000007', 5, 80, 400, 3);
INSERT positions(position, quantity, price, summ, invoice)
VALUES ('p1000008', 5, 100, 500, 3);
CREATE TABLE accounts
(
account INT NOT NULL,
invoice INT NOT NULL,
);
INSERT accounts(account, invoice)
VALUES (01, 2);
INSERT accounts(account, invoice)
VALUES (02, 2);
INSERT accounts(account, invoice)
VALUES (03, 1);
INSERT accounts(account, invoice)
VALUES (04, 3);
INSERT accounts(account, invoice)
VALUES (05, 1);
INSERT accounts(account, 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
结果:
position | quantity | price | summ | invoice | account |
---|---|---|---|---|---|
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:
position | quantity | price | summ | invoice | account |
---|---|---|---|---|---|
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:
position | quantity | price | summ | invoice | account 1 | account 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 服务器版本。在 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, 1 ),
( 'p1000002', 3, 100, 300, 2 ),
( 'p1000003', 1, 250, 250, 2 ),
( 'p1000004', 2, 120, 240, 1 ),
( 'p1000005', 4, 100, 400, 1 ),
( 'p1000006', 3, 100, 300, 1 ),
( 'p1000007', 5, 80, 400, 3 ),
( 'p1000008', 5, 100, 500, 3 );
DECLARE @accounts table (
account INT NOT NULL, invoice INT NOT NULL
);
INSERT INTO @accounts ( account, invoice ) VALUES
( 1, 2 ), ( 2, 2 ), ( 3, 1 ), ( 4, 3 ), ( 5, 1 ), ( 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, quantity, price, summ, invoices.invoice
ORDER BY
invoice;
SQL 服务器 2017 之前
SELECT
positions.position,
positions.quantity,
positions.price,
positions.summ,
invoices.invoice,
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 ( '' )
), 1, 1, '' ) AS account
) AS acct
GROUP BY
position, quantity, price, summ, invoices.invoice, acct.account
ORDER BY
invoice;
两个查询return
+----------+----------+-------+------+------------+---------+
| 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 数据类型。以下示例对您的数据进行了自由假设,最值得注意的是预计只有两个帐户。话虽如此,您可以根据需要扩展 PIVOT
中的 [account n]
列,而不必求助于动态 SQL.
SELECT DISTINCT
positions.position,
positions.quantity,
positions.price,
positions.summ,
invoices.invoice
, 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;
Returns
+----------+----------+-------+------+------------+-----------+-----------+
| 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 |
+----------+----------+-------+------+------------+-----------+-----------+