2011-12-15 1 views
0

В базе данных PostgreSQL У меня есть 2 таблицы, как эти из них:Регистрация таблиц с помощью соответствующего периода

Таблица пунктов:

item_id item  save_date (date dd/mm/yy) 
---------------------------------------------- 
1   car  01/12/11 
2   wheel  10/12/11 
3   screen 11/12/11 
4   table  15/12/11 

Таблица периодов:

period_id period_name start (date dd/mm/yy) 
------------------------------------------------ 
1   period1  05/12/11 
2   period2  09/12/11 
3   period3  12/12/11 

Так что я хотел бы получить объединенная таблица, добавляющая к элементам таблицы наиболее подходящий период (если по совпадениям). Период будет соответствовать, если save_date элемента будет после даты начала периода.

Например, для «car» дата 01/12/2011, поэтому я перехожу к периодам таблицы, и там я вижу, что все периоды начинаются после этой даты, поэтому я должен установить ее в null. Если я возьму «экран», дата будет 11/12/11, поэтому в периоды я должен взять period2, потому что это последнее, которое соответствует.

Так присоединился к таблице приведет:

item_id item  save_date period_name 
---------------------------------------------- 
1   car  01/12/11  null 
2   wheel  10/12/11  period2 
3   screen 11/12/11  period2 
4   table  15/12/11  period3 

Что такое лучший способ сделать это соединение?

Благодаря

+0

период3 не должен быть еще действителен (до бесконечности), так как нет периода с более поздней датой? – wildplasser

+0

Учтите, что если ваша таблица периодов имела атрибут даты окончания, запрос был бы тривиальным. – onedaywhen

+0

@wildplasser, onedaywhen Да, периоды действительны с этого времени до вечности. Я не знаю заранее дату окончания, потому что дата окончания будет датой начала следующего (ближайшего по времени) периода – Javi

ответ

2
SET search_path='tmp'; 

DROP TABLE items CASCADE; 
CREATE TABLE items 
    (item_id INTEGER NOT NULL PRIMARY KEY 
    , item VARCHAR 
    , save_date date NOT NULL 
    ); 
INSERT INTO items(item_id,item,save_date) VALUES 
(1, 'car', '2011-12-01') 
,(2, 'wheel', '2011-12-10') 
,(3, 'screen', '2011-12-11') 
,(4, 'table', '2011-12-15') 
    ; 

DROP TABLE periods CASCADE; 
CREATE TABLE periods 
    (period_id INTEGER NOT NULL PRIMARY KEY 
    , period_name VARCHAR 
    , start_date date NOT NULL 
    ); 
INSERT INTO periods(period_id,period_name,start_date) VALUES 
(1, 'period1', '2011-12-05') 
,(2, 'period2', '2011-12-09') 
,(3, 'period3', '2011-12-12') 
    ; 
-- self-join to find the next interval 
WITH pe AS (
    SELECT p0.period_id,p0.period_name,p0.start_date 
     , p1.start_date AS end_date 
    FROM periods p0 
    -- must be a left join; because the most recent interval is still open 
    -- (has no successor) 
    LEFT JOIN periods p1 ON p1.start_date > p0.start_date 
    WHERE NOT EXISTS (
     SELECT * FROM periods px 
     WHERE px.start_date > p0.start_date 
     AND px.start_date < p1.start_date 
     ) 
    ) 
SELECT it.item_id 
    , it.item 
    , it.save_date 
    , pe.period_id 
    , pe.period_name 
    , pe.start_date 
    , pe.end_date 
FROM items it 
LEFT JOIN pe 
     ON it.save_date >= pe.start_date 
     AND (it.save_date < pe.end_date OR pe.end_date IS NULL) 
    ; 

Результат:

item_id | item | save_date | period_id | period_name | start_date | end_date 
---------+--------+------------+-----------+-------------+------------+------------ 
     1 | car | 2011-12-01 |   |    |   | 
     2 | wheel | 2011-12-10 |   2 | period2  | 2011-12-09 | 2011-12-12 
     3 | screen | 2011-12-11 |   2 | period2  | 2011-12-09 | 2011-12-12 
     4 | table | 2011-12-15 |   3 | period3  | 2011-12-12 | 
(4 rows) 
2

Крест применять или совместно связанные с суб-запрос будет работать.

Sub Query подход как Cross Применить только для SQL Server 2005 (непроверенные)

SELECT Item_Id, 
     Item, 
     Save_Date, 
     (
     SELECT MAX(Period_Name) 
      FROM Periods 
     WHERE Start = (
       SELECT MAX(start) MaxStart 
        FROM periods P 
       WHERE I.Save_Date > P.start) 
     ) AS Period_Name 
FROM ITEMS I 
Смежные вопросы