1

To 'copy' the PDV structure of a data set, it has been advised to "reference a data set at compile time" using

if 0 then set <data-set>

For example,

data toBeCopied;
    length var1 $ 4. var2 $ 4. ;
    input var1 $ var2 $;

    datalines;
    this  is
    just  some
    fake  data 
    ;
run;

data copyPDV;
    if 0 then set toBeCopied;

    do var1 = 'cutoff' ;
        do var2 =  'words';
            output;
        end;
    end;    
run;

When you run this, however, the following NOTE appears in the log:

NOTE: DATA STEP stopped due to looping.

This is because the data step never reaches the EOF marker and gets stuck in an infinite loop, as explained in Data Set Looping. (It turns out the DATA step recognizes this and terminates the loop, hence the NOTE in the log).

It seems like usage of the if 0 then set <data-set> statement is a longstanding practice, dating as far back as 1987. Although it seems hacky to me, I can't think of another way to produce the same result (i.e. copying PDV structure), aside from manually restating the attribute requirements. It also strikes me as poor form to allow ERRORs, WARNINGs, and NOTEs which imply unintended program behavior to remain in the log.

Is there way to suppress this note or an altogether better method to achieve the same result (i.e. of copying the PDV structure of a data set)?

If you include a stop; statement, as in

if 0 then do;
        set toBeCopied;
        stop;
    end;

the NOTE still persists.

Trying to restrict the SET to a single observation also seems to have no effect:

if 0 then set toBeCopied (obs=1);

Joe
  • 62,789
  • 6
  • 49
  • 67
Lorem Ipsum
  • 4,020
  • 4
  • 41
  • 67
  • 1
    The issue actually has nothing to do with how you are defining your varaibles, but with how you are defining your observations. – Tom Sep 21 '16 at 16:17
  • Do you understand what `if 0` does? That may be part of your problem here. – Joe Sep 21 '16 at 16:19
  • @Tom Can you please elaborate? If I do something like `data copyPDV_v2; if 0 then set toBeCopied; input var1 $ var2 $; datalines; cutoff words cutoff words ; run;` then I no longer get the error, but this seems to defeat the purpose of using `if 0 then set` (to avoid manually copying variables/attributes) – Lorem Ipsum Sep 21 '16 at 16:23
  • See answer below for more detail. – Tom Sep 21 '16 at 16:40

3 Answers3

3

Normally SAS will terminate a data step at the point when you read past the input. Either of raw data or SAS datasets. For example this data step will stop when it executes the SET statement for the 6th time and finds there are no more observations to read.

data want;
  put _n_=;
  set sashelp.class(obs=5);
run;

To prevent loops SAS checks to see if you read any observations in this iteration of the data step. It is smart enough not to warn you if you are not reading from any datasets. So this program does not get a warning.

data want ;
  do age=10 to 15;
    output;
  end;
run;

But by adding that SET statement you triggered the checking. You can prevent the warning by having a dataset that you are actually reading so that it stops when it reads past the end of the actual input data.

data want;
  if 0 then set sashelp.class ;
  set my_class;
run;

Or a file you are reading.

data want ;
  if 0 then set sashelp.class ;
  infile 'my_class.csv' dsd firstobs=2 truncover ;
  input (_all_) (:) ;
run;

Otherwise add a STOP statement to manually end the data step.

data want ;
  if 0 then set sashelp.class;
  do age=10 to 15;
   do sex='M','F';
    output;
   end;
  end;
  stop;
run;
Tom
  • 47,574
  • 2
  • 16
  • 29
  • While not a solution, an alternative is to insert a `put 'NOTE: The following DATA STEP stop is intentional'` or something which lets the reader know it is not an error. As long as the statement includes "NOTE:", the line will be colored appropriately in the log. – Lorem Ipsum Sep 22 '16 at 13:50
2

The stop needs to not be in the if 0 branch. That branch is never executed. stop needs to be executed, and executed at the spot where you want the execution to stop.

data copyPDV;
    if 0 then set toBeCopied;

    do var1 = 'cutoff' ;
        do var2 =  'words';
            output;
        end;
    end;    
    stop;
run;
Joe
  • 62,789
  • 6
  • 49
  • 67
  • D'oh! Of course, that makes sense. The whole concept of `if 0 then set` relies on the fact that `if 0` is always false and is therefore never executed. – Lorem Ipsum Sep 21 '16 at 16:25
  • Do you know of another way to implement this? Since `if 0 then set` is rather opaque, my instinct is to throw it in a macro with an informative name like `%CopyPDVStructure();` so that the logic is more apparent and others who are not familiar with it don't need to get tripped up. This approach won't work if the `if` and `stop;` statements are in separate branches. – Lorem Ipsum Sep 21 '16 at 16:31
  • I don't think I would call `if 0 then set` opaque, it is pretty straightforward what it's doing to me. But that's up to you I guess. If you want to be able to insert arbitrarily in a data step I think your only option would be a fairly complex macro that used DOSUBL and the dictionary tables to construct this; the solutions Shenglin Chen mentions work fine if you're okay with it being a self contained data step or SQL block. – Joe Sep 21 '16 at 16:52
  • 1
    I agree with Joe, `if 0 then set` is used enough to be familiar to SAS programmers. That said, if you really want to hide it in a macro, and you know that macro users will never want two iterations of the data step loop, I suppose you could have your macro be: `if 0 then set &data; if _n_=2 then stop;` But that would feel dangerous to me, because the `if 0 then set` approach is often used in settings where a user wants (needs) multiple iterations of the data step loop. Safer to let them live with the NOTE:, or better yet, code their own STOP. – Quentin Sep 21 '16 at 18:30
  • Actually, thinking about it - if you use the `set sashelp.class(obs=0);` from Shenglin Chen's answer, that should work fine in a macro, no? – Joe Sep 21 '16 at 20:45
  • 1
    @joe don't think it will work in the way the OP wants, where they they are writing records to the output dataset. As soon as `set sashelp.class(obs=0);` executes, I think it will end the data step. – Quentin Sep 22 '16 at 02:28
  • @Quentin That was the behavior which I experienced with `set sashelp.class (obs=0);`. – Lorem Ipsum Sep 22 '16 at 13:40
  • @Quentin Ah, good point - it only works in a separate data step. – Joe Sep 22 '16 at 14:03
2

Not to suppress this note in your situation. It is method to get structure of a data set.

data class;
   set sashelp.class(obs=0);
run;

or

proc sql;
   create table class1 like sashelp.class;
quit;
Shenglin Chen
  • 4,504
  • 11
  • 11