Можно ли сделать мой запрос более эффективным, и как насчет добавления предложения GROUP BY? - PullRequest
0 голосов
/ 22 сентября 2019

Я пытаюсь написать запрос, который возвращает:

  1. Общая сумма транзакций, которые произошли до диапазона дат, для конкретного клиента.
  2. Общая сумма транзакцийдля определенного клиента.
  3. Общая сумма платежей, выполненных до определенного диапазона дат, для конкретного клиента.
  4. Общая сумма платежей, произошедших в течение определенного периода.диапазон дат для конкретного клиента.

С этой целью я пришел к следующему запросу.

declare @StartDate DATE = '2016-08-01'
declare @EndDate DATE = '2016-08-31'
declare @BillingCategory INT = 0

select c.Id, c.Name, c.StartingBalance,
(select coalesce(sum(Amount), 0) from Transactions t where t.CustomerId = c.Id and t.[Date] < @StartDate) xDebits,
(select coalesce(sum(Amount), 0) from Transactions t where t.CustomerId = c.Id and t.[Date] >= @StartDate and t.[Data] <= @EndDate) Debits,
(select coalesce(sum(Amount), 0) from Payments p where p.CustomerId = c.Id and p.[Date] < @StartDate) xCredits,
(select coalesce(sum(Amount), 0) from Payments p where p.CustomerId = c.Id and p.[Date] >= @StartDate and p.[Date] <= @EndDate) Credits
from customers c
where c.BillingCategory in (0,1,2,3,4,5)

Этот запрос, похоже, дает желаемые результаты.Я использовал подзапросы, потому что не мог понять, как сделать то же самое, используя JOIN s.Но у меня есть несколько вопросов.

  1. Извлекает ли этот запрос данные о транзакциях и платежах для каждого отдельного клиента перед его фильтрацией в соответствии с моим условием WHERE?Если это так, это кажется большой тратой.Можно ли это улучшить?

  2. Я также хотел бы добавить GROUP BY для суммирования каждого столбца платежей и транзакций на BillingCategory.Но как можно добавить здесь предложение GROUP BY, если столбцы SELECT ed ограничены агрегатными функциями, если их нет в предложении GROUP BY?

Transactionsи таблицы Payments имеют внешние ключи к таблице Customers.

Образцы данных (не реальные)

Клиенты:

Id    Name    BillingCategory
----- ------- ---------------
1     'ABC'   0
2     'DEF'   1
3     'GHI'   0

уже купили:

Id    CustomerId Date         Amount
----- ---------- ------------ ------
1     2          '2016-08-01' 124.90
2     2          '2016-08-04' 37.23
3     1          '2016-08-27' 450.02

Платежи:

Id    CustomerId Date         Amount
----- ---------- ------------ ------
1     1          '2016-09-01' 50.00
2     1          '2016-09-23' 75.00
3     2          '2016-09-01' 100.00

Ответы [ 2 ]

2 голосов
/ 22 сентября 2019

Вы можете построить свои суммы отдельно для Transactions и Payments в CTE, а затем соединить их вместе:

WITH
    CustomerTransactions AS
        (
            SELECT CustomerId,
                    SUM(CASE WHEN [Date] < @StartDate THEN 1 ELSE 0 END * COALESCE(Amount, 0)) AS xDebits,
                    SUM(CASE WHEN [Date] >= @StartDate THEN 1 ELSE 0 END * COALESCE(Amount, 0)) AS Debits
                FROM Transactions
                GROUP BY CustomerId
        ),
    CustomerPayments AS,
        (
            SELECT CustomerId,
                    SUM(CASE WHEN [Date] < @StartDate THEN 1 ELSE 0 END * COALESCE(Amount, 0)) AS xCredits,
                    SUM(CASE WHEN [Date] >= @StartDate THEN 1 ELSE 0 END * COALESCE(Amount, 0)) AS Credits
                FROM Payments
                GROUP BY CustomerId
        )
SELECT C.Id, c.Name, c.StartingBalance,
        COALESCE(T.xDebits, 0) AS xDebits,
        COALESCE(T.Debits, 0) AS Debits,
        COALESCE(P.xCredits, 0) AS xCredits,
        COALESCE(P.Credits, 0) AS Credits
    FROM Custormers C
        LEFT OUTER JOIN CustomerTransactions T ON T.CustomerId = C.Id
        LEFT OUTER JOIN CustomerPayments P ON P.CustomerId = C.Id
    WHERE C.BillingCategory IN(0, 1, 2, 3, 4, 5);
1 голос
/ 22 сентября 2019

Вы можете сделать с подзапросами, чтобы быть более эффективным.Предварительный запрос сгруппирован по каждому клиенту только для тех клиентов, которые соответствуют определенным категориям.Эти подзапросы всегда приводят к не более 1 записи на каждого клиента, поэтому вы не получите декартово результат.Получите это для своих дебетов и кредитов и повторно присоединитесь к своему основному списку клиентов с левым соединением в случае, если одна или другая сторона (дебеты / кредиты) могут не существовать.

declare @StartDate DATE = '2016-08-01'
declare @EndDate DATE = '2016-08-31'
declare @BillingCategory INT = 0

select
        c.ID,
        c.Name,
        c.StartingBalance,
        coalesce( AllDebits.xDebits, 0 ) DebitsPrior,
        coalesce( AllDebits.Debits, 0 ) Debits
        coalesce( AllCredits.xCredits, 0 ) CreditsPrior,
        coalesce( AllCredits.Credits, 0 ) Credits
    from 
        customers c
            LEFT JOIN 
            ( select t.CustomerID,
                    sum( case when t.[Date] < @StartDate then Amount else 0 end ) xDebits,
                    sum( case when t.[Date] >= @StartDate then Amount else 0 end ) Debits
                from
                    customers c1
                        JOIN Transactions t
                            on c1.CustomerID = t.CustomerID
                where
                    c1.BillingCategory in (0,1,2,3,4,5)
                group by
                    t.CustomerID ) AllDebits
                on c.CustomerID = AllDebits.CustomerID
            LEFT JOIN 
            ( select p.CustomerID,
                    sum( case when p.[Date] < @StartDate then Amount else 0 end ) xCredits,
                    sum( case when p.[Date] >= @StartDate then Amount else 0 end ) Credits
                from
                    customers c1
                        JOIN Payments p
                            on c1.CustomerID = p.CustomerID
                where
                    c1.BillingCategory in (0,1,2,3,4,5)
                group by
                    p.CustomerID ) AllCredits
                on c.CustomerID = AllCredits.CustomerID
    where
        c.BillingCategory in (0,1,2,3,4,5)

ДОБАВЛЕНИЕ КОММЕНТАРИИ

Что касается ответа Томаса, да, они близки.Моя версия также добавляет объединение в таблицу клиентов для определенной категории счетов, и вот почему.Я не знаю размер вашей базы данных, сколько клиентов, сколько транзакций.Если вы имеете дело с большой суммой, которая оказывает влияние на производительность, версия Томаса запрашивает КАЖДОГО клиента и КАЖДУЮ транзакцию.Моя версия опрашивает только квалифицированных клиентов по критериям категории выставления счетов, которые вы ограничили.

Опять же, не зная размера данных, если вы имеете дело с записями по 100 тыс., Может не быть заметной производительности.Если вы имеете дело с 100k КЛИЕНТАМИ, это может быть совсем другая история.

@ JonathanWood, правильно, но в моей версии каждый внутренний подзапрос включает cus

...