Объединять и разбивать диапазоны дат в SQL - PullRequest
1 голос
/ 16 ноября 2011

Я работаю над проблемой, когда у меня есть несколько таблиц с диапазонами дат, датой начала и датой окончания.Я пытаюсь в TSQL (2008 R2) взять эти таблицы и объединить их в одну денормализованную таблицу для лучшего поиска в нашем наборе отчетов.

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

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

Для целей этого вопроса я предполагаю, что у меня есть две таблицыэто необходимо объединить, но в действительности я должен сделать это для нескольких наборов таблиц, каждый из которых содержит 3-6 таблиц.

Вот пример таблиц A и B:

Таблица A:

IdentityId    EmployeeId  StartDateId  EndDateId    Value
---------------------------------------------------------
1             1           1980-01-01   1980-02-01   A
2             1           1980-02-02   1980-03-01   B
3             1           1980-03-01   -1           C
4             2           1980-01-15   1980-02-01   D
5             2           1980-02-02   1980-02-20   E
6             2           1980-02-21   -1           F

Таблица B

IdentityId    EmployeeId  StartDateId  EndDateId    Value
---------------------------------------------------------
1             1           1980-01-10   1980-02-01   G
2             1           1980-02-02   1980-03-01   H
3             1           1980-03-02   -1           I
4             2           1980-01-10   1980-02-06   J
5             2           1980-02-07   1980-03-01   K
6             2           1980-03-02   -1           L

Комбинация будет (Столбец идентификаторов в таблице результатов является просто бегущим счетчиком; он не соответствует столбцам идентификаторовв таблице A или B)

IdentityId    EmployeeId  StartDateId  EndDateId   TableA_Value    TableB_Value
-------------------------------------------------------------------------------
1             1           1980-01-01   1980-01-10  A               G
2             1           1980-01-10   1980-02-01  A               G
3             1           1980-02-02   1980-03-01  B               H
4             1           1980-03-02   -1          C               I
5             2           1980-01-10   1980-01-15  D               J
6             2           1980-01-15   1980-02-01  D               J
7             2           1980-02-02   1980-02-06  E               J
8             2           1980-02-07   1980-02-20  E               K
9             2           1980-02-21   1980-03-01  F               K
10            2           1980-03-02   -1          F               L

Одна проблема заключается в том, что я не хочу полагаться на тот факт, что у каждого сотрудника есть непрерывный набор дат (то есть для одногоидентификатор сотрудника в упорядоченном наборе: дата начала следующей записи - это дата окончания предыдущей записи) .В приведенном выше примере я попытался привести все сценарии, которые я мог представить в наборе данных.Единственное допущение состоит в том, что исходная система имеет «диапазон дат окончания», то есть запись, которая находится, создается с наибольшей датой окончания для сотрудника в качестве его StartDateId и имеет -1 для EndDateId.

Другойпроблема, из-за которой у меня возникают проблемы, - это когда у меня есть дата окончания, а дата начала не совпадает.Сценарий выше с сотрудником 2 для строк 6 и 7 показывает это.

Мой первый подход состоял в том, чтобы соединить каждую таблицу с измерением даты (количество дней), например:

SELECT T1.IdentityId AS FactId, DD.DimDateId
FROM
    [dbo].[Table_A] T1 JOIN [dbo].[DimDate] DD
        ON DD.DimDateId BETWEEN T1.StartDimDateId AND T1.EndDimDateId

Затем возьмите каждый из этих наборов результатов и соедините их вместе.Я думал, что в этот момент я мог видеть, для каких дней каждый набор данных перекрывался.Тем не менее, я не мог придумать, как вернуться к диапазону дат, как только у меня появятся отдельные дни, и я нахожусь в режиме ограничения производительности, поскольку у меня в каждой таблице сотни тысяч строк, причем диапазон дат каждой строки в среднем составляет околов месяц.

Может кто-нибудь показать мне / помочь мне понять решение этой проблемы?Я работаю в SSIS, чтобы решить эту проблему, но на данный момент я пытаюсь выполнить SQL, так как изначально думал, что это даст выигрыш в производительности.

Я думаю, что эта конкретная проблема представляет собоймного, когда имеешь дело с отчетными базами данных и аналитическими решениями (что именно пытается решить эта конкретная проблема)?

РЕДАКТИРОВАТЬ

Я провел некоторое исследование того, что заполняет таблицы.Похоже, даты для данного идентификатора сотрудника являются непрерывными.

1 Ответ

0 голосов
/ 16 ноября 2011
-- create some tables and data
-- I use the standard convention of using NULL as the end-date
-- for a "still open" interval.
CREATE TABLE tmp.tablea
    ( id serial NOT NULL
    , uid INTEGER NOT NULL
    , begin_date DATE NOT NULL
    , end_date DATE 
    , aval CHAR(1) 
    );
INSERT INTO tmp.tablea (uid, begin_date, end_date, aval) VALUES
  (1, '1980-01-01', '1980-02-01', 'A' )
, (1, '1980-02-01', '1980-03-01', 'B' )
, (1, '1980-03-01', NULL,         'C' )
, (2, '1980-01-15', '1980-02-01', 'D' )
, (2, '1980-02-01', '1980-02-20', 'E' )
, (2, '1980-02-20', NULL,         'F' )
    ;

DROP TABLE tmp.tableb;
CREATE TABLE tmp.tableb
    ( id serial NOT NULL
    , uid INTEGER NOT NULL
    , begin_date DATE NOT NULL
    , end_date DATE 
    , bval CHAR(1) 
    );
INSERT INTO tmp.tableb (uid, begin_date, end_date, bval) VALUES
 (1, '1980-01-10', '1980-02-01', 'G')
,(1, '1980-02-01', '1980-03-01', 'H' )
,(1, '1980-03-01', NULL,         'I' )
,(2, '1980-01-10', '1980-02-06', 'J' )
,(2, '1980-02-06', '1980-03-01', 'K' )
,(2, '1980-03-01', NULL,         'L' )
    ;

SET search_path='tmp';

UPDATE Yet another edit (between is too ugly), fencepost-errors all over the place...

SELECT aa.uid AS uid
    , GREATEST (aa.begin_date, bb.begin_date) as the_begin
    , LEAST (aa.end_date, bb.end_date) as the_end
    , aa.aval
    , aa.begin_date AS bega
    , aa.end_date AS enda
    , bb.bval
    , bb.begin_date AS begb
    , bb.end_date AS endb
FROM tablea aa
    , tableb bb
    WHERE aa.uid = bb.uid
    AND (0=1
        OR (aa.begin_date >= bb.begin_date AND aa.begin_date < bb.end_date  )
        OR (bb.begin_date >= aa.begin_date AND bb.begin_date < aa.end_date )
        OR (bb.end_date > aa.begin_date AND bb.end_date <= aa.end_date  )
        OR (aa.end_date > bb.begin_date AND aa.end_date <= bb.end_date )
        OR (aa.end_date IS NULL AND bb.begin_date >= aa.begin_date)
        OR (bb.end_date IS NULL AND aa.begin_date >= bb.begin_date)
        )
    ;

Результат:

 uid | the_begin  |  the_end   | aval |    bega    |    enda    | bval |    begb    |    endb    
-----+------------+------------+------+------------+------------+------+------------+------------
   1 | 1980-01-10 | 1980-02-01 | A    | 1980-01-01 | 1980-02-01 | G    | 1980-01-10 | 1980-02-01
   1 | 1980-02-01 | 1980-03-01 | B    | 1980-02-01 | 1980-03-01 | H    | 1980-02-01 |  1980-03-01
   1 | 1980-03-01 |            | C    | 1980-03-01 |            | I    | 1980-03-01 | 
   2 | 1980-01-15 | 1980-02-01 | D    | 1980-01-15 | 1980-02-01 | J    | 1980-01-10 | 1980-02-06
   2 | 1980-02-01 | 1980-02-06 | E    | 1980-02-01 | 1980-02-20 | J    | 1980-01-10 | 1980-02-06
   2 | 1980-02-06 | 1980-02-20 | E    | 1980-02-01 | 1980-02-20 | K    | 1980-02-06 | 1980-03-01
   2 | 1980-02-20 | 1980-03-01 | F    | 1980-02-20 |            | K    | 1980-02-06 | 1980-03-01
   2 | 1980-03-01 |            | F    | 1980-02-20 |            | L    | 1980-03-01 | 
(8 rows)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...