The DL/I PUT Statement

Introduction to the DL/I PUT Statement

If you are unfamiliar with the PUT statement, see SAS Statements: Reference.
A PUT statement writes information to the file specified by the most recently executed FILE statement. If the FILE statement is a DL/I FILE statement, the corresponding PUT statement issues a DL/I update call.
There are no special options for a DL/I PUT statement as there are for the DL/I INFILE and DL/I FILE statements. The form of the DL/I PUT statement is the same as that of the standard PUT statement:
PUT variable optional-specifications;
For example, assume that you are issuing an insert call for the CUSTOMER segment of the AcctDBD database. The following DL/I PUT statement (which looks just like a standard PUT statement) formats a CUSTOMER segment and issues the ISRT call:
put @1   ssnumber       $char11.
    @12  custname       $char40.
    @52  addr_line_1    $char30.
    @82  addr_line_2    $char30.
    @112 custcity       $char28.
    @140 custstat       $char2.
    @142 custland       $char20.
    @162 custzip        $char10.
    @172 h_phone        $char12.
    @184 o_phone        $char12.;
Although the syntax of the DL/I PUT statement is identical to that of the standard PUT statement, your use of the DL/I PUT is often different. Segment format and suggested uses of the DL/I PUT statement are discussed in Using the DL/I PUT Statement.

Example 3: An Update Call

This DATA step reads MyData.Customer, an existing SAS data set containing information about new customers, and updates the AcctDBD database with the data in the SAS data set:
data _null_;
   set mydata.customer;
   length ssa1 $9;
   infile acctsam dli call=func ssa=ssa1
      status=st pcbno=4;
   file acctsam dli;
   func = 'ISRT';
   ssa1 = 'CUSTOMER';
   put @1   ssnumber         $char11.
       @12  custname         $char40.
       @52  addr_line_1      $char30.
       @82  addr_line_2      $char30.
       @112 custcity         $char28.
       @140 custstat         $char2.
       @142 custland         $char20.
       @162 custzip          $char10.
       @172 h_phone          $char12.
       @184 o_phone          $char12.;
   if st ¬= '  ' then
      if st = 'LB' or st = 'II' then
         _error_ = 0;
      else
         do;
            file log;
            put _all_;
            abort;
         end;
run;
To update AcctDBD with new occurrences of the CUSTOMER segment type, this program issues qualified insert calls that add observations from MyData.Customer to the database. The DL/I INFILE statement defines ACCTSAM as the PSB. Options in the INFILE statement specify the following information:
  • The SAS variable FUNC contains the call function.
  • PCBNO= specifies the database PCB to use.
  • SSA1 contains the SSA that specifies the segment name of the segment to be inserted.
  • STATUS= specifies where the status code is returned.
Defaults are in effect for the other DL/I INFILE options: the output buffer length is 1000 bytes, and segment names and PCB mask data are not returned.
If the ISRT call is not successful, the status code variable ST is set with the DL/I status code and the automatic variable _ERROR_ is set to 1. After the ISRT call, the status code variable ST is checked for non-blanks. If the variable value is either LB or II, which indicate that the segment occurrence already exists, the automatic variable _ERROR_ is reset to 0 and processing continues. Otherwise, all values from the program data vector are written to the SAS log, and the DATA step cancels.

Using the DL/I PUT Statement

A PUT statement writes data to the current output file, which is determined by the most recently executed FILE statement. A DL/I PUT statement writes to a DL/I database or message queue by issuing a DL/I update call. If you are unfamiliar with the PUT statement, see SAS Statements: Reference for more information.
In order for a DL/I update call to be executed, the CALL= option must be specified in the DL/I INFILE statement. The value of the CALL= variable must be set to the appropriate update call before the DL/I PUT statement is executed. If CALL= is not specified, the call function defaults to GN and no update calls can be issued.
The update call issued by a DL/I PUT statement might or might not be successful. DL/I returns various status codes that indicate whether the update call was successful. It is always a good idea to check the status code, but it is especially important in an update program. If you are unfamiliar with DL/I status codes, consult your IBM documentation for descriptions. Your SAS program can obtain the return code if the STATUS= option of the INFILE statement is specified. The _ERROR_ and STATUS= variable checking guidelines discussed in Using the DL/I INPUT Statement are also applicable to DL/I PUT statements.

REPL Call

When you replace a segment (REPL call) with a DL/I PUT statement, you must place the entire segment in the output buffer, even if all fields are not being changed.
One way the buffer can be formatted is by specifying all fields and their locations. For example, this DL/I PUT statement formats the entire CUSTOMER segment of the AcctDBD database:
put @1   ssnumber             $char11.
    @12  custname             $char40.
    @52  addr_line_1          $char30.
    @82  addr_line_2          $char30.
    @112 custcity             $char28.
    @140 custstat             $char2.
    @142 custland             $char20.
    @162 custzip              $char10.
    @172 h_phone              $char12.
    @184 o_phone              $char12.;
Another way to format the output buffer is with the _INFILE_ specification. If the current input source is a DL/I INFILE and the last DL/I INPUT statement retrieved the DL/I segment to be replaced, then the following DL/I PUT statement formats the output buffer with the contents of the retrieved segment and holds the segment in the output buffer until another DL/I PUT statement is executed:
put _infile_ @;
A subsequent DL/I PUT statement can modify the data in the output buffer and execute the REPL call. Example 4 illustrates this technique.
Tip
The effect of a trailing @ in a DL/I PUT statement is slightly different from the effect of one in a DL/I INPUT statement. A trailing @ in a DL/I PUT statement causes data to be moved to the output buffer but does not issue the update call. Instead, the call is issued by the next DL/I PUT statement that does not terminate with a trailing @. In a DL/I INPUT statement with a trailing @, the get call is issued, and data is moved to the input buffer. The next DL/I INPUT statement can then move data to the program data vector.

Example 4: Issuing REPL Calls

In this example, CUSTOMER segments are updated with change-of-address information from a SAS 6 data set called MyData.NewAddr. The SAS 6 DATA step interface works exactly like the SAS 7 and later DATA step interfaces, except that the SAS 7 and later DATA step interfaces support SAS variable and member names of up to 32 characters. The interface works as long as the SAS variable names specified in the DL/I INPUT statement match those specified in the DL/I PUT statement. Variables in this SAS data set are SSN (Social Security number), NEWADDR1, NEWADDR2, NEWCITY, NEWSTATE, and NEWZIP. After the CUSTOMER segment is retrieved, the PUT statement formatting the output buffer is issued. The segment is held in the output buffer until a second PUT statement is issued that executes a REPL call to update the CUSTOMER segment.
Notice that SSA1, a qualified SSA, is constructed by concatenating the SSA specification with the value of the SSN variable in the SAS data set. SSA1 is set to blanks after the GHU call because an SSA is not needed for the REPL call. (Since the program issues get calls with qualified SSAs, access is random.)
data _null_;
   set mydata.newaddr;
   length ssa1 $31;
   infile acctsam dli ssa=ssa1 call=func 
      status=st pcbno=4;
   ssa1 = 'CUSTOMER(SSNUMBER =' || ssn || ')';
   func = 'GHU ';
   input;
   if st = '  ' then
      do;
         func = 'REPL';
         ssa1 = '  ';
         file acctsam dli;
         put _infile_ @;
         put @52 newaddr1  $char30.
             @82 newaddr2  $char30.
             @112 newcity  $char28.
             @140 newstate $char2.
             @162 newzip   $char10.;
         if st ¬= '  ' then
            link abendit;
      end;
   else
      if st = 'GE' then
         _error_ = 0;
      else
         link abendit;
   return;

   abendit:
      file log;
      put _all_;
      abort;
run;
Alternatively, the two DL/I PUT statements can be combined into one without the trailing @ sign. For example:
data _null_;
   set mydata.newaddr;
   length ssa1 $31;
   infile acctsam dli ssa=ssa1 call=func 
      status=st pcbno=4;
   ssa1 = 'CUSTOMER(SSNUMBER ='||ssn||')';
   func = 'GHU  ';
   input;
   if st = '  ' then
      do;
         func = 'REPL';
         ssa1 = '  ';
         file acctsam dli;
         put @1  _infile_
             @52 newaddr1 $char30.
             @82 newaddr2 $char30.
             @112 newcity $char28.
             @140 newstatw $char2.
             @162 newzip  $char10.;
         if st ¬= '  ' then
            link abendit;
      end;
   else
      if st = 'GE' then
         _error_ = 0;
      else
         link abendit;
   return;

   abendit:
      file log;
      put _all_;
      abort;
run;

DLET Call

When issuing a delete call (DLET), DL/I requires that the sequence field of the segment be formatted in the output buffer. The DL/I PUT statement formats the sequence field. Alternatively, if the current INFILE is a DL/I INFILE and the last DL/I INPUT statement retrieved the DL/I segment to be deleted, then the following SAS statement formats the output buffer with the contents of the retrieved segment (including the sequence field) and executes the DLET call:
put _infile_;
Example 5: Issuing DLET Calls demonstrates this technique.

Example 5: Issuing DLET Calls

The following example deletes all WIRETRAN segments with a transaction date of 03/31/95:
data _null_;
   length ssa1 $30;
   retain db 'WIRETRN '  ;
   infile acctsam dli call=func dbname=db 
      ssa=ssa1 status=st;
   func = 'GHN ';
   ssa1 = 'WIRETRAN(WIREDATE =03/31/95) ';
   input;
   if st = '  ' then
      do;
         file acctsam dli;
         func = 'DLET';
         ssa1 = '  ';
         put _infile_;
         if st ¬= '  ' then
            link abendit;
      end;
   else
      if st = 'GB' then
         do;
            _error_ = 0;
            stop;
         end;
      else
         link abendit;
   return;

   abendit:
      file log;
      put _all_;
      abort;
run;
Note: A check for a status code of GB is required in this DATA step because it uses a qualified SSA and random access processing. The DATA step does not set the end-of-file condition, and the source logic must check for it to stop the DATA step normally.