Postgres фильтруется с помощью запроса - PullRequest
0 голосов
/ 17 октября 2018

В настоящее время у меня есть следующий запрос:

WITH instances AS (
    SELECT b.ldtc as date, a.fk_item_id, b.movement, a.quantity, 
        CASE WHEN b.movement = 'Inbound' THEN a.quantity ELSE -a.quantity END as absquantity
    FROM inventory_resupplylogiteminstance a
    INNER JOIN inventory_resupplylog b ON b.uid = a.fk_resupply_log_id
)

SELECT a.name, 
    SUM(CASE WHEN b.date < ('2018-10-10'::date) THEN b.absquantity END) as starting_balance,

    SUM(CASE WHEN b.date > ('2018-10-10'::date) AND b.date < ('2018-10-12'::date) AND b.movement = 'aa' THEN b.absquantity END) as aa,
    SUM(CASE WHEN b.date > ('2018-10-10'::date) AND b.date < ('2018-10-12'::date) AND b.movement = 'bb' THEN b.absquantity END) as bb,
    SUM(CASE WHEN b.date > ('2018-10-10'::date) AND b.date < ('2018-10-12'::date) AND b.movement = 'cc' THEN b.absquantity END) as cc,
    SUM(CASE WHEN b.date > ('2018-10-10'::date) AND b.date < ('2018-10-12'::date) AND b.movement = 'dd' THEN b.absquantity END) as dd,

    SUM(CASE WHEN b.date < ('2018-10-12'::date) THEN b.absquantity END) AS ending_balance




FROM inventory_item a
LEFT JOIN instances b ON b.fk_item_id = a.uid

GROUP BY a.uid, a.name
ORDER BY a.name

Как вы можете видеть, строки SUM 2-5-й избыточны в том смысле, что они запрашивают строки, где b.date находится между 2018-10-10 и2018-10-12.Можно ли как-то переписать мой запрос так, чтобы ему приходилось писать b.date > ('2018-10-10'::date) AND b.date < ('2018-10-12'::date) только один раз и при этом иметь возможность ВЫБРАТЬ starting_balance и ending_balance в одной строке?

1 Ответ

0 голосов
/ 17 октября 2018

Вы можете проверить диапазон в CTE и создать флаг, который указывает, находится ли строка в указанном диапазоне.

Затем вы можете использовать этот флаг в последнем SELECT.Переключение на выражение FILTER () также делает его более читабельным:

WITH instances AS (
  SELECT b.ldtc as date, 
         a.fk_item_id, 
         b.movement, 
         a.quantity, 
         CASE 
           WHEN b.movement = 'Inbound' THEN a.quantity 
           ELSE -a.quantity 
         END as absquantity,
         -- the column in_range returns either true or false
         (b.ldtc > ('2018-10-10'::date) AND b.date < ('2018-10-12'::date)) as in_range
  FROM inventory_resupplylogiteminstance a
    INNER JOIN inventory_resupplylog b ON b.uid = a.fk_resupply_log_id
)
SELECT a.name, 
       SUM(b.absquantity) filter (where b.date < '2018-10-10'::date) as starting_balance,
       SUM(b.absquantity) filter (where in_range and b.movement = 'aa') as aa,
       SUM(b.absquantity) filter (where in_range and b.movement = 'bb') as bb,
       SUM(b.absquantity) filter (where in_range and b.movement = 'cc') as cc,
       SUM(b.absquantity) filter (where in_range and b.movement = 'dd') as dd,
       SUM(b.absquantity) filter (where b.date < '2018-10-12'::date) AS ending_balance
FROM inventory_item a
  LEFT JOIN instances b ON b.fk_item_id = a.uid
GROUP BY a.uid, a.name
ORDER BY a.name;

Если вы не хотите повторять дату для начального и конечного баланса, вы можете указать диапазон, для которого вы хотите проверитьв CTE, затем используйте функции диапазона Postgres в конечном запросе:

WITH instances AS (
  SELECT b.ldtc as date, 
         a.fk_item_id, 
         b.movement, 
         a.quantity, 
         CASE 
           WHEN b.movement = 'Inbound' THEN a.quantity 
           ELSE -a.quantity 
         END as absquantity,
         daterange('2018-10-10'::date, '2018-10-12'::date, '()') as check_range
  FROM inventory_resupplylogiteminstance a
    INNER JOIN inventory_resupplylog b ON b.uid = a.fk_resupply_log_id
)
SELECT a.name, 
    SUM(b.absquantity) filter (where b.date < lower(check_range)) as starting_balance,
    SUM(b.absquantity) filter (where b.date <@ b.check_range and b.movement = 'aa') as aa,
    SUM(b.absquantity) filter (where b.date <@ b.check_range and b.movement = 'bb') as bb,
    SUM(b.absquantity) filter (where b.date <@ b.check_range and b.movement = 'cc') as cc,
    SUM(b.absquantity) filter (where b.date <@ b.check_range and b.movement = 'dd') as dd,
    SUM(b.absquantity) filter (where b.date < upper(b.check_range))) AS ending_balance
FROM inventory_item a
  LEFT JOIN instances b ON b.fk_item_id = a.uid
GROUP BY a.uid, a.name
ORDER BY a.name;

daterange('2018-10-10'::date, '2018-10-12'::date, '()') создает диапазон дат , где две даты исключен .

Оператор <@ проверяет, попадает ли дата в указанный диапазон.

Таким образом, выражение b.date <@ b.check_range эквивалентно b.date > '2018-10-10'::date AND b.date < '2018-10-12'::date (поскольку диапазон был определен без учета ребер)

...