2011-01-25 27 views
7

Tôi có bảng sau đây và tôi đang cố gắng phát hiện các sản phẩm bị gián đoạn trong các nhịp của nó.Oracle SQL: Phát hiện các gián đoạn trong các khoảng liên tục

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 

Vì vậy, ở đây tôi muốn nó báo cáo lại product3 bởi vì chúng ta đang thiếu khoảng

07/01/2011 - 09/31/2011

Bất kỳ ý tưởng về làm thế nào tôi có thể làm điều này?

EDIT: Oracle Ver: 10g

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 
) 

EDIT 2 ngày bắt đầu và ngày kết thúc không thể chồng lên nhau

EDIT 3 một khoảng có thể hai ngày bất kỳ miễn là price_end_date> = price_start_date. Bằng nhau được bao gồm kể từ khi một sản phẩm có thể được bán trong một ngày.

+1

Chúng tôi cần truy vấn của bạn và có khả năng là câu lệnh 'CREATE TABLE' cho (các) bảng liên quan, để có thể trợ giúp bạn. Và phiên bản Oracle ... Tại sao mọi người upvoting một câu hỏi chưa hoàn chỉnh ?! –

+0

Ngày bắt đầu và ngày kết thúc có thể trùng lặp không? – DCookie

+0

Bạn luôn quan tâm đến một năm? Và giả sử tôi có 6 tháng đầu năm, nhưng không có gì qua cuối năm - có khả thi không? Ví dụ, giả sử sản phẩm 3 chỉ có bản ghi đầu tiên. Nó có nên được báo cáo không? – DCookie

Trả lời

2

Hãy thử này (sử dụng LEAD chức năng phân tích):

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 

Ví dụ với cài đặt

 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 

EDIT: Cập nhật truy vấn để xem xét start_date tiếp theo và end_date hiện tại để tránh các vấn đề với phân phối dữ liệu.

+0

Tại sao bạn thêm 3 tháng vào ngày bắt đầu? Bạn có giả sử dữ liệu được tổ chức hàng quý? Tôi không chắc chắn rằng giả định được bảo đảm. Giả sử sản phẩm 3 có thêm một bản ghi cho khoảng trống còn thiếu? Truy vấn của bạn vẫn báo cáo nó là bị thiếu. Tôi nghĩ rằng cách tiếp cận của bạn là tốt, chỉ cần tinh chỉnh. Tôi nghĩ bạn nên thêm một ngày vào mỗi ngày và kiểm tra điều đó với next_start_date của mình. – DCookie

+0

@DCookie: Có Tôi giả sử dữ liệu được tổ chức một cách tinh vi. Vì OP đang nói về các nhịp nên có một khoảng thời gian được xác định trước mà shuold được sử dụng. Nếu OP có thể cung cấp thêm thông tin về khoảng thời gian, truy vấn có thể dễ dàng được tinh chỉnh để chứa các nhịp. – Chandu

+0

Trừ khi bạn biết dữ liệu không được tổ chức theo cách đó, vì sản phẩm 2 có một bản ghi cho cả năm và sản phẩm 3 có bản ghi trong một nửa năm. – DCookie

0

Bạn có thể sử dụng mệnh đề exists để lọc các hàng có hàng trước đó tồn tại và mệnh đề not exist để tìm các hàng mà hàng trước không kết thúc trên hàng hiện tại cộng với một ngày. Ví dụ:

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 
     ); 

này in:

PRODUCT   UNIT_COST PRICE_STA PRICE_END 
----------------------- ---------- --------- --------- 
product 3   25 01-OCT-11 31-DEC-11 
0

Bạn có thể làm một số so sánh toán học của các dãy, giả sử bạn khắc phục những ngày xấu trong bộ mẫu của bạn:

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 

nào sẽ trở lại:

PRODUCT                   
----------------- 
product 3                  
1 row selected 
1

Bạn cũng có thể sử dụng kỹ thuật này. Nó sử dụng một truy vấn bên trong (chronological_record) để gán một thứ hạng cho mỗi bản ghi trong bảng TBL_PRODUCT (thứ hạng đang được sắp xếp theo số start_date trong mỗi 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 

Trong ví dụ của bạn, subquery (chronological_record) sẽ mang lại một cái gì đó như thế này:

 
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 

Truy vấn chính của INNER JOIN có hiệu quả phù hợp trước đó ghi lên với hồ sơ (thứ tự thời gian nói) tiếp theo của họ.

+0

chắc chắn hoạt động, mặc dù có vẻ hơi dài so với việc sử dụng 'lead (...)' hoặc 'lag (...)'? – araqnid

+0

@araqnid: Thành thật mà nói, tôi không biết về 'LEAD' hoặc' LAG' trước ngày hôm nay. Cảm ơn vì tiền hỗ trợ! –

1

Giả sử bảng của bạn được gọi products, cột ngày bắt đầu của bạn được đặt tên s và cột ngày kết thúc của bạn được đặt tên 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 ; 

Sau đó truy vấn này cung cấp cho bạn tất cả các sản phẩm có "mất tích" nhịp:

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; 

Vì nhóm này giống nhau trong cả hai chế độ xem, nên tất nhiên điều này có thể được kết hợp thành một truy vấn duy nhất, mặc dù làm cho giải pháp hơi rõ ràng hơn:

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; 

Nhưng như Stephanie Page chỉ ra, đó là tối ưu hóa sớm; nó không chắc rằng bạn sẽ được quét cho phá vỡ trong liên tục kéo dài tất cả những gì thường xuyên.

+2

Cách tiếp cận thú vị, mặc dù nó có nghĩa là quét bảng sản phẩm hai lần. Tuy nhiên, chúng không thể được kết hợp thành một truy vấn đơn lẻ? – araqnid

+2

Bạn đã nhìn thấy rất nhiều bảng Sản phẩm với hàng tỷ hàng? Có gì lớn đối với ** TWO ** bản quét của một bảng có thể hoàn toàn được lưu vào bộ nhớ cache. Tần suất phân tích "Khoảng cách" sẽ được chạy như thế nào? Bạn cập nhật các bảng "sản phẩm" bao lâu một lần? –

Các vấn đề liên quan