Суммирование / подсчет перекрывающихся значений или дат с группой по идентификаторам в sql - PullRequest
0 голосов
/ 13 марта 2019

Я работаю с таблицей sas, а даты представлены в виде чисел, указанных в столбцах «введено» и «оставлено». Я должен сосчитать количество дней, в течение которых участник оставался в системе. Как, например, ниже для идентификатора 1, человек вошел на 7071 и снова использовал другой продукт на 7075, хотя он постоянно находился в системе с 7071 по 7083. То есть даты перекрываются. Я хочу посчитать окончательную продолжительность пребывания участника в системе, например, для идентификатора 1 это 12 дней (7083-7071) + 2 дня (7087–7089) + 4 дня (7095–7099). Итого 18 дней. (Есть некоторые повторяющиеся введенные и оставленные значения, но другие столбцы (здесь не показаны) не совпадают, поэтому эти строки не были удалены.). Поскольку я работаю в sas, идея может быть как в формате sas, так и в формате sas-sql.

Для элемента 2 перекрытие значений отсутствует. Таким образом, количество дней составляет 2 (от 8921 до 8923) + 5 дней (от 8935 до 8940) = 7 дней. Я смог решить этот случай, поскольку дни не перекрывались, но в случае совпадения приветствуются любые предложения или коды / советы.

id  Entered  left
 1    7071   7077
 1    7071   7077
 1    7075   7079
 1    7077   7083
 1    7077   7083
 1    7078   7085
 1    7087   7089
 1    7095   7099
 2    8921   8923
 2    8935   8940

Итак, финальный стол должен быть в форме

id  days_in_system
 1       18
 2       7

1 Ответ

1 голос
/ 13 марта 2019

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

Вот решение на основе хеша - идея состоит в том, чтобы создать хеш, содержащий все отдельные дни, в течение которых участник оставался с вами, а затем подсчитать количество элементов в нем в конце:

data have;
input id  Entered  left;
cards;
 1    7071   7077
 1    7071   7077
 1    7075   7079
 1    7077   7083
 1    7077   7083
 1    7078   7085
 1    7087   7089
 1    7095   7099
 2    8921   8923
 2    8935   8940
 ;
 run;

data want;

length day 8;
if _n_ = 1 then do;
  declare hash h();
  rc = h.definekey('day');
  rc = h.definedone();
end;

do until(last.id);
  set have;
  by id;
  do day = entered to left - 1;
    rc = h.add();
  end;
end;

total_days = h.num_items;
rc = h.clear();
keep id total_days;

run;

Это должно быть достаточно мало для памяти, так как нужно загружать дни только для одного идентификатора за раз.

Выход с идентификатора 1 равен 20, а не 18 - вот разбивка новых днейдобавил строку за строкой, которую я сгенерировал, добавив немного логики отладки.Если это не так, укажите где:

_N_=1
7071 7072 7073 7074 7075 7076
_N_=2
No new days
_N_=3
7077 7078
_N_=4
7079 7080 7081 7082
_N_=5
No new days
_N_=6
7083 7084
_N_=7
7087 7088
_N_=8
7095 7096 7097 7098
_N_=1
8921 8922
_N_=2
8935 8936 8937 8938 8939

Если вы хотите добавить только дни для строк, соответствующих определенному условию, вы можете выбрать их, используя предложение where в операторе set, например,

  set have(where = (var1 in ('value1', 'value2', ...)));
...