Динамическое сжатие разных строк в одной и той же таблице SQL - PullRequest
0 голосов
/ 02 апреля 2019

Клиенту нужен инструмент, чтобы показать неудачные ежемесячные платежи клиентов несколько месяцев назад. Начальный месяц и количество месяцев назад должны быть определены пользователем все платежи заносятся в одну таблицу. Там могут быть или не быть платежи одним и тем же клиентом в предыдущие месяцы. Это таблица с несколькими строками для каждого платежа, поэтому мне нужно использовать group by.

Я пытаюсь найти наилучший способ запроса информации.

Я полагаю, что он должен быть построен динамически (я прав? Можно ли это сделать без динамического SQL?), И видел различные подходы при поиске в Интернете:

  1. динамически добавлять строки в предложении select, используя регистры, например, такой запрос: SQL-запрос для сравнения продаж продукта по месяцам

  2. динамически добавлять UNIONS в одну и ту же таблицу для каждого сравниваемого месяца

  3. динамически добавлять ЛЕВЫЕ СОЕДИНЕНИЯ в одну и ту же таблицу для каждого сравниваемого месяца

Код концепции для подхода 1:

DECLARE @month int, @monthNum int
SET @month = 103
SET @monthNum = 1

SELECT
    name,ID,Property
    ,MAX(CASE WHEN month = @month THEN month ELSE 0 END) AS month 
    ,SUM(CASE WHEN month = @month THEN paymentSum ELSE 0 END) AS paymentSum 
    ,MAX(CASE WHEN month = @month THEN PaymentFailReason ELSE 0 END) AS PaymentFailReason
    ,MAX(CASE WHEN month = @month -1 THEN month ELSE 0 END) AS monthMinusOne
    ,SUM(CASE WHEN month = @month -1 THEN paymentSum ELSE 0 END) AS paymentSumMinusOne
    ,MAX(CASE WHEN month = @month -1 THEN PaymentFailReason ELSE 0 END) AS PaymentFailReasonMinusOne

FROM
    payments

WHERE
    month BETWEEN @month -@monthNum AND @month AND status = 2

GROUP BY
name,ID,Property

Статус = 2 означает, что платеж был неудачным.

Есть ли предпочтительное решение в таком случае?

Один подход более эффективен, чем другой?

Пример данных:

month   ID  name    Property    paymentSum  PaymentFailReason   Status
100      1  Aron       A            100       Has No money         2
100      2  Burt       B            100       Has No money         2
100      3  Carl       C            50        Has No money         2
101      1  Aron       A            50        Has No money         2
101      2  Burt       B            50        Has No money         2
101      3  Carl       C            50        Has No money         2
102      1  Aron       A            100       Has No money         2
102      2  Burt       B            100                            1
102      3  Carl       C            100       Has No money         2
103      1  Aron       A            102       Has No money         2
103      2  Burt       B            102       Has No money         2
103      3  Carl       C            102                            1 

Результаты запроса вышеуказанных данных за месяц № 103 и 1 месяц назад должны быть:

month   ID  name    Property    paymentSum  PaymentFailReason   Status  monthMinusOne   paymentSumMinusOne  PaymentFailReasonMinusOne
103      1  Aron        A            102       Has No money        2         102                100               Has No money
103      2  Burt        B            102       Has No money        0         102                0                 0

Карл заплатил успешно на 103, поэтому он не в результатах.

Берт успешно заплатил 102, поэтому его данные за этот месяц неактуальны (все нули).

Edit: Я создал динамический SQL-запрос, который зацикливается на создание случаев на количество месяцев назад, определенных пользователями. занимает много времени ... Чего мне не хватает? ЛЕВЫЕ СОЕДИНЕНИЯ И СОЮЗЫ были бы лучше?

1 Ответ

0 голосов
/ 07 апреля 2019

Разобрался сам.Надеюсь, что это помогает другим ...

Вариант 1, очевидно, прекрасно работает.по какой-то причине, когда я впервые запустил его в SSMS, это заняло более 10 секунд в течение 20 месяцев назад - но это было только при первом запуске.

Странно.(Может сервер адаптируется после первого запуска?)

С тех пор он работает очень быстро, меньше секунды за много месяцев назад.

вот что я сделал: (Улучшения и замечания по стилю)был бы очень признателен!)

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[CompareFailedPayments](@month int , @NumOfMonths int) 
AS

BEGIN
DECLARE @SqlHead nvarchar(max), @Sql nvarchar(max)='', @Crlf nvarchar(max), @counter int
    SET @counter = 0
    SET @Crlf = CHAR(13)+CHAR(10)

SET @SqlHead = 'DECLARE @month int = '+ CAST(@month AS VARCHAR) + @Crlf +
    'SELECT name,IDnumber,Property, ID, month, paymentsum, PaymentFailReason, EncryptedCardNum '

    SET @Sql =  'SELECT name,IDnumber,Property,Payments.ID as Id, Payments.Property as p'+ @Crlf +
    ',MAX(CASE WHEN month = @month THEN month ELSE NULL END) AS month '+ @Crlf +
    ',SUM(CASE WHEN month = @month THEN paymentsum ELSE NULL END) AS paymentsum '+ @Crlf +
    ',MAX(CASE WHEN month = @month THEN PaymentFailReason ELSE NULL END) AS PaymentFailReason '+ @Crlf +
    ',MAX(CASE WHEN month = @month THEN RIGHT(EncryptedCardNum,4) ELSE NULL END) AS EncryptedCardNum '+ @Crlf +
    'FROM Payments '+ @Crlf +
    'INNER JOIN'+ @Crlf +
    'client ON Payments.ID = client.ID'+ @Crlf +
    'WHERE (Payments.month = '+ CAST(@month AS VARCHAR) +') AND (Payments.status = 2)'+ @Crlf +
    'GROUP BY client.name, client.IDnumber, Payments.Property,Payments.ID, Payments.Property '+ @Crlf +
    ') AS Base'+ @Crlf +
    'LEFT OUTER JOIN'+ @Crlf +
    '('+ @Crlf +
    'SELECT Payments.ID as Id1, Payments.Property as p1'+ @Crlf+ @Crlf

    WHILE @counter < @NumOfMonths

        BEGIN
        SET @counter = @counter+1

        SET @Sql = @Sql+',MAX(CASE WHEN month = @month-'+CAST(@counter AS VARCHAR)+' THEN month'+' ELSE NULL END) AS month'+CAST(@counter AS VARCHAR)+ @Crlf +
        ',SUM(CASE WHEN month = @month-'+CAST(@counter AS VARCHAR)+' THEN paymentsum ELSE NULL END) AS paymentsum'+CAST(@counter AS VARCHAR)+ @Crlf +
        ',MAX(CASE WHEN month = @month-'+CAST(@counter AS VARCHAR)+' THEN PaymentFailReason ELSE NULL END) AS PaymentFailReason'+CAST(@counter AS VARCHAR)+ @Crlf +
        ',MAX(CASE WHEN month = @month-'+CAST(@counter AS VARCHAR)+' THEN RIGHT(EncryptedCardNum,4) ELSE NULL END) AS EncryptedCardNum'+CAST(@counter AS VARCHAR)+ @Crlf + @Crlf 

         SET @SqlHead = @SqlHead +', month'+CAST(@counter AS VARCHAR)+', paymentsum'+CAST(@counter AS VARCHAR)+', PaymentFailReason'+CAST(@counter AS VARCHAR)+', EncryptedCardNum'+CAST(@counter AS VARCHAR)+''

        END

     SET @SqlHead = @SqlHead +'  FROM(' + @Crlf

     SET @Sql = @Sql+
    'FROM            Payments'+ @Crlf +
    'WHERE        (Payments.month BETWEEN '+CAST(@month-@NumOfMonths AS VARCHAR)+' AND ' +CAST(@month-1 AS VARCHAR)+') AND (Payments.status = 2)'+ @Crlf +
    'GROUP BY Payments.ID, Payments.Property'+ @Crlf +
    ') AS Back '+ @Crlf +
    'ON Base.ID = Back.m1 and Base.h = Back.h1'  

--PRINT(@sqlHead+@Sql)

EXEC(@sqlHead+@Sql) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...