简体   繁体   English

汇总 SQL 数据库中具有多对多关系的两列

[英]Summarize two columns that have a many-to-many relationship in an SQL database

I have the following database tables Transactions and Payments .我有以下数据库表TransactionsPayments The data is structured as such that both Transactions and Payments have a many-to-many relationship, which are joined together in a query as such:数据的结构使得交易和付款都具有多对多关系,它们在查询中连接在一起,如下所示:

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                  |

I want to be able to simplify the table into something like below (if this is at all even possible in SQL)我希望能够将表简化为如下所示(如果这在 SQL 中是可能的)

| Transactions.Id | Payments.PaymentId | Transactions.Amount |
| ----------------| -------------------| --------------------|
| 3492            | 123456, 123457     | 123                 |
| 3493            | 123458             | 300                 |
| 3494, 3495      | 123459             | 35                  |

...or even without the third column (if it reduces complexity) ...甚至没有第三列(如果它降低了复杂性)

| Transactions.Id | Payments.PaymentId |
| ----------------| -------------------|
| 3492            | 123456, 123457     |
| 3493            | 123458             |
| 3494, 3495      | 123459             |

What I have so far:到目前为止我所拥有的:

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             |

How do I add Transactions.Id into a comma separated list as well?如何将 Transactions.Id 添加到逗号分隔列表中? I am using SQL Server 12.0.2000.8我正在使用 SQL 服务器 12.0.2000.8

SQLFiddle here SQLFiddle在这里

Basically, you have to take two passes in order to aggregate the payments per transaction and then the transaction per payment aggregate.基本上,您必须通过两次才能汇总每笔交易的付款,然后汇总每笔付款的交易。 This assumes (based on the sample data and desired output) that when a payment applies to multiple transactions, you want the sum, but when a transaction has multiple payments, you want the max (or they'll always be the same in which case max works just fine?):这假设(基于样本数据和所需的输出)当付款适用于多个交易时,您需要总和,但是当交易有多个付款时,您需要最大值(或者在这种情况下它们总是相同的max 工作正常吗?):

;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;

Output: Output:

Transactions.Id交易.Id Payments.PaymentId Payments.PaymentId Amount数量
3492 3492 123456, 123457 123456, 123457 123.00 123.00
3493 3493 123458 123458 300.00 300.00
3494, 3495 3494, 3495 123459 123459 35.00 35.00

On SQL Server 2017+ (or Azure SQL Database) this becomes much simpler, though still a little convoluted for this specific use case.在 SQL Server 2017+(或 Azure SQL 数据库)上,这变得更加简单,尽管对于这个特定的用例来说仍然有点复杂。 This doesn't help you right now, but could help other readers today, future readers, or even future you:这对你现在没有帮助,但可以帮助今天的其他读者、未来的读者,甚至未来的你:

;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;

Same output:同 output:

The following code generates the output you want下面的代码生成你想要的output

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

result 结果

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM