2012-06-26 4 views
16

Мне нужна помощь в создании запроса MS SQL 2012, который будет соответствовать желаемому выходному этапу. Строки суммируют данные по одному диапазону дат (месяц отправки учетной записи), а столбцы суммируют его на другой диапазон дат (месяц даты платежа)Необходимая помощь SQL Stairstep

Таблица 1: Учетные записи учетные записи, размещаемые для коллекций.

CREATE TABLE [dbo].[Accounts](
    [AccountID] [nchar](10) NOT NULL, 
    [SubmissionDate] [date] NOT NULL, 
    [Amount] [money] NOT NULL, 
CONSTRAINT [PK_Accounts] PRIMARY KEY CLUSTERED (AccountID ASC)) 

INSERT INTO [dbo].[Accounts] VALUES ('1000', '2012-01-01', 1999.00) 
INSERT INTO [dbo].[Accounts] VALUES ('1001', '2012-01-02', 100.00) 
INSERT INTO [dbo].[Accounts] VALUES ('1002', '2012-02-05', 350.00) 
INSERT INTO [dbo].[Accounts] VALUES ('1003', '2012-03-01', 625.00) 
INSERT INTO [dbo].[Accounts] VALUES ('1004', '2012-03-10', 50.00) 
INSERT INTO [dbo].[Accounts] VALUES ('1005', '2012-03-10', 10.00) 

Таблица 2: Trans отслеживает платежи из

CREATE TABLE [dbo].[Trans](
    [TranID] [int] IDENTITY(1,1) NOT NULL, 
    [AccountID] [nchar](10) NOT NULL, 
    [TranDate] [date] NOT NULL, 
    [TranAmount] [money] NOT NULL, 
CONSTRAINT [PK_Trans] PRIMARY KEY CLUSTERED (TranID ASC)) 

INSERT INTO [dbo].[Trans] VALUES (1000, '2012-01-15', 300.00) 
INSERT INTO [dbo].[Trans] VALUES (1000, '2012-02-15', 300.00) 
INSERT INTO [dbo].[Trans] VALUES (1000, '2012-03-15', 300.00) 
INSERT INTO [dbo].[Trans] VALUES (1002, '2012-02-20', 325.00) 
INSERT INTO [dbo].[Trans] VALUES (1002, '2012-04-20', 25.00) 
INSERT INTO [dbo].[Trans] VALUES (1003, '2012-03-24', 625.00) 
INSERT INTO [dbo].[Trans] VALUES (1004, '2012-03-28', 31.00) 
INSERT INTO [dbo].[Trans] VALUES (1004, '2012-04-12', 5.00) 
INSERT INTO [dbo].[Trans] VALUES (1005, '2012-04-08', 7.00) 
INSERT INTO [dbo].[Trans] VALUES (1005, '2012-04-28', 3.00) 

Вот что желаемый результат должен выглядеть

        *Total Payments in Each Month* 
SubmissionYearMonth TotalAmount | 2012-01 2012-02 2012-03 2012-04 
-------------------------------------------------------------------- 
2012-01    2099.00  | 300.00 300.00 300.00  0.00 
2012-02    350.00  |   325.00  0.00 25.00 
2012-03    685.00  |     656.00 15.00 

Первые две колонки просуммировать Account.Amount группировку по месяцам.

Последние 4 столбца суммируют Tran.TranAmount по месяцам для Учетных записей, помещенных в данный месяц текущей строки.

Запрос, с которым я работал, чувствую себя близко. У меня просто нет лаги. Вот запрос, я работаю с до сих пор:

Select SubmissionYearMonth, 
     TotalAmount, 
     pt.[0] AS MonthOld0, 
     pt.[1] AS MonthOld1, 
     pt.[2] AS MonthOld2, 
     pt.[3] AS MonthOld3, 
     pt.[4] AS MonthOld4, 
     pt.[5] AS MonthOld5, 
     pt.[6] AS MonthOld6, 
     pt.[7] AS MonthOld7, 
     pt.[8] AS MonthOld8, 
     pt.[9] AS MonthOld9, 
     pt.[10] AS MonthOld10, 
     pt.[11] AS MonthOld11, 
     pt.[12] AS MonthOld12, 
     pt.[13] AS MonthOld13 

From (
     SELECT Convert(Char(4),Year(SubmissionDate)) + '-' + Right('00' + Convert(VarChar(2), DatePart(Month, SubmissionDate)),2) AS SubmissionYearMonth, 
     SUM(Amount) AS TotalAmount 
     FROM Accounts 
     GROUP BY Convert(Char(4),Year(SubmissionDate)) + '-' + Right('00' + Convert(VarChar(2), DatePart(Month, SubmissionDate)),2) 
    ) 
AS AccountSummary 
OUTER APPLY 
(
SELECT * 
FROM (
     SELECT CASE WHEN DATEDIFF(Month, SubmissionDate, TranDate) < 13 
        THEN DATEDIFF(Month, SubmissionDate, TranDate) 
        ELSE 13 
       END AS PaymentMonthAge, 
       TranAmount 
     FROM Trans INNER JOIN Accounts ON Trans.AccountID = Accounts.AccountID 
     Where Convert(Char(4),Year(TranDate)) + '-' + Right('00' + Convert(VarChar(2), DatePart(Month, TranDate)),2) 
      = AccountSummary.SubmissionYearMonth 
     ) as TransTemp 
     PIVOT (SUM(TranAmount) 
       FOR PaymentMonthAge IN ([0], 
             [1], 
             [2], 
             [3], 
             [4], 
             [5], 
             [6], 
             [7], 
             [8], 
             [9], 
             [10], 
             [11], 
             [12], 
             [13])) as TransPivot 
) as pt 

Это производит следующий вывод:

SubmissionYearMonth TotalAmount MonthOld0 MonthOld1 MonthOld2 MonthOld3 ... 
2012-01    2099.00  300.00 NULL  NULL  NULL  ... 
2012-02    350.00  325.00 300.00 NULL  NULL  ... 
2012-03    685.00  656.00 NULL  300.00 NULL  ... 

Что касается заголовков даты столбца. Я не уверен, что лучший вариант здесь. Я мог бы добавить дополнительный набор столбцов и создать расчетное значение, которое я мог бы использовать в результирующем отчете.

SQL Скрипка: http://www.sqlfiddle.com/#!6/272e5/1/0

Спасибо за любую помощь.

+9

** Отличная ** работа, включая образцы данных и запросы. Так каждый вопрос должен быть написан! – mellamokb

+3

@mellamokb: Плюс ссылка на [SQLFiddle] (http://sqlfiddle.com), и это было бы прекрасно :) –

+2

@juergend: Действительно. Вот запрос OP: http://www.sqlfiddle.com/#!3/56223/1 – mellamokb

ответ

1

Thomas, я использовал свой ответ, как вдохновение для следующего решения я в конечном итоге с помощью.

Сначала создаю TransmitDate, TranDate cross join скелетную матрицу даты, которую я позже использую, чтобы присоединиться к данным AccountSummary и TranSummary.

Полученный результат запроса не отформатирован в столбцах за месяц TranDate. Скорее я использую вывод в базе данных служб отчетов SQL Server и используя группировку столбцов, основанную на столбце TranSummaryMonthNum, для получения желаемого форматированного вывода.

SQL Fiddle version

; 
WITH 
    --Generate a list of Dates, from the first SubmissionDate, through today. 
    --Note: Requires the use of: 'OPTION (MAXRECURSION 0)' to generate a list with more than 100 dates. 
    CTE_AutoDates AS 
    (Select Min(SubmissionDate) as FiscalDate 
     From Accounts 
     UNION ALL 
     SELECT DATEADD(Day, 1, FiscalDate) 
     FROM CTE_AutoDates 
     WHERE DATEADD(Day, 1, FiscalDate) <= GetDate() 
    ), 

    FiscalDates As 
    (SELECT FiscalDate, 
      DATEFROMPARTS(Year(FiscalDate), Month(FiscalDate), 1) as FiscalMonthStartDate 
     FROM CTE_AutoDates 
     --Optionaly filter Fiscal Dates by the last known Math.Max(SubmissionDate, TranDate) 
     Where FiscalDate <= (Select Max(MaxDate) 
          From (Select Max(SubmissionDate) as MaxDate From Accounts 
           Union All 
           Select Max(TranDate) as MaxDate From Trans 
           ) as MaxDates 
         ) 
    ), 

    FiscalMonths as 
    (SELECT Distinct FiscalMonthStartDate 
     FROM FiscalDates 
    ), 

    --Matrix to store the reporting date groupings for the Account submission and payment periods. 
    SubmissionAndTranMonths AS 
    (Select AM.FiscalMonthStartDate as SubmissionMonthStartDate, 
      TM.FiscalMonthStartDate as TransMonthStartDate, 
      DateDiff(Month, (Select Min(FiscalMonthStartDate) From FiscalMonths), TM.FiscalMonthStartDate) as TranSummaryMonthNum 
     From FiscalMonths AS AM 
      Join FiscalMonths AS TM 
      ON TM.FiscalMonthStartDate >= AM.FiscalMonthStartDate 
    ), 

    AccountData as 
    (Select A.AccountID, 
      A.Amount, 
      FD.FiscalMonthStartDate as SubmissionMonthStartDate 
     From Accounts as A 
      Inner Join FiscalDates as FD 
      ON A.SubmissionDate = FD.FiscalDate 
    ), 


    TranData as 
    (Select T.AccountID, 
      T.TranAmount, 
      AD.SubmissionMonthStartDate, 
      FD.FiscalMonthStartDate as TranMonthStartDate 
     From Trans as T 
      Inner Join AccountData as AD 
      ON T.AccountID = AD.AccountID 
      Inner Join FiscalDates AS FD 
      ON T.TranDate = FD.FiscalDate 
    ), 

    AccountSummaryByMonth As 
    (Select ASM.FiscalMonthStartDate, 
      Sum(AD.Amount) as TotalSubmissionAmount 
     From FiscalMonths as ASM 
      Inner Join AccountData as AD 
      ON ASM.FiscalMonthStartDate = AD.SubmissionMonthStartDate 
     Group By 
      ASM.FiscalMonthStartDate 
    ), 

    TranSummaryByMonth As 
    (Select STM.SubmissionMonthStartDate, 
      STM.TransMonthStartDate, 
      STM.TranSummaryMonthNum, 
      Sum(TD.TranAmount) as TotalTranAmount 
     From SubmissionAndTranMonths as STM 
      Inner Join TranData as TD 
      ON STM.SubmissionMonthStartDate = TD.SubmissionMonthStartDate 
       AND STM.TransMonthStartDate = TD.TranMonthStartDate 
     Group By 
      STM.SubmissionMonthStartDate, 
      STM.TransMonthStartDate, 
      STM.TranSummaryMonthNum 
    ) 

--#Inspect 1 
--Select * From SubmissionAndTranMonths 
--OPTION (MAXRECURSION 0) 

--#Inspect 1 Results 
--SubmissionMonthStartDate TransMonthStartDate TranSummaryMonthNum 
--2012-01-01    2012-01-01   0 
--2012-01-01    2012-02-01   1 
--2012-01-01    2012-03-01   2 
--2012-01-01    2012-04-01   3 
--2012-02-01    2012-02-01   1 
--2012-02-01    2012-03-01   2 
--2012-02-01    2012-04-01   3 
--2012-03-01    2012-03-01   2 
--2012-03-01    2012-04-01   3 
--2012-04-01    2012-04-01   3 

--#Inspect 2 
--Select * From AccountSummaryByMonth 
--OPTION (MAXRECURSION 0) 

--#Inspect 2 Results 
--FiscalMonthStartDate TotalSubmissionAmount 
--2012-01-01   2099.00 
--2012-02-01   350.00 
--2012-03-01   685.00 

--#Inspect 3 
--Select * From TranSummaryByMonth 
--OPTION (MAXRECURSION 0) 

--#Inspect 3 Results 
--SubmissionMonthStartDate TransMonthStartDate TranSummaryMonthNum TotalTranAmount 
--2012-01-01    2012-01-01   0     300.00 
--2012-01-01    2012-02-01   1     300.00 
--2012-01-01    2012-03-01   2     300.00 
--2012-02-01    2012-02-01   1     325.00 
--2012-02-01    2012-04-01   3     25.00 
--2012-03-01    2012-03-01   2     656.00 
--2012-03-01    2012-04-01   3     15.00 

Select STM.SubmissionMonthStartDate, 
     ASM.TotalSubmissionAmount, 
     STM.TransMonthStartDate, 
     STM.TranSummaryMonthNum, 
     TSM.TotalTranAmount 
From SubmissionAndTranMonths as STM 
    Inner Join AccountSummaryByMonth as ASM 
     ON STM.SubmissionMonthStartDate = ASM.FiscalMonthStartDate 
    Left Join TranSummaryByMonth AS TSM 
     ON STM.SubmissionMonthStartDate = TSM.SubmissionMonthStartDate 
      AND STM.TransMonthStartDate = TSM.TransMonthStartDate 
Order By STM.SubmissionMonthStartDate, STM.TranSummaryMonthNum 
OPTION (MAXRECURSION 0) 

--#Results 
--SubmissionMonthStartDate TotalSubmissionAmount TransMonthStartDate TranSummaryMonthNum TotalTranAmount 
--2012-01-01    2099.00    2012-01-01   0     300.00 
--2012-01-01    2099.00    2012-02-01   1     300.00 
--2012-01-01    2099.00    2012-03-01   2     300.00 
--2012-01-01    2099.00    2012-04-01   3     NULL 
--2012-02-01    350.00    2012-02-01   1     325.00 
--2012-02-01    350.00    2012-03-01   2     NULL 
--2012-02-01    350.00    2012-04-01   3     25.00 
--2012-03-01    685.00    2012-03-01   2     656.00 
--2012-03-01    685.00    2012-04-01   3     15.00 
+3

Уважаемый Натан, если кто-то помог вам по существу с вашим запросом, то, на мой взгляд, вы должны выбрать ответ этого человека вместо того, чтобы предоставлять свои собственные и выбирать его. – ErikE

1

Следующий запрос в значительной степени возвращает то, что вы хотите. Вам нужно выполнять операции отдельно. Я просто присоединился к результатам вместе:

select a.yyyymm, a.Amount, 
     t201201, t201202, t201203, t201204 
from (select LEFT(convert(varchar(255), a.submissiondate, 121), 7) as yyyymm, 
       SUM(a.Amount) as amount 
     from Accounts a 
     group by LEFT(convert(varchar(255), a.submissiondate, 121), 7) 
    ) a left outer join 
     (select LEFT(convert(varchar(255), a.submissiondate, 121), 7) as yyyymm, 
       sum(case when trans_yyyymm = '2012-01' then tranamount end) as t201201, 
       sum(case when trans_yyyymm = '2012-02' then tranamount end) as t201202, 
       sum(case when trans_yyyymm = '2012-03' then tranamount end) as t201203, 
       sum(case when trans_yyyymm = '2012-04' then tranamount end) as t201204 
     from Accounts a join 
      (select t.*, LEFT(convert(varchar(255), t.trandate, 121), 7) as trans_yyyymm 
      from trans t 
      ) t 
      on a.accountid = t.accountid 
     group by LEFT(convert(varchar(255), a.submissiondate, 121), 7) 
    ) t 
     on a.yyyymm = t.yyyymm 
order by 1 

Я получаю NULL, где у вас 0,00 в двух ячейках.

+0

Конечные точки диапазона дат не известны в момент запроса. Каждый раз, когда запрос выполняется, он создает список строк для всех известных месяцев представления отчетности, а также фиксированное или потенциально изменяемое количество столбцов суммарного количества платежей. Несколько дополнительных нулей не имеют большого значения. – Nathan

5

Поскольку вы используете SQL Server 2012, мы можем использовать функцию Format, чтобы сделать дату красивой. Нет необходимости группировать строки. Вместо этого я считаю полезным использовать правильный тип данных до тех пор, пока я могу, и использовать только Format или Convert на дисплее (или вообще нет, и пусть средний уровень обрабатывает дисплей).

В этом решении я произвольно принял самый ранний TransDate и извлек из него первый день этого месяца. Однако можно легко заменить это выражение статическим значением желаемой даты начала, и это решение займет это и следующие 12 месяцев.

With SubmissionMonths As 
    (
    Select DateAdd(d, -Day(A.SubmissionDate) + 1, A.SubmissionDate) As SubmissionMonth 
    , A.Amount 
    From dbo.Accounts As A 
) 
    , TranMonths As 
    (
    Select DateAdd(d, -Day(Min(T.TranDate)) + 1, Min(T.TranDate)) As TranMonth 
     , 1 As MonthNum 
    From dbo.Accounts As A 
    Join dbo.Trans As T 
     On T.AccountId = A.AccountId 
    Join SubmissionMonths As M 
     On A.SubmissionDate >= M.SubmissionMonth 
     And A.SubmissionDate < DateAdd(m,1,SubmissionMonth) 
    Union All 
    Select DateAdd(m, 1, TranMonth), MonthNum + 1 
    From TranMonths 
    Where MonthNum < 12 
) 
    , TotalBySubmissionMonth As 
    (
    Select M.SubmissionMonth, Sum(M.Amount) As Total 
    From SubmissionMonths As M 
    Group By M.SubmissionMonth 
) 
Select Format(SMT.SubmissionMonth,'yyyy-MM') As SubmissionMonth, SMT.Total 
    , Sum(Case When TM.MonthNum = 1 Then T.TranAmount End) As Month1 
    , Sum(Case When TM.MonthNum = 2 Then T.TranAmount End) As Month2 
    , Sum(Case When TM.MonthNum = 3 Then T.TranAmount End) As Month3 
    , Sum(Case When TM.MonthNum = 4 Then T.TranAmount End) As Month4 
    , Sum(Case When TM.MonthNum = 5 Then T.TranAmount End) As Month5 
    , Sum(Case When TM.MonthNum = 6 Then T.TranAmount End) As Month6 
    , Sum(Case When TM.MonthNum = 7 Then T.TranAmount End) As Month7 
    , Sum(Case When TM.MonthNum = 8 Then T.TranAmount End) As Month8 
    , Sum(Case When TM.MonthNum = 9 Then T.TranAmount End) As Month9 
    , Sum(Case When TM.MonthNum = 10 Then T.TranAmount End) As Month10 
    , Sum(Case When TM.MonthNum = 11 Then T.TranAmount End) As Month11 
    , Sum(Case When TM.MonthNum = 12 Then T.TranAmount End) As Month12 
From TotalBySubmissionMonth As SMT 
    Join dbo.Accounts As A 
    On A.SubmissionDate >= SMT.SubmissionMonth 
     And A.SubmissionDate < DateAdd(m,1,SMT.SubmissionMonth) 
    Join dbo.Trans As T 
    On T.AccountId = A.AccountId 
    Join TranMonths As TM 
    On T.TranDate >= TM.TranMonth 
     And T.TranDate < DateAdd(m,1,TM.TranMonth) 
Group By SMT.SubmissionMonth, SMT.Total 

SQL Fiddle version

+0

Это выглядит очень многообещающим. Я собираюсь немного поиграть с ним и посмотреть, смогу ли я использовать эту реализацию. Благодарю. – Nathan

+0

+1 за хороший ответ. Кроме того, OP должен был отметить ваш ответ как выбранный, потому что он помог ему получить решение. – ErikE

1

Следующий запрос в точности повторяет результаты your final query in your own answer, но занимает не более 1/30-й центральный процессор (или лучше), плюс в целом гораздо проще.

Если бы у меня было время & энергия, я уверен, что смогу найти еще больше улучшений ... моя кишка говорит мне, что мне может не ударить по таблице Accounts столько раз. Но в любом случае это огромное улучшение и должно очень хорошо работать даже для очень больших наборов результатов.

См. the SqlFiddle for it.

WITH L0 AS (SELECT 1 N UNION ALL SELECT 1), 
L1 AS (SELECT 1 N FROM L0, L0 B), 
L2 AS (SELECT 1 N FROM L1, L1 B), 
L3 AS (SELECT 1 N FROM L2, L2 B), 
L4 AS (SELECT 1 N FROM L3, L2 B), 
Nums AS (SELECT N = Row_Number() OVER (ORDER BY (SELECT 1)) FROM L4), 
Anchor AS (
    SELECT MinDate = DateAdd(month, DateDiff(month, '20000101', Min(SubmissionDate)), '20000101') 
    FROM dbo.Accounts 
), 
MNums AS (
    SELECT N 
    FROM Nums 
    WHERE 
     N <= DateDiff(month, 
     (SELECT MinDate FROM Anchor), 
     (SELECT Max(TranDate) FROM dbo.Trans) 
    ) + 1 
), 
A AS (
    SELECT 
     AM.AccountMo, 
     Amount = Sum(A.Amount) 
    FROM 
     dbo.Accounts A 
     CROSS APPLY (
     SELECT DateAdd(month, DateDiff(month, '20000101', A.SubmissionDate), '20000101') 
    ) AM (AccountMo) 
    GROUP BY 
     AM.AccountMo 
), T AS (
    SELECT 
     AM.AccountMo, 
     TM.TranMo, 
     TotalTranAmount = Sum(T.TranAmount) 
    FROM 
     dbo.Accounts A 
     CROSS APPLY (
     SELECT DateAdd(month, DateDiff(month, '20000101', A.SubmissionDate), '20000101') 
    ) AM (AccountMo) 
     INNER JOIN dbo.Trans T 
     ON A.AccountID = T.AccountID 
     CROSS APPLY (
     SELECT DateAdd(month, DateDiff(month, '20000101', T.TranDate), '20000101') 
    ) TM (TranMo) 
    GROUP BY 
     AM.AccountMo, 
     TM.TranMo 
) 
SELECT 
    SubmissionStartMonth = A.AccountMo, 
    TotalSubmissionAmount = A.Amount, 
    M.TransMonth, 
    TransMonthNum = N.N - 1, 
    T.TotalTranAmount 
FROM 
    A 
    INNER JOIN MNums N 
     ON N.N >= DateDiff(month, (SELECT MinDate FROM Anchor), A.AccountMo) + 1 
    CROSS APPLY (
     SELECT TransMonth = DateAdd(month, N.N - 1, (SELECT MinDate FROM Anchor)) 
    ) M 
    LEFT JOIN T 
     ON A.AccountMo = T.AccountMo 
     AND M.TransMonth = T.TranMo 
ORDER BY 
    A.AccountMo, 
    M.TransMonth; 
Смежные вопросы