在sql server中将多行组合成一行

时间:2014-01-04 02:53:33

标签: sql-server sql-server-2008 sql-server-2012

我有一张这样的桌子。

+----+-------+---------+----------+---------+----------+---------+----------+
| id | Month | Debit_A | Credit_A | Debit_B | Credit_B | Debit_C | Credit_C |
+----+-------+---------+----------+---------+----------+---------+----------+
| 1  |  Jan  | 100.50  |          |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 2  |  Jan  |         |  100.50  |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 3  |  Jan  | 150.25  |          |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 4  |  Jan  |         |          |         |          | 300.00  |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 5  |  Jan  |         |          |         |          |         |  300.00  |
+----+-------+---------+----------+---------+----------+---------+----------+
| 6  |  Feb  |         |          |  79.80  |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 7  |  Feb  |         |          |         |   79.80  |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 8  |  Feb  |         |          | 200.00  |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 9  |  Feb  |         |          |         |  200.00  |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 10 |  Mar  |         |          |         |          | 1500.00 |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 11 |  Mar  |         |          |         |          |         | 1500.00  |
+----+-------+---------+----------+---------+----------+---------+----------+
| 12 |  Apr  | 100.00  |          |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 13 |  Apr  |         |  50.00   |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 14 |  May  |         |          |  50.75  |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 15 |  May  |         |          |         |  50.70   |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 16 |  Jun  |         |          |         |          |  75.50  |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 17 |  Jun  |         |          |         |          |         |  75.50   |
+----+-------+---------+----------+---------+----------+---------+----------+
| 18 |  Jun  |         |          |         |          |  75.50  |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 19 |  Jun  |         |          |         |          |         |  75.50   |
+----+-------+---------+----------+---------+----------+---------+----------+
| 20 |  Jul  |  89.50  |          |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+

我想要的是将具有相同Month值和相同Debit / Credit值的行组合到新表中。这会将借记和贷记行合并为一行。 例如:

+----+-------+---------+----------+---------+----------+---------+----------+
| id | Month | Debit_A | Credit_A | Debit_B | Credit_B | Debit_C | Credit_C |
+----+-------+---------+----------+---------+----------+---------+----------+
| 1  |  Jan  | 100.50  |  100.50  |         |          | 300.00  |  300.00  |
+----+-------+---------+----------+---------+----------+---------+----------+
| 2  |  Jan  | 150.25  |          |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 3  |  Feb  |         |          |  79.80  |   79.80  |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 4  |  Feb  |         |          | 200.00  |  200.00  |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 5  |  Mar  |         |          |         |          | 1500.00 | 1500.00  |
+----+-------+---------+----------+---------+----------+---------+----------+
| 6  |  Apr  | 100.00  |          |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 7  |  Apr  |         |  50.00   |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 8  |  May  |         |          |  50.75  |  50.70   |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+
| 9  |  Jun  |         |          |         |          |  75.50  |  75.50   |
+----+-------+---------+----------+---------+----------+---------+----------+
| 10 |  Jun  |         |          |         |          |  75.50  |  75.50   |
+----+-------+---------+----------+---------+----------+---------+----------+
| 11 |  Jul  |  89.50  |          |         |          |         |          |
+----+-------+---------+----------+---------+----------+---------+----------+

如何在SQL Server 2008或SQL Server 2012中执行此操作?

3 个答案:

答案 0 :(得分:0)

如果我是你,我有两个解决方案可以实现这一目标:

  1. 使用临时表(物理表或临时表{#Table或## Table})并多次更新行以设置这些字段的值。
  2. 使用游标(我通常不喜欢游标,因为它会影响数据库的性能,说实话,我对游标的技能非常差)。
  3. 对于临时表

    1. 我会进行初始插入并在beggining
    2. 中将借方值设置为0
    3. 为每种借记类型
    4. 进行更新

      <强>更新

      SELECT id, Month, 0 AS Debit_A, 0 AS Credit_A, 0 AS Debit_B, 0 AS Credit_B, 0 AS Debit_C, 0 AS Credit_C
      INTO #TempTable
      FROM YourTable
      GROUP BY Month
      
      UPDATE T
      SET Debit_A = Value
      FROM #TempTable T
      INNER JOIN DebitATable D
      ON T.Month = D.Month
      

      我会使用类似的东西(抱歉,因为它不太通用,因为我不知道数据库的结构)

      您可以使用GROUPSUM获取您在借记卡或信用卡值中获得的值

答案 1 :(得分:0)

-- 1st we import the raw account activity data
IF OBJECT_ID('dbo.RawActivity','U') IS NOT NULL
    DROP TABLE dbo.RawActivity;
GO

-- for the sake of mnemonics--and precedence when ordering--transactions
-- for account A, B, C and so on are defined as 0 for debits and 1 for
-- credits, and month short names are replaced with their number
CREATE TABLE dbo.RawActivity(id int,
                             M int,
                             A0 money,
                             A1 money,
                             B0 money,
                             B1 money,
                             C0 money,
                             C1 money);
GO
INSERT dbo.RawActivity
VALUES
(1, 1, 100.50, NULL, NULL, NULL, NULL, NULL),
(2, 1, NULL, 100.50, NULL, NULL, NULL, NULL),
(3, 1, 150.25, NULL, NULL, NULL, NULL, NULL),
(4, 1, NULL, NULL, NULL, NULL, 300.00, NULL),
(5, 1, NULL, NULL, NULL, NULL, NULL, 300.00),
(6, 2, NULL, NULL, 79.80, NULL, NULL, NULL),
(7, 2, NULL, NULL, NULL, 79.80, NULL, NULL),
(8, 2, NULL, NULL, 200.00, NULL, NULL, NULL),
(9, 2, NULL, NULL, NULL, 200.00, NULL, NULL),
(10 ,3, NULL, NULL, NULL, NULL, 1500.00, NULL),
(11, 3, NULL, NULL, NULL, NULL, NULL, 1500.00),
(12, 4, 100.00, NULL, NULL, NULL, NULL, NULL),
(13, 4, NULL, 50.00, NULL, NULL, NULL, NULL),
(14, 5, NULL, NULL, 50.75, NULL, NULL, NULL),
(15, 5, NULL, NULL, NULL, 50.70, NULL, NULL),
(16, 6, NULL, NULL, NULL, NULL, 75.50, NULL),
(17, 6, NULL, NULL, NULL, NULL, NULL, 75.50),
(18, 6, NULL, NULL, NULL, NULL, 75.50, NULL),
(19, 6, NULL, NULL, NULL, NULL, NULL, 75.50),
(20, 7, 89.50, NULL, NULL, NULL, NULL, NULL);
GO

-- 2nd we are going to flatten the raw data into a spagetti of transactions
-- with type T and parent transaction
IF OBJECT_ID('dbo.Activity','U') IS NOT NULL
    DROP TABLE dbo.Activity;
GO

CREATE TABLE dbo.Activity(id int, M int, amount money, T char(2), pid int);
GO
INSERT INTO dbo.Activity
SELECT  id,
        M,
        -- amount: the 1st non null
        ISNULL(A0, ISNULL(A1, ISNULL(B0, ISNULL(B1, ISNULL(C0, C1))))),
        -- type
        CASE
            WHEN A0 IS NOT NULL THEN 'A0'
            WHEN A1 IS NOT NULL THEN 'A1'
            WHEN B0 IS NOT NULL THEN 'B0'
            WHEN B1 IS NOT NULL THEN 'B1'
            WHEN C0 IS NOT NULL THEN 'C0'
            WHEN C1 IS NOT NULL THEN 'C1'
        END,
        -- parent id
        NULL
FROM dbo.RawActivity;

-- 3rd it gets interesting; we update the parent id with the 1st transaction
-- of each month, with help from FIRST_VALUE...
UPDATE dbo.Activity SET pid = P.pid
FROM
(
    SELECT  P.id,
            FIRST_VALUE(A.id) OVER(PARTITION BY A.M ORDER BY A.M, A.id) AS pid
    FROM    dbo.Activity AS P INNER JOIN
            dbo.Activity AS A ON P.id = A.id
) AS P
WHERE dbo.Activity.id = P.id;

-- we update same month and type transactions parent so that they are not
-- added together...
UPDATE  A
SET     A.pid = A.id
FROM    dbo.Activity AS A JOIN
        dbo.Activity AS B ON A.pid = B.id AND A.T = B.T AND A.M = B.M;

-- and we look for (type, parent) duplicates to update the parent; this
-- catches subsequent transactions of the same type. The naming of
-- transaction types pays off here because of the partitioning, and
-- the new parent will be the transaction right above the duplicate
-- TODO: test with more than two consecutive transactions of the same
-- type (there is none in your data)
WITH Duplicates AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY T, pid ORDER BY id) AS r FROM dbo.Activity
)
UPDATE Duplicates SET pid = id - 1 WHERE r > 1;

-- finally, we can pivot the whole thing to get back to the desired output;
-- the outer SELECT statement is just for beautification.
SELECT  ROW_NUMBER() OVER(ORDER BY M) AS id,
        FORMAT(DATEFROMPARTS(YEAR(GETDATE()), M, 1), 'MMM') AS [Month],
        A0 AS Debit_A,
        A1 AS Credit_A,
        B0 AS Debit_B,
        B1 AS Credit_B,
        C0 AS Debit_C,
        C1 AS Credit_C
FROM
(
    SELECT  M,
            SUM([A0]) AS A0,
            SUM([A1]) AS A1,
            SUM([B0]) AS B0,
            SUM([B1]) AS B1,
            SUM([C0]) AS C0,
            SUM([C1]) AS C1
    FROM
    (
        SELECT id, M, amount, T, pid FROM dbo.Activity
    ) AS A
    PIVOT
    (
        SUM(amount)
        FOR T IN ([A0], [A1], [B0], [B1], [C0], [C1])
    ) AS PVT
    GROUP BY M, pid
) AS Result;

答案 2 :(得分:-1)

Jan应该只有一行,而Debit_A = 250.75? 在标准SQL中,我猜你可以使用它,不确定它是否适合sql-server:

select sum(debit_a), sum(debit_b), .. from table name group by month;