SQL-запрос для объединения одной таблицы с суммой до каждой возвращенной строки - PullRequest
0 голосов
/ 13 ноября 2018

Я столкнулся с задачей, в которой мне нужно возвращать общее СЧЕТНОЕ И СУММУ выданных политик за каждый день месяца и сравнивать его с предыдущим годом.

Таблица PolicyOrder имеет поля:

PolicyOrderId - первичный ключ CreatedAt (DATETIME) CalculatedPremium - стоимость политики или «премиум» PolicyOrderStatusId - не имеет отношения к вопросу, но все же - статус политики.

Чтобы решить эту проблему, я предложил внутренний запрособъединяет собственные таблицы и суммы / числа путем группировки в соответствии с ДНЕМ даты создания.

SELECT 
      DATE(po1.CreatedAt) AS dayDate_2017, 
      SUM(po1.CalculatedPremium) AS premiumSum_2017,
      COUNT(po1.PolicyOrderId) AS policyCount_2017,
      po2.*
FROM 
      PolicyOrder po1
INNER JOIN (
           SELECT 
                DATE(CreatedAt) AS dayDate_2018, 
                SUM(CalculatedPremium) AS premiumSum_2018, 
                COUNT(PolicyOrderId) AS policyCount_2018
           FROM 
                PolicyOrder po2
           WHERE
                YEAR(CreatedAt) = 2018 AND 
                MONTH(CreatedAt) = 10 AND
                PolicyOrderStatusId = 6 
           GROUP BY
                DAY(CreatedAt)
       ) po2 ON ( 
           DAY(po2.dayDate_2018) = DAY(po1.CreatedAt) 
       )
WHERE   
       YEAR(po1.CreatedAt) = 2017 AND 
       MONTH(po1.CreatedAt) = 10 AND 
       PolicyOrderStatusId = 6 
GROUP BY 
       DAY(po1.CreatedAt)

Приведенный выше запрос возвращает следующие результаты:

dayDate_2017 | premiumSum_2017 | policyCount_2017 | dayDate_2018 | premiumSum_2018 | policyCount_2018
2017-10-01   | 4699.36         | 98               | 2018-10-01   | 8524.21         | 144
2017-10-02   | 9114.55         | 168              | 2018-10-02   | 7942.25         | 140
2017-10-03   | 9512.43         | 178              | 2018-10-03   | 9399.61         | 161
2017-10-04   | 9291.77         | 155              | 2018-10-04   | 6922.83         | 137
2017-10-05   | 8063.27         | 155              | 2018-10-05   | 9278.58         | 178
2017-10-06   | 9743.40         | 184              | 2018-10-06   | 6139.38         | 136
...
2017-10-31   | ...

Проблема в том, что теперь мне нужнодобавьте еще два столбца, в которых должны учитываться политики и суммы, добавленные с начала года ДО ДО каждой возвращенной строки.

Desired results:
dayDate_2017 | premiumSum_2017 | policyCount_2017 | sumFromYearBegining | countFromYearBegining 
2017-10-01   | 4699.36         | 98               | 150000.34           | 5332   
2017-10-02   | 9114.55         | 168              | 156230.55           | 5443
2017-10-03   | 9512.43         | 178              | 160232.44           | 5663
    ...
2017-10-31   | ...


WHERE:

sumFromYearBegining (150000.34) - SUM of premiumSum from 2017-01-01 until 2017-10-01 (excluding)
countFromYearBegining (5332) - COUNT of policies from 2017-01-01 until 2017-10-01 (excluding)

sumFromYearBegining (1566239.55) - SUM of premiumSum from 2017-01-01 until 2017-10-02 (excluding)
countFromYearBegining (5443) - COUNT of policies from 2017-01-01 until 2017-10-02 (excluding)

sumFromYearBegining (160232.44) - SUM of premiumSum from 2017-01-01 until 2017-10-02 (excluding)
countFromYearBegining (5663) - COUNT of policies from 2017-01-01 until 2017-10-02 (excluding)

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

DB Fiddle: https://www.db -fiddle.com / f / ckM8HyTD6NjLbK41Mq1gct / 5

Любая помощь от вас SQL ниндзя высоко ценится.

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

Если вам нужен способ, который позволяет избежать использования @variables в списке выбора, а также избегает аналитики (их поддерживает только mysql 8), вы можете сделать это с помощью полукартового произведения:

WITH prevYr AS(
    SELECT 
        YEAR(CreatedAt) AS year_prev,
        MONTH(CreatedAt) AS month_prev,
        DAY(CreatedAt) AS day_prev,
        SUM(CalculatedPremium) AS premiumSum_prev, 
        COUNT(PolicyOrderId) AS policyCount_prev
    FROM 
        PolicyOrder
    WHERE
        CreatedAt BETWEEN '2017-02-01' AND '2017-02-28' AND
        PolicyOrderStatusId = 6 
    GROUP BY
        YEAR(CreatedAt), MONTH(CreatedAt), DAY(CreatedAt)
),
currYr AS (
    SELECT 
        YEAR(CreatedAt) AS year_curr,
        MONTH(CreatedAt) AS month_curr,
        DAY(CreatedAt) AS day_curr,
        SUM(CalculatedPremium) AS premiumSum_curr, 
        COUNT(PolicyOrderId) AS policyCount_curr
    FROM 
        PolicyOrder
    WHERE
        CreatedAt BETWEEN '2018-02-01' AND '2018-02-28' AND
        PolicyOrderStatusId = 6 
    GROUP BY
        YEAR(CreatedAt), MONTH(CreatedAt), DAY(CreatedAt)
) 


SELECT 
      *
FROM
       prevYr
       INNER JOIN 
       currYr
       ON  
           currYr.day_curr = prevYr.day_prev

       INNER JOIN
       (
           SELECT 
                main.day_prev AS dayRolling_prev, 
                SUM(pre.premiumSum_prev) AS premiumSumRolling_prev, 
                SUM(pre.policyCount_prev) AS policyCountRolling_prev
           FROM 
                prevYr main LEFT OUTER JOIN prevYr pre ON pre.day_prev < main.day_prev
           GROUP BY
                main.day_prev
        ) rollingPrev
        ON  
           currYr.day_curr = rollingPrev.dayRolling_prev

ORDER BY 1,2,3

МыСуммируйте данные за 2017 год и 2018 год в два CTE, потому что это делает вещи намного чище и аккуратнее позже, особенно для этого скользящего счета.Вы, вероятно, можете легко следовать логике CTE, потому что он более или менее прямо взят из вашего запроса - я только отбросил столбец DATE в пользу триплета год / месяц / дата, потому что он сделал другие вещи чище (объединения) и может быть объединенна дату, если это необходимо.Я также поменял предложения WHERE на использование date BETWEEN x AND y, потому что это будет использовать индекс для столбца, в то время как использование YEAR(date) = x AND MONTH(date) = y может не

Скользящее число работает через что-то, что я назвал полукартовым.Это на самом деле декартово произведение;любое объединение базы данных, в результате которого строки из одной или обеих таблиц умножаются и многократно представляются в выходных данных, является декартовым произведением.Вместо того, чтобы быть полным продуктом (каждая строка пересекается с каждой другой строкой), в этом случае он использует меньше чем, поэтому каждая строка пересекается только с подмножеством строк.По мере увеличения даты все больше строк соответствуют предикату, потому что 30-я дата имеет 29 строк, которые меньше ее.

Таким образом, это приводит к следующей структуре данных:

maindate   predate    maincount precount
2017-02-01 NULL       10        NULL

2017-02-02 2017-02-01 20        10

2017-02-03 2017-02-01 30        10
2017-02-03 2017-02-02 30        20

2017-02-04 2017-02-01 40        10
2017-02-04 2017-02-02 40        20
2017-02-04 2017-02-03 40        30

Вы можете видеть, что для любой данной основной даты она повторяется N - 1 раз, потому что N - 1 дат ниже, чем вкоторые удовлетворяют условию соединения predate < maindate

Если мы сгруппируем по maindate и суммируем значения, связанные с каждым предшествующим периодом, мы получим скользящую сумму всех предварительных значений в эту основную дату (таким образом, на4-й день месяца, это СУММА (предварительный подсчет для дат 1–3, т. Е. 10 + 20 + 30 = 60. На 5-й день мы суммируем значения для дней с 1 по 4. На 6-й день мы суммируем дни 1до 5 и т. д.)

0 голосов
/ 13 ноября 2018

Мы можем использовать Определяемые пользователем переменные для расчета скользящей суммы / количества при отсутствии доступности оконных функций.

Сначала нам нужно будет определить сумму и число для каждый день в 2017 году (даже если вам нужны строки только для определенного месяца). Потому что для расчета скользящей суммы для дней в мартовском месяце нам также понадобятся значения суммы / количества за январь и февраль. Одна из возможностей оптимизации состоит в том, что мы можем ограничить вычисления только с первого месяца до требуемого месяца.

Обратите внимание, что ORDER BY daydate_2017 необходимо для правильного расчета суммы проката. По умолчанию данные неупорядочены. Без определения заказа мы не можем гарантировать, что сумма будет правильной.

Кроме того, нам нужно два уровня запросов на дополнительный выбор. Первый уровень используется для расчета значений скользящей суммы. Второй уровень используется для ограничения результата только февральским месяцем. Поскольку WHERE выполняется до SELECT; мы не можем ограничить результат февральским месяцем, на самом первом уровне.

Если вам нужна аналогичная скользящая сумма на 2018 год; аналогичная логика запросов может быть реализована в другом наборе запросов подбора.

SELECT dt2_2017.*, dt_2018.*
FROM 
(
SELECT dt_2017.*,
       @totsum := @totsum + dt_2017.premiumsum_2017 AS sumFromYearBegining_2017,
       @totcount := @totcount + dt_2017.policycount_2017 AS countFromYearBeginning_2017
FROM   (SELECT Date(po1.createdat)        AS dayDate_2017,
               Sum(po1.calculatedpremium) AS premiumSum_2017,
               Count(po1.policyorderid)   AS policyCount_2017
        FROM   PolicyOrder AS po1
        WHERE  po1.policyorderstatusid = 6 AND 
               YEAR(po1.createdat) = 2017 AND 
               MONTH(po1.createdat) <= 2 -- calculate upto February for 2017
        GROUP  BY daydate_2017
        ORDER  BY daydate_2017) AS dt_2017
CROSS JOIN (SELECT @totsum := 0, @totcount := 0) AS user_init_vars 
) AS dt2_2017 
INNER JOIN (
             SELECT 
               DATE(po2.CreatedAt) AS dayDate_2018, 
               SUM(po2.CalculatedPremium) AS premiumSum_2018, 
               COUNT(po2.PolicyOrderId) AS policyCount_2018
             FROM 
               PolicyOrder po2
             WHERE
                YEAR(po2.CreatedAt) = 2018 AND 
                MONTH(po2.CreatedAt) = 2 AND
                po2.PolicyOrderStatusId = 6 
             GROUP BY
                dayDate_2018
           ) dt_2018 ON DAY(dt_2018.dayDate_2018) = DAY(dt2_2017.dayDate_2017)   
WHERE YEAR(dt2_2017.daydate_2017) = 2017 AND 
      MONTH(dt2_2017.daydate_2017) = 2;

РЕЗУЛЬТАТ: Посмотреть на БД Fiddle

| dayDate_2017 | premiumSum_2017 | policyCount_2017 | sumFromYearBegining_2017 | countFromYearBeginning_2017 | dayDate_2018 | premiumSum_2018 | policyCount_2018 |
| ------------ | --------------- | ---------------- | ------------------------ | --------------------------- | ------------ | --------------- | ---------------- |
| 2017-02-01   | 4131.16         | 131              | 118346.77                | 3627                        | 2018-02-01   | 8323.91         | 149              |
| 2017-02-02   | 2712.74         | 85               | 121059.51000000001       | 3712                        | 2018-02-02   | 9469.33         | 153              |
| 2017-02-03   | 3888.59         | 111              | 124948.1                 | 3823                        | 2018-02-03   | 6409.21         | 97               |
| 2017-02-04   | 2447.99         | 74               | 127396.09000000001       | 3897                        | 2018-02-04   | 5693.69         | 120              |
| 2017-02-05   | 1437.5          | 45               | 128833.59000000001       | 3942                        | 2018-02-05   | 8574.97         | 129              |
| 2017-02-06   | 4254.48         | 127              | 133088.07                | 4069                        | 2018-02-06   | 8277.51         | 133              |
| 2017-02-07   | 4746.49         | 136              | 137834.56                | 4205                        | 2018-02-07   | 9853.75         | 173              |
| 2017-02-08   | 3898.05         | 125              | 141732.61                | 4330                        | 2018-02-08   | 9116.33         | 144              |
| 2017-02-09   | 8306.86         | 286              | 150039.46999999997       | 4616                        | 2018-02-09   | 8818.32         | 166              |
| 2017-02-10   | 6740.99         | 204              | 156780.45999999996       | 4820                        | 2018-02-10   | 7880.17         | 134              |
| 2017-02-11   | 4290.38         | 133              | 161070.83999999997       | 4953                        | 2018-02-11   | 8394.15         | 180              |
| 2017-02-12   | 3687.58         | 122              | 164758.41999999995       | 5075                        | 2018-02-12   | 10378.29        | 171              |
| 2017-02-13   | 4939.31         | 159              | 169697.72999999995       | 5234                        | 2018-02-13   | 9383.15         | 160              |

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