反复水平加入数据

时间:2013-07-28 03:57:06

标签: sql sql-server tsql

我有两个看起来像这样的表:

Categories ( CategoryId, Type, Name )
Transactions ( TxnId, DateTime, Amount, CategoryId )

我想每个月获得每个类别的所有交易SUM

我可以通过设置一个sproc(见下文)来生成这个,它为每个类别生成给定日期范围的总和(例如一个月2013-07-01 <= Transactions.DateTime < 2013-08-01。但是我不知道如何为所有月份做到这一点在数据库中,所以我决定我可以反复运行sproc(给定不同的最小/最大日期)来获得不同月份的总和。

我想将结果合并到一个表中。

我的类别sum sproc看起来像这样:

SELECT
    Categories.Name,
    Categories.Type,
    Categories.CategoryId,
    CategorySpendingForMonth.Amount
FROM
    Categories
    LEFT OUTER JOIN (

        SELECT
            CategoryId,
            SUM(Amount) AS Amount
        FROM
            Txns
        WHERE
            [DateTime] >= @fromDate
            AND
            [DateTime] < @toDate
        GROUP BY
            CategoryId

    ) AS CategorySpendingForMonth ON Categories.CategoryId = CategorySpendingForMonth.CategoryId
ORDER BY
    Categories.Type,
    Categories.Name

我的结果如下:

Name        Type    CatId    Amount
Car         0       9       -183.22
Electricity 0       12      -86.05
Insurance   0       17      NULL
Internet    0       14      -39.99
Phone       0       23      -50.04
Rent        0       19      -2284.80
Xoom        0       25      -604.99

但我希望它看起来像这样:

Name        Type    CatId    June       July        Aug
Car         0       9       -183.22     -183.22     -183.22
Electricity 0       12      -86.05      -86.05      -86.05
Insurance   0       17      NULL        -12.30      NULL
Internet    0       14      -39.99      -39.99      -39.99
Phone       0       23      -50.04          -50.04  -50.04
Rent        0       19      -2284.80    -2284.80    -2284.80
Xoom        0       25      -604.99     -604.99     -604.99

(并在额外的月份添加其他列)

所以我想我可以简单地为每个月添加数据来调用我的sproc,但我不知道如何将我现有的数据加入到这里(如果它是垂直的我会使用UNION ,但这是横向的。

这就是我的想法(无效的SQL):

SELECT 
    Name,
    Type,
    CategoryId,
    Amount
FROM
    EXEC CategoriesSummary( '2013-07-01', '2013-08-01' )
    FOR i = 0 TO 12
        LEFT OUTER JOIN CategoriesSummary( '2013-' + (7 + i) +'-01', '2013-' + (8 + 1) + '-01' )
    NEXT

....但那是无效的,那么解决这个问题的最佳方法是什么?

谢谢!

2 个答案:

答案 0 :(得分:1)

要获得更一般的解决方案,请在其他问题https://stackoverflow.com/a/13583258/1744834中查看我的答案。

对于不太通用的解决方案:),您可以使用PIVOT

declare @Year int = 2013

;with CTE as
(
    select C.Name, datepart(mm, T.DateTime) as M, sum(T.Amount) as Amount
    from Txns as T
        left outer join Categories as C on C.categoryId = T.categoryID
    where
        T.DateTime >= dateadd(yy, 2013 - datepart(yy, 0), 0) and
        T.DateTime < dateadd(yy, 2013 - datepart(yy, 0) + 1, 0)
    group by C.Name, datepart(mm, T.DateTime)
)
select
    Name,
    p.[1] as [January],
    p.[2] as [February],
    p.[3] as [March],
    p.[4] as [April],
    p.[5] as [May],
    p.[6] as [June],
    p.[7] as [July],
    p.[8] as [August],
    p.[9] as [September],
    p.[10] as [October],
    p.[11] as [November],
    p.[12] as [December]
from CTE as C
pivot 
(
    sum(Amount)
    for M in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) as p

请参阅SQL FIDDLE EXAMPLE

答案 1 :(得分:0)

在这种情况下,你可能最好使用表值函数。

我无法验证语法,但是这样:

CREATE FUNCTION dbo.tvf(@fromDate DATETIME)
RETURNS @rows TABLE 
(
    val UNIQUEIDENTIFIER PRIMARY KEY
)
AS 
BEGIN
INSERT INTO @rows
SELECT
    valId AS val
FROM
    Values
WHERE changed > @fromDate
ORDER BY
    valid
RETURN
END

然后您可以像以下一样使用您的TVF:

SELECT * FROM dbo.tvf('1/1/2000')

TVF的文档在这里:http://msdn.microsoft.com/en-us/library/ms191165(v=SQL.105).aspx