2015-12-14 3 views
0

У меня есть две большие таблицы, которые я пытаюсь объединить, чтобы сгруппировать первые записи на основе поля из второй таблицы. Левая таблица содержит около 50 млн записей событий, в правой таблице - около 35 млн записей ежемесячных интервалов. Ежемесячные интервалы находятся на уровне subjID, поэтому я не могу уменьшить размер правой таблицы, сохраняя только даты начала и окончания. В настоящее время для соединения требуется около 40-60 минут.Оптимизация SAS Proc SQL-запрос

Я попытался создать простые индексы для subjID, eventDate, startDate и endDate, однако, похоже, это не улучшило производительность (создание индексов завершено за 5 минут, соединение завершено за 38 минут).

Есть ли другой вариант, который я мог бы использовать для улучшения обработки?

Левая таблица событий на уровне subjID:

data eventsTable; 
input @1 subjID 8. 
    @10 eventDate date9.; 
format eventDate mmddyy10.; 
datalines; 
101  01AUG2011 
101  28AUG2011 
101  30AUG2011 
101  01SEP2011 
101  12SEP2011 
101  28SEP2011 
102  01JAN2015 
102  15JAN2015 
102  01FEB2015 
102  16FEB2015 
; 
run; 

Right Таблица ежемесячных интервалов на уровне subjID. Я пытаюсь привести ENDDATE к событиям, если события произошли между датой начала и окончания:

data monthlyTable; 
input @1 subjID 8. 
    @10 startDate date9. 
    @22 endDate date9.; 
format startDate endDate mmddyy10.; 
datalines; 
101  28JUL2011 30AUG2011 
101  30AUG2011 28SEP2011 
101  28SEP2011 28OCT2011 
102  01DEC2014 02JAN2015 
102  02JAN2015 02FEB2015 
102  02FEB2015 02MAR2015 
; 
run; 

Выход:

proc sql; 
create table wantTable as 
    select a.*, 
     endDate as monthlyDate 
    from eventsTable a left join monthlyTable b on 
     a.subjID = b.subjID 
    where a.eventDate > b.startDate and a.eventDate <= b.endDate 
     order by subjID, eventDate; 
quit; 
+0

Вы просто добавляете 'enddate' из' monthTable' или присоединяете ли вы некоторые другие переменные? – user667489

+0

Я добавляю 5 переменных из ежемесячной таблицы. На практике моя левая таблица имеет 6 переменных, а моя правая таблица имеет 11 переменных. – Chibuzo

+0

Я обновил свой ответ, включив подход хеширования, который позволит вам прикрепить все переменные за один раз. – user667489

ответ

1

Если у вас достаточно памяти, и вам нужен только enddate от monthlyTable, вы можете обнаружить, что слияние формата - это более эффективный способ сделать это. Однако, если оба набора данных являются большими, есть только такая оптимизация, на которую вы можете надеяться, поскольку вам всегда нужно делать по крайней мере полное чтение каждого из них.

data t_format(keep = fmtname--hlo) /view = t_format; 
    set monthlytable(keep = subjID startdate enddate) end = eof; 
    retain fmtname 'myinfmt' type 'i'; 
    length start end $18; /*Increase for IDs longer than 8 digits*/ 
    start = cats(put(subjID,z8.),put(startdate + 1,yymmdd10.)); 
    end = cats(put(subjID,z8.),put(enddate,yymmdd10.)); 
    label = enddate; 
    output; 
    if eof then do; 
    hlo = 'O'; 
    label = .N; 
    output; 
    end; 
run; 

proc format cntlin = t_format; 
run; 

data want; 
    set eventstable; 
    enddate = input(cats(put(subjID,z8.),put(eventdate,yymmdd10.)),myinfmt18.); 
    format enddate yymmdd10.; 
run; 

Обратите внимание на использование yymmdd10. и z8. форматов - это гарантирует, что ключи всегда такой же длины, избегая неоднозначности, и что диапазоны значений подстановки правильно указаны в порядке возрастания при создании цифровой Informat myinfmt. Полагаю, строго говоря, это informate merge, а не формат сливается, но это такая же идея.

Если вы хотите вернуть несколько переменных поиска с помощью этого подхода, вам необходимо объединить их вместе при определении формата и затем разделить их после его применения.

Я бы оценил, что для указанных подходов требуется примерно 1,5 ГБ памяти для указанных вами наборов данных (т. Е. (18 байт x 2 для диапазона дат + 8 байтов для форматированного значения) x 35 м строк. В зависимости от длины ваших идентификаторов это может немного отличаться.

Если вам нужны несколько значений поиска, вы можете сделать аналогичную вещь, используя хэш-слияние, но я подозреваю, что слияние формата более эффективно в этом случае.

Одним из возможных хэш слияния подход выглядит следующим образом:

data t_lookup /view= t_lookup; 
    set monthlytable; 
    by subjID; 
    if first.subjID then id_range_count = 0; 
    id_range_count + 1; 
run; 

data want; 
    set eventstable; 
    if _n_ = 1 then do; 
    if 0 then set monthlytable(keep = subjID startdate enddate); /*Add extra lookup vars here as needed*/ 
    declare hash h(dataset:"t_lookup"); 
    rc = h.definekey("subjID","id_range_count"); 
    rc = h.definedata("startdate","enddate"); /*Add extra lookup vars here as needed*/ 
    rc = h.definedone(); 
    end; 
    match = 0; 
    rc = 0; 
    do id_range_count = 1 by 1 while(rc = 0 and match = 0); 
    rc = h.find(); 
    match = startdate < eventdate <= enddate; 
    end; 
    if match = 0 then call missing(startdate,enddate); 
    drop rc match id_range_count; 
run; 
+0

Я использовал один и тот же набор данных 6 раз, используя каждый из предложенных методов: Proc SQL, Hash Merge и Format Merge, а слияние форматов дали незначительно лучшие результаты. Я запускал его несколько раз чтобы получить более репрезентативный набор. Средние значения для каждого метода следующие (h: mm: ss): Proc SQL = 0:19:21; Hash Merge = 0:21:58; Формат Merge = 0:16:11. Первоначальный результат 40 минут был прослежен до проблем на сервере, поэтому он не был представителем. – Chibuzo

0

Лучший индекс для запроса является составной индекс на monthlyTable(subjId, startDate, endDate). Однако я не уверен, что это будет большим улучшением с точки зрения производительности в SAS.

+0

Я попытался использовать составной индекс, однако я думаю, что SAS не использует индексы, которые я создал вообще. У меня есть сортировка в конце моего запроса 'wantTable' и на основе журнала, я думаю, что SAS использует это. 'ПРИМЕЧАНИЕ: использовалась сортировка с использованием SAS.' – Chibuzo

0

У меня была лучшая удача с предварительной сортировки наборов данных, чем при создании индексов. Однако предварительная сортировка может занять много времени в зависимости от размера наборов данных и того, что вы их сортируете. Это может занять больше времени, чем исходный SQL-запрос, поэтому тестирование становится важным.

Попробуйте запустить

PROC SORT DATA=eventsTable ; 
    BY subjID eventDate ; 
RUN ; 

PROC SORT DATA=monthlyTable ; 
    BY subjID startDate endDate ; 
RUN ; 

до вашего PROC SQL. Единственное объяснение, которое у меня есть, это то, что SAS распознает информацию заголовка SORT BY и не требует сканирования целых таблиц, ищущих объединения, поскольку данный subjID, вероятно, будет только на нескольких последовательных страницах. На нескольких последовательных страницах также уменьшается количество операций ввода-вывода.