Обновление/манипулирование данными SAS

Я относительно новичок в программировании SAS, но за последние несколько месяцев я освоил основы, и это удовлетворило мои потребности. Однако в настоящее время у меня проблемы, и мне нужна помощь. Я пытаюсь обновить базу данных и создать две новые переменные, которые помогут отслеживать обновления. Итак, я упростил свою проблему со следующими таблицами:

Исходная таблица

ID      Record_ID   Correction_ID
0001    A001    
0002    A002    
0003    A003        A001
0004    A004        A002
0005    A005    
0006    A006        A004

Целевая таблица

ID          Record_ID   Correction_ID   Original_Record     Count
0001        A001                            A001                0
0002        A002                            A002                0
0003        A003            A001            A001                1
0004        A004            A002            A002                1
0005        A005                            A005                0
0006        A006            A004            A002                2

Correction_ID указывает на запись, которую текущая пытается исправить/дополнить.

Количество указывает номер обновления исходной записи.

Спасибо.

Изменить

Коды Proc SQL, которые я пробовал, но не работали:

ID          Record_ID   Correction_ID   Original_Record     Count
Table 1
0001        A001                            A001            0
0002        A002                            A002            0
0005        A005                            A005            0

Table 2
0003        A003            A001        
0004        A004            A002        
0006        A006            A004        

SELECT  ID,
        Record_ID, *how to include ID from both table? Or don’t even separate? 
        Correction_ID, *same as above
        CASE
            WHEN Correction_ID is null THEN One.Original_Record
                ELSE (SELECT Original_Record FROM One WHERE Two.Correction_ID=One.Record_ID)
        END as Original_Record,
        CASE
            WHEN Count is not null THEN One.Count
                ELSE (SELECT Count FROM One WHERE Two.Correction_ID=One.Record_ID) + 1
        END as Count;
        FROM Table 1 AS One, Table 2 AS Two;

person Zheren    schedule 01.09.2015    source источник
comment
Какие шаги вы пытались добавить эти переменные и каков был результат?   -  person Adam B    schedule 01.09.2015
comment
Пожалуйста, опубликуйте код, который вы написали до сих пор в вопросе.   -  person Joe    schedule 01.09.2015
comment
@Joe Я пытался использовать proc SQL, но это не сработало. Я посоветовался с коллегой, и мне сказали использовать три временных массива для Record_ID, Original_Record, Count и выполнять итерации по ним. Но, учитывая мои нынешние знания, я не думаю, что смогу это сделать.   -  person Zheren    schedule 02.09.2015
comment
@ Адам Б см. редактирование. С тех пор я играл с динамическими макросами и думаю, что у меня может быть грубое решение. Включая множество переименований, сортировок, слияний и т. д., что кажется недостаточно эффективным. Возможно, поэтому мой коллега предложил временные массивы.   -  person Zheren    schedule 02.09.2015


Ответы (2)


Следующий код работает для ваших данных как есть. Он использует объект Hash, где сохраняется «Original_Record», а «count» суммируется. Некоторые элементы могут быть избыточными на данный момент (возможно, '_start' не нужен).

data have;
    infile cards truncover;
    input (ID      Record_ID   Correction_ID) (:$8.);
    cards;
0001    A001    
0002    A002    
0003    A003        A001
0004    A004        A002
0005    A005    
0006    A006        A004
0007    A007        
0008    A008        A006
0009    A009        A003
;;;;
run;

data want;
    if _n_=1 then
        do;
            declare hash h();
            h.definekey('_end');
            h.definedata('_end', '_start', '_origin', 'count');
            h.definedone();
            length _end _start _origin $ 8;
            /*call missing (of _:, count);*/
        end;

    set have;

    if missing (correction_id) then
        do;
            original_record=record_id;
            count=0;
        end;
    else
        do;
            rc=h.find(key:correction_id);

            if rc ne 0 then
                    do;
/*if there is no match, this would be the first time of modifying, '_origin' is set to the value of correction_id, count is set to 1*/
                    _origin=correction_id;
                    count=1;
                end;
            else
                do;

/*if there is a match, then '_origin stays the same, so no  
operation is needed, but count increased by 1*/
                    count=count+1;
                end;

            _end=record_id;
            _start=correction_id;
            Original_Record=_origin;
            rc=h.replace();
        end;

    drop rc _:;
run;
person Haikuo Bian    schedule 02.09.2015
comment
Спасибо. Я принял решение в моей проблеме. Не могли бы вы сделать это, если rc ne 0 тогда сделать; _origin=идентификатор_коррекции; количество=1; конец; пытаетесь закодировать? Или, если можно, просто прокомментируйте остальную часть кода? - person Zheren; 02.09.2015
comment
Только что добавлены некоторые комментарии. - person Haikuo Bian; 02.09.2015
comment
Спасибо, поэтому rc=h.find(key:correction_id); проверяет, находится ли correct_id в хэш-объекте, и в основном устанавливает для всего значение count=1 в первый раз, а в последующие разы count получает +1 каждый раз, когда correct_id появляется в хэше, определенном _origin или _end? - person Zheren; 02.09.2015
comment
Это правильно, но не только проверяет, но и возвращает, если есть совпадение. Поэтому убедитесь, что вы понимаете, почему здесь используется метод Find вместо метода Check. - person Haikuo Bian; 02.09.2015
comment
Спасибо за вашу помощь, я почитаю больше о хэше в SAS, чтобы подтвердить свое понимание. - person Zheren; 02.09.2015

Если у вас есть лицензия SAS/OR, то этот код делает примерно то же самое, но проще, так как массивы PROC OPTMODEL — это хеши. Он загружает все данные в оперативную память, поэтому цена простоты — потребление памяти.

Я буду повторно использовать набор данных Haikuo:

 data have;
    infile cards truncover;
    input (ID      Record_ID   Correction_ID) (:$8.);
    cards;
    0001    A001    
    0002    A002    
    0003    A003        A001
    0004    A004        A002
    0005    A005    
    0006    A006        A004
    0007    A007        
    0008    A008        A006
    0009    A009        A003
;

Я не думаю, что нам действительно нужен ID, поэтому я проигнорировал его, чтобы сделать код более наглядным. Он не используется внутри, но при необходимости вы можете добавить его в операторы read data и create data.

proc optmodel;
    set<str,str> RECORDS;
    set ALL = setof{<i,j> in RECORDS} i;
    str parent  {ALL diff {<i,('')> in RECORDS}};
    str original{i in ALL} init i;
    num count   {     ALL} init 0;

    read data have into RECORDS=[Record_Id Correction_ID];
    for {<ri,rj> in RECORDS: rj ~= ''} do;
        parent  [ri] = rj;
        count   [ri] = count   [parent[ri]] + 1;
        original[ri] = original[parent[ri]];
    end;
    create data want from [Record_ID Correction_ID]=RECORDS 
        Original_Record = original[Record_ID] Count = count[Record_ID];
quit;
person Leo    schedule 02.09.2015
comment
Здравствуйте, Лео, к сожалению, у меня есть доступ только к Base SAS, но спасибо, что нашли время написать коды. - person Zheren; 02.09.2015
comment
Не волнуйтесь! Надеюсь, это будет полезно для тех, кто Google это. - person Leo; 03.09.2015