Запуск складного запиту для кожної дати в діапазоні


9

У мене є таблиця замовлень

   Column   |            Type             |                      Modifiers                      
------------+-----------------------------+-----------------------------------------------------
 id         | integer                     | not null default nextval('orders_id_seq'::regclass)
 client_id  | integer                     | not null
 start_date | date                        | not null
 end_date   | date                        | 
 order_type | character varying           | not null

Дані мають неперекриваються постійні замовлення для client_id, а іноді і тимчасове замовлення, яке перекриває постійний порядок на його start_date, коли вони мають відповідний client_id. Існують обмеження на рівні додатків, які зберігають накази одного типу від перекриття.

 id | client_id | start_date |  end_date  | order_type 
----+-----------+------------+------------+------------
 17 |        11 | 2014-02-05 |            | standing
 18 |        15 | 2014-07-16 | 2015-07-19 | standing
 19 |        16 | 2015-04-01 |            | standing
 20 |        16 | 2015-07-18 | 2015-07-18 | temporary

Наприклад, для 2015-07-18клієнта 16 є замовлення №20, оскільки це активне замовлення, оскільки воно перекриває постійне замовлення №19. З деякою суєтою я знайшов ефективний спосіб запитів щодо активних ідентифікаторів замовлення на побачення.

    SELECT id from (
      SELECT
        id,
        first_value(id) OVER (PARTITION BY client_id ORDER BY order_type DESC) active_order_id
      FROM orders
      WHERE start_date <= ? and (end_date is null OR end_date >= ?)
    ) active_orders
    WHERE id = active_order_id

Якщо ви запитаєте це 2015-07-18в якості заповнювачів, ви отримаєте

 id 
----
 17
 18
 20

План запитів щодо цього запиту порівняно з деякими іншими моїми ідеями (наприклад, підзапити, що підраховують кількість тимчасових замовлень для клієнта на дату) досить невеликий, і я його дуже задоволений. (дизайн столу, я не в захваті від)

Тепер мені потрібно знайти всі активні замовлення для діапазону дат, з'єднаних із датами, на які вони активні. Наприклад, діапазон дат , 2015-07-18щоб 2015-07-19я хотів би наступний результат.

active_date | id 
------------+----
 2015-07-18 | 17
 2015-07-18 | 18
 2015-07-18 | 20
 2015-07-19 | 17
 2015-07-19 | 18
 2015-07-19 | 19

Упорядкуйте 20 переопределень, порядок 19 на, 2015-07-18але не на 2015-07-19.

Я виявив, generate_series()що можу створити діапазон дат, але я не маю поняття, як би це зробити, щоб отримати таблицю дат та ідентифікаторів замовлення. Моя думка - це перехресне з'єднання, але я не можу зрозуміти, як змусити цю роботу в цій ситуації.

Дякую

ОНОВЛЕННЯ Додано загадку sql .


2
Чи можете ви показати деякі приклади даних? Ці активні / неактивні та тимчасові речі не дуже зрозумілі після першого читання.
— dezso

Так, незрозуміло. У вашому запиті знайдеться одне замовлення на кожного клієнта, і воно, схоже, не буде детермінованим. Якщо для клієнта є 2 або більше замовлень, з тим самим типом, яке з двох повернених буде довільним і змінюватиметься в залежності від виконання. Отже, у вас є або якісь обмеження на столі, які ви нам не сказали, або ваш запит невірний.
— ypercubeᵀᴹ

Я оновив своє питання набагато більше деталей, і так, є обмеження в даних.
— рекобата

Відповіді:


5

Я б використав select distinct onзамість віконної функції, а потім просто приєднався до днів.

select 
    distinct on (date, client_id) date, 
    id 
from orders
inner join generate_series('2015-07-18'::date, '2015-07-19'::date, '1 day') date
  on start_date <= date and (end_date is null or date <= end_date)
order by date, client_id, order_type desc

http://sqlfiddle.com/#!15/5a420/16/0

Я можу розробити більше, якщо щось не зрозуміло.


Це не стосується тимчасового замовлення / постійного замовлення, але це може бути зроблено після приєднання =)
— перезавантажтеся

Це визначає той самий порядок, що і у вашому вікні запиту. Отже, для будь-якого (дата, client_id) він обрав би перший тип_порядку у зворотному алфавітному порядку.
— Саймон Перепелиця

Внутрішнє з'єднання є ідеальним, а виділене виділення набагато простіше зрозуміти (і виконує приблизно так само добре), ніж вікно. З будь-якої іншої причини я не повинен використовувати функції вікон?
— рекобата

1
Ось про це. Я думаю, що distinct onце навіть більш оптимізовано, ніж запит до вікна. До речі, я мушу зазначити, що це звичайна проблема "топ-групи" в SQL: stackoverflow.com/questions/3800551/…
— Саймон Перепелиця,

Це чудове читання, я маю щось вивчити. Якщо у вас є якийсь час, у мене є розширена версія цього питання, в якій використовується те, що я дізнався тут. dba.stackexchange.com/questions/108767/… Я впевнений, що повернусь, щоб оновити його тим, що я дізнаюся за цим посиланням. І спасибі
— рекобата

0

Напишіть функцію, яка приймає одну параметр як параметр і повертає список дат + ідентифікаторів, які мають замовлення.

Потім використовуйте Gene_series, як ви запропонували, і зателефонуйте до функції в межах дат.

Це загальна стратегія при роботі зі складними умовами в SQL.

Я включив деякий код нижче, але відповідь у SQL вище набагато простіше.

Ось функція:

create or replace function o( date) returns setof INT AS '
SELECT id from (
 SELECT
  id,
  first_value(id) OVER (PARTITION BY client_id ORDER BY order_type DESC) active_order_id
 FROM orders
 WHERE start_date <= $1 and (end_date is null OR end_date >= $1)
) active_orders
WHERE id = active_order_id;
' LANGUAGE sql ;

І як це назвати:

select distinct d, o(d::date) 
from generate_series('2015-07-18'::date, '2015-07-19'::date, '1 day') as d;

SQLFiddle


2
Ви можете залишити цю відповідь якоюсь деталлю, зразком коду тощо. Як це є, ця відповідь може бути видалена, оскільки вона досить розпливчаста.
— Макс Вернон

Чи зможете ви оновити мою загадку на прикладі? sqlfiddle.com/#!15/5a420/3/0
— рекобат

Я оновив свою відповідь, щоб включити якийсь код, але відповідь вище простіший.
— Дон Дрейк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.