汇总 SQL 数据库中具有多对多关系的两列
Summarize two columns that have a many-to-many relationship in an SQL database
我有以下数据库 tables Transactions
和 Payments
。数据的结构使得交易和付款都具有多对多关系,它们在查询中连接在一起:
SELECT Transactions.Id, Payments.Id, Transactions.Amount
FROM Transactions
LEFT JOIN Payments ON Transactions.Id = Payments.TransactionId
| Transactions.Id | Payments.PaymentId | Transactions.Amount |
| ----------------| -------------------| --------------------|
| 3492 | 123456 | 123 |
| 3492 | 123457 | 123 |
| 3493 | 123458 | 300 |
| 3494 | 123459 | 10 |
| 3495 | 123459 | 25 |
我希望能够将 table 简化为如下所示(如果这在 SQL 中完全可行的话)
| Transactions.Id | Payments.PaymentId | Transactions.Amount |
| ----------------| -------------------| --------------------|
| 3492 | 123456, 123457 | 123 |
| 3493 | 123458 | 300 |
| 3494, 3495 | 123459 | 35 |
...甚至没有第三列(如果它降低了复杂性)
| Transactions.Id | Payments.PaymentId |
| ----------------| -------------------|
| 3492 | 123456, 123457 |
| 3493 | 123458 |
| 3494, 3495 | 123459 |
我目前拥有的:
SELECT
Transactions.Id,
STUFF((SELECT '; ' + CAST(Payments.Id as varchar)
FROM Payments
WHERE Payments.TransactionId = Transactions.Id
FOR XML PATH('')), 1, 1, '') [PaymentIds]
FROM Transactions
| Transactions.Id | PaymentIds |
| ----------------| -------------------|
| 3492 | 123456, 123457 |
| 3493 | 123458 |
| 3494 | 123459 |
| 3495 | 123459 |
如何将 Transactions.Id 添加到逗号分隔列表中?
我正在使用 SQL 服务器 12.0.2000.8
基本上,您必须通过两次才能汇总每笔交易的付款,然后汇总每笔付款的交易。这假设(基于示例数据和所需的输出)当付款应用于多个交易时,您需要总和,但是当交易有多个付款时,您需要最大值(或者在这种情况下它们总是相同的)最大工作正常吗?):
;WITH FirstPass AS
(
SELECT t.Id, pId = STUFF(
(SELECT ', ' + CONVERT(varchar(11), p.Id)
FROM dbo.Payments AS p
WHERE p.TransactionId = t.Id
ORDER BY p.Id
FOR XML PATH(''), TYPE).value(N'./text()[1]',
N'varchar(max)'), 1, 2, ''),
Amount = MAX(t.Amount)
FROM dbo.Transactions AS t
GROUP BY t.Id
)
SELECT [Transactions.Id] = STUFF(
(SELECT ', ' + CONVERT(varchar(11), fp.Id)
FROM FirstPass AS fp
WHERE fp.pId = FirstPass.pId
ORDER BY fp.pId
FOR XML PATH(''), TYPE).value(N'./text()[1]',
N'varchar(max)'), 1, 2, ''),
[Payments.PaymentId] = FirstPass.pId,
Amount = SUM(FirstPass.Amount)
FROM FirstPass
GROUP BY FirstPass.pId;
输出:
Transactions.Id
Payments.PaymentId
Amount
3492
123456, 123457
123.00
3493
123458
300.00
3494, 3495
123459
35.00
在 SQL Server 2017+(或 Azure SQL 数据库)上,这变得更加简单,但对于这个特定的用例来说仍然有点复杂。这目前对您没有帮助,但可以帮助今天的其他读者、未来的读者,甚至未来的您:
;WITH FirstPass AS
(
SELECT t.Id, ca.pId, Amount = MAX(t.Amount)
FROM dbo.Transactions AS t
CROSS APPLY
(
SELECT pId = STRING_AGG(p.Id, ', ')
FROM dbo.Payments AS p
WHERE p.TransactionId = t.Id
) AS ca GROUP BY t.Id, ca.pId
)
SELECT [Transactions.Id] = STRING_AGG(Id, ', '),
[Payments.PaymentId] = pId,
Amount = SUM(Amount)
FROM FirstPass
GROUP BY pId;
相同的输出:
下面的代码生成你想要的输出
WITH cte1 AS(
SELECT
Transactions.Id AS TransactionID,
Payments.Id AS PaymentID,
max(Transactions.amount) Amount
FROM Transactions
LEFT JOIN Payments ON Transactions.Id = Payments.TransactionId
GROUP BY Transactions.Id,Payments.Id),
cte2 AS(
SELECT
STUFF((SELECT ',' + cast(t2.TransactionID as varchar(100))
FROM cte1 t2
WHERE t2.PaymentID = t1.PaymentID
FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'')TransactionID,
STUFF((SELECT ',' + cast(t2.PaymentID as varchar(100))
FROM cte1 t2
WHERE t2.TransactionID = t1.TransactionID
FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'')PaymentID,
(SELECT sum(Amount)
FROM cte1 t2
WHERE t2.PaymentID = t1.PaymentID
GROUP BY PaymentID)Amount
FROM cte1 t1
)
SELECT TransactionID,PaymentID,Amount
FROM cte2
GROUP BY TransactionID,PaymentID,Amount
我有以下数据库 tables Transactions
和 Payments
。数据的结构使得交易和付款都具有多对多关系,它们在查询中连接在一起:
SELECT Transactions.Id, Payments.Id, Transactions.Amount
FROM Transactions
LEFT JOIN Payments ON Transactions.Id = Payments.TransactionId
| Transactions.Id | Payments.PaymentId | Transactions.Amount |
| ----------------| -------------------| --------------------|
| 3492 | 123456 | 123 |
| 3492 | 123457 | 123 |
| 3493 | 123458 | 300 |
| 3494 | 123459 | 10 |
| 3495 | 123459 | 25 |
我希望能够将 table 简化为如下所示(如果这在 SQL 中完全可行的话)
| Transactions.Id | Payments.PaymentId | Transactions.Amount |
| ----------------| -------------------| --------------------|
| 3492 | 123456, 123457 | 123 |
| 3493 | 123458 | 300 |
| 3494, 3495 | 123459 | 35 |
...甚至没有第三列(如果它降低了复杂性)
| Transactions.Id | Payments.PaymentId |
| ----------------| -------------------|
| 3492 | 123456, 123457 |
| 3493 | 123458 |
| 3494, 3495 | 123459 |
我目前拥有的:
SELECT
Transactions.Id,
STUFF((SELECT '; ' + CAST(Payments.Id as varchar)
FROM Payments
WHERE Payments.TransactionId = Transactions.Id
FOR XML PATH('')), 1, 1, '') [PaymentIds]
FROM Transactions
| Transactions.Id | PaymentIds |
| ----------------| -------------------|
| 3492 | 123456, 123457 |
| 3493 | 123458 |
| 3494 | 123459 |
| 3495 | 123459 |
如何将 Transactions.Id 添加到逗号分隔列表中? 我正在使用 SQL 服务器 12.0.2000.8
基本上,您必须通过两次才能汇总每笔交易的付款,然后汇总每笔付款的交易。这假设(基于示例数据和所需的输出)当付款应用于多个交易时,您需要总和,但是当交易有多个付款时,您需要最大值(或者在这种情况下它们总是相同的)最大工作正常吗?):
;WITH FirstPass AS
(
SELECT t.Id, pId = STUFF(
(SELECT ', ' + CONVERT(varchar(11), p.Id)
FROM dbo.Payments AS p
WHERE p.TransactionId = t.Id
ORDER BY p.Id
FOR XML PATH(''), TYPE).value(N'./text()[1]',
N'varchar(max)'), 1, 2, ''),
Amount = MAX(t.Amount)
FROM dbo.Transactions AS t
GROUP BY t.Id
)
SELECT [Transactions.Id] = STUFF(
(SELECT ', ' + CONVERT(varchar(11), fp.Id)
FROM FirstPass AS fp
WHERE fp.pId = FirstPass.pId
ORDER BY fp.pId
FOR XML PATH(''), TYPE).value(N'./text()[1]',
N'varchar(max)'), 1, 2, ''),
[Payments.PaymentId] = FirstPass.pId,
Amount = SUM(FirstPass.Amount)
FROM FirstPass
GROUP BY FirstPass.pId;
输出:
Transactions.Id | Payments.PaymentId | Amount |
---|---|---|
3492 | 123456, 123457 | 123.00 |
3493 | 123458 | 300.00 |
3494, 3495 | 123459 | 35.00 |
在 SQL Server 2017+(或 Azure SQL 数据库)上,这变得更加简单,但对于这个特定的用例来说仍然有点复杂。这目前对您没有帮助,但可以帮助今天的其他读者、未来的读者,甚至未来的您:
;WITH FirstPass AS
(
SELECT t.Id, ca.pId, Amount = MAX(t.Amount)
FROM dbo.Transactions AS t
CROSS APPLY
(
SELECT pId = STRING_AGG(p.Id, ', ')
FROM dbo.Payments AS p
WHERE p.TransactionId = t.Id
) AS ca GROUP BY t.Id, ca.pId
)
SELECT [Transactions.Id] = STRING_AGG(Id, ', '),
[Payments.PaymentId] = pId,
Amount = SUM(Amount)
FROM FirstPass
GROUP BY pId;
相同的输出:
下面的代码生成你想要的输出
WITH cte1 AS(
SELECT
Transactions.Id AS TransactionID,
Payments.Id AS PaymentID,
max(Transactions.amount) Amount
FROM Transactions
LEFT JOIN Payments ON Transactions.Id = Payments.TransactionId
GROUP BY Transactions.Id,Payments.Id),
cte2 AS(
SELECT
STUFF((SELECT ',' + cast(t2.TransactionID as varchar(100))
FROM cte1 t2
WHERE t2.PaymentID = t1.PaymentID
FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'')TransactionID,
STUFF((SELECT ',' + cast(t2.PaymentID as varchar(100))
FROM cte1 t2
WHERE t2.TransactionID = t1.TransactionID
FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(MAX)'),1,1,'')PaymentID,
(SELECT sum(Amount)
FROM cte1 t2
WHERE t2.PaymentID = t1.PaymentID
GROUP BY PaymentID)Amount
FROM cte1 t1
)
SELECT TransactionID,PaymentID,Amount
FROM cte2
GROUP BY TransactionID,PaymentID,Amount