Oracle SQL: обнаружение разрывов в непрерывных пролетах - PullRequest
7 голосов
/ 25 января 2011

У меня есть следующая таблица, и я пытаюсь обнаружить продукты, у которых есть разрывы в пределах.

Product     | unit_Cost | price start date |    price end date
--------------------------------------------------------------------------
product 1     15.00         01/01/2011      03/31/2011
product 1     15.00         04/01/2011      06/31/2011
product 1     15.00         07/01/2011      09/31/2011
product 1     15.00         10/01/2011      12/31/2011

product 2     10.00         01/01/2011      12/31/2011

product 3     25.00         01/01/2011      06/31/2011
product 3     25.00         10/01/2011      12/31/2011

Итак, я хочу, чтобы он сообщил о продукте3, потому что нам не хватает диапазона

07/01/2011 - 09/31/2011

Есть идеи, как мне это сделать?

EDIT: Oracle Ver: 10 г

Create Table Statement

CREATE TABLE Sandbox.TBL_PRODUCT
(
  PRODUCT_ID        VARCHAR2(13 BYTE),   
  PRODUCT           VARCHAR2(64 BYTE),
  UNIT_COST         NUMBER,
  PRICE_START_DATE  DATE,
  PRICE_END_DATE    DATE
)

РЕДАКТИРОВАТЬ 2 даты начала и окончания не могут перекрываться

РЕДАКТИРОВАТЬ 3 промежуток может быть любыми двумя датами, пока price_end_date> = price_start_date. Равно включено, так как товар может продаваться в течение одного дня.

Ответы [ 5 ]

2 голосов
/ 25 января 2011

Попробуйте (с использованием аналитической функции LEAD):

SELECT *
  FROM (
                SELECT a.*, LEAD(price_start_date,1,NULL) OVER(PARTITION BY product ORDER BY price_end_date) next_start_date 
         FROM Product a
       )
WHERE (price_end_date + 1)<> next_start_date

Пример с настройкой

        CREATE TABLE PRODUCT
          (
            PRODUCT   VARCHAR2(100 BYTE),
            UNIT_COST NUMBER,
            START_DATE DATE,
            END_DATE DATE
          );

        INSERT INTO Product VALUES('product 1','15.00',TO_DATE('01/01/2011','MM/DD/RRRR'),TO_DATE('03/31/2011','MM/DD/RRRR'));
        INSERT INTO Product VALUES('product 1','15.00',TO_DATE('04/01/2011','MM/DD/RRRR'),TO_DATE('06/30/2011','MM/DD/RRRR'));
        INSERT INTO Product VALUES('product 1','15.00',TO_DATE('07/01/2011','MM/DD/RRRR'),TO_DATE('09/30/2011','MM/DD/RRRR'));
        INSERT INTO Product VALUES('product 1','15.00',TO_DATE('10/01/2011','MM/DD/RRRR'),TO_DATE('12/31/2011','MM/DD/RRRR'));
        INSERT INTO Product VALUES('product 2','10.00',TO_DATE('01/01/2011','MM/DD/RRRR'),TO_DATE('12/31/2011','MM/DD/RRRR'));
        INSERT INTO Product VALUES('product 3','25.00',TO_DATE('01/01/2011','MM/DD/RRRR'),TO_DATE('06/30/2011','MM/DD/RRRR'));
        INSERT INTO Product VALUES('product 3','25.00',TO_DATE('10/01/2011','MM/DD/RRRR'),TO_DATE('12/31/2011','MM/DD/RRRR'));

SELECT *
  FROM (
                SELECT a.*, LEAD(start_date,1,NULL) OVER(PARTITION BY product ORDER BY start_date) next_start_date 
                 FROM Product a
              )
WHERE (end_date + 1)<> next_start_date

РЕДАКТИРОВАТЬ : обновлен запросрассмотреть следующую дату начала и текущую дату окончания, чтобы избежать проблем с распределением данных.

1 голос
/ 25 января 2011

Если ваша таблица называется products, столбец с датой начала называется s, а столбец с датой окончания - e:

create view max_interval as 
select product, 
max(e) - min(s) as max_interval 
from products group by product;


create view total_days as 
select product, 
sum( e - s ) + count(product) - 1 as total_days 
from products group by product  ;

Тогда этот запрос дает вам все продукты с «отсутствующими» интервалами:

select a.*, b.*
from max_interval a 
left outer join total_days b 
on (a.product = b.product)
where a.max_interval <> b.total_days;

Поскольку группировка по в обоих представлениях одинакова, это, конечно, можно объединить в один запрос, хотя и сделать решение немного менее понятным:

select product, 
max(e) - min(s) as max_interval, 
sum( e - s ) + count(product) - 1 as total_days 
from products group by product  
having max(e) - min(s) <> sum( e - s ) + count(product) - 1;

Но, как отмечает Стефани Пейдж, это преждевременная оптимизация; маловероятно, что вы будете так часто сканировать разрывы на непрерывных пролетах.

1 голос
/ 25 января 2011

Вы также можете использовать эту технику.Он использует внутренний запрос (chronological_record), чтобы присвоить ранг каждой записи в таблице TBL_PRODUCT (ранг сортируется по start_date внутри каждого product).

WITH
  chronological_record AS
  (
    SELECT
      product,
      unit_cost,
      start_date,
      end_date,
      (DENSE_RANK() OVER (PARTITION BY product ORDER BY start_date))
          AS chronological_order
    FROM
      TBL_PRODUCT
  )

SELECT
  earlier.product,
  (earlier.end_date + 1) AS missing_period_start_date,
  (later.start_date - 1) as missing_period_end_date
FROM
  CHRONOLOGICAL_RECORD earlier
  INNER JOIN
  CHRONOLOGICAL_RECORD later
    ON
        earlier.product = later.product
      AND
        (earlier.chronological_order + 1) = later.chronological_order
WHERE
  (earlier.end_date + 1) <> later.start_date

В вашемНапример, подзапрос (chronological_record) мог бы дать что-то вроде этого:

Product   | unit_Cost | start date | end date   | chronological_order
--------------------------------------------------------------------------
product 1    15.00      01/01/2011   03/31/2011    1
product 1    15.00      04/01/2011   06/31/2011    2
product 1    15.00      07/01/2011   09/31/2011    3
product 1    15.00      10/01/2011   12/31/2011    4

product 2    10.00      01/01/2011   12/31/2011    1

product 3    25.00      01/01/2011   06/31/2011    1
product 3    25.00      10/01/2011   12/31/2011    2

Основной запрос INNER JOIN эффективно сопоставляет более ранние записи со следующими (хронологически говоря) записями.

0 голосов
/ 25 января 2011

Вы можете сделать математическое сравнение диапазонов, предполагая, что вы исправили неправильные даты в вашем наборе выборок:

SELECT PRODUCT
FROM Sandbox.TBL_PRODUCT
HAVING SUM(PRICE_END_DATE - PRICE_START_DATE + 1) < MAX(PRICE_END_DATE) - MIN(PRICE_START_DATE) + 1
GROUP BY PRODUCT

Что вернет:

PRODUCT                                                                         
-----------------
product 3                                                                       
1 row selected
0 голосов
/ 25 января 2011

Можно использовать предложение exists для фильтрации строк, для которых существует более ранняя строка, и предложение not exist, чтобы найти строки, в которых предыдущая строка не заканчивается на текущей строке плюс один день. Например:

select  *
from    TBL_PRODUCT t1
where   exists
        (
        select  *
        from    TBL_PRODUCT t2
        where   t2.PRODUCT = t1.PRODUCT
                and t2.PRICE_END_DATE < t1.PRICE_START_DATE
        )
        and not exists
        (
        select  *
        from    TBL_PRODUCT t3
        where   t3.PRODUCT = t1.PRODUCT
                and t3.PRICE_END_DATE + 1 = t1.PRICE_START_DATE
        );

Это печатает:

PRODUCT          UNIT_COST PRICE_STA PRICE_END
----------------------- ---------- --------- ---------
product 3           25 01-OCT-11 31-DEC-11
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...