Объединение двух таблиц на основе несовпадающих полей даты - PullRequest
0 голосов
/ 19 февраля 2019

Есть две таблицы (A и B), к которым я хочу присоединиться по дате.Это осложняется тем фактом, что даты не обязательно располагаются аккуратно между таблицами.То есть даты из таблицы B могут выходить за пределы или между записями в таблице A.

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

Это пример того, как может выглядеть таблица A (для одного участника):

Table A
-------------------------------------------+
participant start       end
-------------------------------------------+
101         1-1-2010    26-4-2010
101         27-4-2010   2-10-2014
101         3-10-2014   4-1-2015
101         5-1-2015    31-8-2015
101         1-9-2015    12-10-2016
101         13-10-2016  31-12-2018

Ниже приведен пример таблицы B, для которой требуетсядля соединения с таблицей A. Как видите, даты для простого левого соединения слишком сильно отличаются:

Table B
---------------------------------------------------------+
participant start_date  end_date    Content
---------------------------------------------------------+
101         1-1-2012    31-8-2012   A
101         1-9-2012    31-8-2013   B
101         1-9-2013    31-8-2014   C
101         1-9-2014    2-10-2014   D
101         3-10-2014   31-8-2015   E
101         1-9-2015    31-1-2016   F
101         1-9-2015    31-1-2016   F

Идея для объединенной таблицы C состоит в том, что каждая строка таблицы A основана на данных изТаблица B. Я хочу выбрать запись из B, которая попадает в диапазон таблицы A. Если подходит несколько записей из B, следует использовать самую последнюю.Если в таблице B нет информации за этот период (как в случае с первой строкой), следует использовать ближайшую информацию.Иными словами, я хочу, чтобы в каждую строку А. добавлялась самая свежая информация о В.

Table C
----------------------------------------------------------------------+
participant startA      endA        startB      endB        Content
----------------------------------------------------------------------+
101         1-1-2010    26-4-2010   1-1-2012    31-8-2012   A
101         27-4-2010   2-10-2014   1-9-2013    31-8-2014   C
101         3-10-2014   4-1-2015    1-9-2014    2-10-2014   D
101         5-1-2015    31-8-2015   3-10-2014   31-8-2015   E
101         1-9-2015    12-10-2016  1-9-2015    31-1-2016   F
101         13-10-2016  31-12-2018  1-9-2015    31-1-2016   F

Это первый раз, когда я работаю с SAS и SQL, так что моя собственнаяусилия работают очень плохо.Ниже я объединяю эти две таблицы в процедуру, состоящую из нескольких шагов: сначала я создаю полное объединение, чтобы получить все возможные (соответствующие) перестановки таблиц A и B. Затем я вычисляю разницу в датах между данными из таблицы Aи B. Наконец, для каждого периода A я выбираю строку, в которой минимальная разница в датах между данными из исходных таблиц.

/* Create outer join of both tables*/
PROC SQL;
    CREATE TABLE work.fulljoin AS
    SELECT a.*, b.* 
    FROM work.table_A AS a
    FULL JOIN work.table_B AS b ON a.participant = b.participant;
quit;

/* Group by ID and entry date of each period */
PROC SORT data=work.fulljoin;
    BY participant startA; 
RUN;

/* Calculate the date differences between tables A and B */
DATA work.fulljoin_wdelta;
    SET work.fulljoin;
    delta=abs(endA-endB);
RUN;

/* Remove unnecessary rows */
PROC SQL;
    CREATE TABLE output.joined AS
    SELECT * FROM work.fulljoin_wdelta
    GROUP BY participant, startA
    HAVING delta=min(delta);
QUIT;

Однако с большими наборами данных (миллионы строк в A и B) это становится запретительным.Кроме того, этот метод, строго говоря, не гарантирует, что вы будете получать самые последние данные B за каждый период A, то есть только те, которые ближе всего на дату окончания.

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

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

Вот метод коррелированных подзапросов, который ищет «лучший» диапазон контента, соответствующий A. У него будут проблемы, если данные контента не различимы end_dateс in participant.

Каждая строка one (цель) выполняет поиск.Логика перекрытия диапазона важна

      where one.participant = two.participant
        and two.start_date < one.end
        and two.end_date > one.start

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

data one;
input participant start: ddmmyy. end: ddmmyy.;

format start end yymmdd10.;

datalines;
101         1-1-2010    26-4-2010
101         27-4-2010   2-10-2014
101         3-10-2014   4-1-2015
101         5-1-2015    31-8-2015
101         1-9-2015    12-10-2016
101         13-10-2016  31-12-2018
;

data two;
input participant start_date: ddmmyy.  end_date: ddmmyy.   Content: $;

format start_date end_date yymmdd10.;

datalines;
101         1-1-2012    31-8-2012   A
101         1-9-2012    31-8-2013   B
101         1-9-2013    31-8-2014   C
101         1-9-2014    2-10-2014   D
101         3-10-2014   31-8-2015   E
101         1-9-2015    31-1-2016   F
101         1-9-2015    31-1-2017   F
run;

proc sql;
  create table want as 
  select 
    one.*
  , ( select min(content)
      from two 
      where one.participant = two.participant
        and two.start_date < one.end
        and two.end_date > one.start
      group by participant
      having end_date = max(end_date)
    ) as content
  from
    one
  order by
    participant, start
  ;
quit;
0 голосов
/ 19 февраля 2019

Я думаю, вы можете упростить существующую логику в один запрос:

proc sql noprint _method;
  create table table_c as
    select 
      a.participant, 
      a.start as start_a, 
      a.end as end_a, 
      b.start_date as start_b, 
      b.end_date as end_b,
      abs(a.end - b.end_date) as delta
    from table_a a inner join table_b b 
    on a.participant = b.participant
    group by a.participant, start_a
    having delta = min(delta)
    ;     
quit;

Вывод журнала подтверждает, что это выполняет хэш-соединение, если у вас достаточно памяти:

NOTE: SQL execution methods chosen are:

      sqxcrta
          sqxsumg
              sqxsort
                  sqxjhsh
                      sqxsrc( WORK.TABLE_B(alias = B) )
                      sqxsrc( WORK.TABLE_A(alias = A) )

Если полученная таблица отличается от того, что вы пытаетесь создать, уточните.

...