The DL/I INPUT Statement

Introduction to the DL/I INPUT Statement

If you are unfamiliar with the INPUT statement, see SAS Statements: Reference for more information.
An INPUT statement reads from the file that is specified by the most recently executed INFILE statement. If the INFILE statement is a DL/I INFILE statement, the INPUT statement issues a DL/I get call and retrieves a segment or segments.
There are no special options for the DL/I INPUT statement as there are for the DL/I INFILE statement. The form of the DL/I INPUT statement is the same as that of the standard INPUT statement:
input variable optional-specifications;
For example, suppose you are issuing a qualified get call for the CUSTOMER segment. The DL/I INPUT statement might be coded like this:
input @1   soc_sec_number  $char11.
      @12  customer_name   $char40.
      @52  addr_line_1     $char30.
      @82  addr_line_2     $char30.
      @112 city            $char28.
      @140 state           $char2.
      @142 country         $char20.
      @162 zip_code        $char10.
      @172 home_phone      $char12.
      @184 office_phone    $char12.;
When this DL/I INPUT statement executes, DL/I retrieves a CUSTOMER segment and places it in the input buffer. Data for the variables specified in the DL/I INPUT statement is then moved from the input buffer to SAS variables in the program data vector by SAS.
Different forms of the INPUT statement can have different results:
  • When an INPUT statement specifies variable names (as in the previous example), the segment is usually retrieved and placed in the input buffer and the values are moved immediately to SAS variables in the program data vector unless this form of the INPUT statement is preceded by an INPUT statement with a trailing @ sign, such as input@. The INPUT statement with a trailing @ sign is described below.
  • If the INPUT statement does not specify any variable names or options, as in this example:
    input;
    a segment or segments are retrieved by the call and placed in the input buffer but no data is mapped to the program data vector. Or, if the previous INPUT statement was input@, this clears the hold.
  • If the INPUT statement does not specify variable names but does have a trailing @:
    input @;
    a call is issued and one or more segments are retrieved and placed in the input buffer. The trailing @ tells SAS to use the data just placed in the input buffer when the next DL/I INPUT statement in that execution of the DATA step is executed. In other words, the trailing @ tells SAS not to issue another call the next time a DL/I INPUT statement is executed. Instead, SAS uses the data that is already in the input buffer. This form of the INPUT statement is very useful in IMS DATA step programs. For more information, see Using the DL/I INPUT Statement.
  • You can combine the form that names variables with the form that uses a trailing @. In this example, a call is issued, a segment is retrieved and placed in the input buffer, and values for the named variables are moved to SAS variables in the program data vector:
    input soc_sec_number $char11. @;
    Because of the trailing @, SAS holds the segment in the input buffer for the next INPUT statement.
Although the syntax of the DL/I INPUT statement and the standard INPUT statement are the same, your use of the DL/I INPUT statement is often different. Suggested uses of the DL/I INPUT statement are discussed in Using the DL/I INPUT Statement.

Example 1: A Get Call

The following DATA step illustrates how to issue get calls using the DL/I INFILE and DL/I INPUT statements:
data custchck;
   retain ssa1 'CUSTOMER*D '
          ssa2 'CHCKACCT ';
   infile acctsam dli ssa=(ssa1,ssa2) status=st 
          pcbno=3;
   input @1   soc_sec_number       $char11.
         @12  customer_name        $char40.
         @52  addr_line_1          $char30.
         @82  addr_line_2          $char30.
         @112 city                 $char28.
         @140 state                $char2.
         @142 country              $char20.
         @162 zip_code             $char10.
         @172 home_phone           $char12.
         @184 office_phone         $char12.
         @226 check_account_number $char12.
         @238 check_amount         pd5.2
         @243 check_date           mmddyy6.
         @251 check_balance        pd5.2;
   if st ¬= '  ' then
      do;
         file log;
         put _all_;
         abort;
      end;
run;

proc print data=custchck;
   title2 'Customer Checking Accounts';
run;
This DATA step creates a SAS data set, CustChck, with one observation for each checking account in the AcctDBD database. To build the data set, the program issues qualified get-next path calls using unqualified SSAs for the CUSTOMER and CHCKACCT segments. The path call is indicated by the *D command code in the CUSTOMER SSA, SSA1. The PCBNO= option specifies the first eligible PCB that permits path calls for the CUSTOMER segment of the AcctDBD database.
The DL/I INFILE statement points to the ACCTSAM PSB and specifies two SSA variables, SSA1 and SSA2. The SSA variables have already been assigned values and lengths by the preceding RETAIN statement. Since these SSAs are not qualified, the program access is sequential. In this get call, the status code is checked and the third PCB is specified. Defaults are in effect for the other DL/I INFILE options: only get-next calls are issued, the input buffer length is 1000 bytes, and segment names and PCB mask data are not returned.
When the DL/I INPUT statement executes and status = ' ', the qualified GN call is issued, the concatenated CUSTOMER and CHCKACCT segments are placed in the input buffer, and data from both segments are moved to SAS variables in the program data vector.
The following output shows the results of this example.
Results of Issuing Get Calls
                                                       The SAS System           
                                     
                                                  Customer Checking Accounts

                 soc_sec_                               addr_
         OBS      number       customer_name            line_1    addr_line_2            city               state

           1    667-73-8275    WALLS, HOOPER J.                   4525 CLARENDONRD       RAPIDAN             VA  
           2    667-73-8275    WALLS, HOOPER J.                   4525 CLARENDONRD       RAPIDAN             VA  
           3    434-62-1234    SUMMERS, MARY T.                   4322 LEON ST.          GORDONSVILLE        VA  
           4    436-42-6394    BOOKER, APRIL M.                   9712WALLINGFORD PL.    GORDONSVILLE        VA  
           5    434-62-1224    SMITH, JAMES MARTIN                133 TOWNSENDST.        GORDONSVILLE        VA  
           6    434-62-1224    SMITH, JAMES MARTIN                133 TOWNSENDST.        GORDONSVILLE        VA  
           7    178-42-6534    PATTILLO, RODRIGUES                9712 COOK RD.          ORANGE              VA  
           8    156-45-5672    O'CONNOR, JOSEPH                   235 MAIN ST.           ORANGE              VA  
           9    657-34-3245    BARNHARDT, PAMELA S.               RT 2 BOX 324           CHARLOTTESVILLE     VA  
          10    667-82-8275    COHEN, ABRAHAM                     2345 DUKE ST.          CHARLOTTESVILLE     VA  
          11    456-45-3462    LITTLE, NANCY M.                   4543 ELGINAVE.         RICHMOND            VA  
          12    234-74-4612    WIKOWSKI, JONATHAN S.              4356 CAMPUDRIVE       RICHMOND             VA  

                                                                           check_
                                                                          account_       check_    check_     check_
         OBS    country     zip_code      home_phone     office_phone      number        amount     date     balance

           1      USA      22215-5600    803-657-3098    803-645-4418   345620145345    1702.19     12857    1266.34
           2      USA      22215-5600    803-657-3098    803-645-4418   345620154633    1303.41     12870    1298.04
           3      USA      26001-0670    803-657-1687                   345620104732     826.05     12869     825.45
           4      USA      26001-0670    803-657-1346                   345620135872     220.11     12868     234.89
           5      USA      26001-0670    803-657-3437                   345620134564    2392.93     12858    2645.34
           6      USA      26001-0670    803-657-3437                   345620134663       0.00     12866     143.78
           7      USA      26042-1650    803-657-1346    803-657-1345   745920057114    1404.90     12944    1502.78
           8      USA      26042-1650    803-657-5656    803-623-4257   345620123456     353.65     12869     463.23
           9      USA      25804-0997    803-345-4346    803-355-2543   345620131455    1243.25     12871    1243.25
          10      USA      25804-0997    803-657-7435    803-645-4234   382957492811    7462.51     12876    7302.06
          11      USA      26502-3317    803-657-3566                   345620134522     608.24     12867     831.65
          12      USA      26502-5317    803-467-4587    803-654-7238   345620113263     672.32     12870      13.28
See Example 6: Issuing Path Calls later in this section for a detailed explanation of a sample IMS DATA step program that includes a similar DATA step.

Using the DL/I INPUT Statement

Checking Status Codes

A get call might or might not successfully retrieve the requested segments. For each call issued, DL/I returns a status code that indicates whether the call was successful. Since the success of a call can affect the remainder of the program, it is a good idea to check status codes, especially in programs that use random access. You can obtain the status code returned by DL/I with the STATUS= option or the PCBF= option of the DL/I INFILE statement. See your IBM documentation for explanations of DL/I status codes.
In general, a call has been successful and the segment(s) has been obtained if the automatic SAS variable _ERROR_ has a value of zero. This corresponds to a blank DL/I return code, or codes of CC, GA, or GK. SAS sets _ERROR_ to 1 if any other DL/I status code is returned or if the special SAS status code SE is returned. (The SE code is generated when SAS cannot format a proper DL/I call from the options specified.) If _ERROR_ is set to 1, the contents of the input buffer and the program data vector are printed on the SAS log when another INPUT statement is executed or when control returns to the beginning of the DATA step, whichever comes first.
Some of the DL/I status codes that set _ERROR_ might not be errors to your SAS program. When this is the case, you should check the actual return code as well as the value of _ERROR_. For example, suppose you are writing a program that looks for a segment with a particular value for a sequence field. If the segment is found, a replace call (REPL) is issued to update the segment. If the segment is not found, _ERROR_ is set to 1, but you do not consider the status code to be an error. Instead, you issue an insert call (ISRT) to add a new segment.
If a status code sets _ERROR_ but you do not consider the status code to be an error, you should reset _ERROR_ to zero before executing another INPUT or PUT statement or returning to the beginning of the DATA step. Otherwise, the contents of the input buffer and program data vector are printed on the SAS log.

Use of the Trailing @

You can use different forms of the DL/I INPUT statement to perform these general functions:
  • issue a DL/I get call
  • place the retrieved segment in the input buffer
  • move data from the input buffer to SAS variables in the program vector if variables are named in the INPUT statement.
In some programs, it is important to check the values of the _ERROR_ or STATUS= variables before moving data from the input buffer to SAS variables in the program data vector. For example, if a get call fails to retrieve the expected segment, the input buffer might still contain data from a previous get call or be filled with missing values. You might not want to move these values to SAS variables. By checking the STATUS= or _ERROR_ variable, you determine whether the call was successful and can decide whether to move the input buffer data to SAS variables.
Similarly, if you issue unqualified get calls with a PCB that is sensitive to more than one segment type, you might need to know what type of segment was retrieved in order to move data to the appropriate SAS variables.
When you want to issue a get call but you need to check _ERROR_ or STATUS= variable values before moving data to SAS variables, use a DL/I INPUT statement with a trailing @ to issue the call:
input @;
The trailing @ pointer control causes SAS to hold the current record (segment) in the input buffer for the next DL/I INPUT statement. The next DL/I INPUT statement to be executed does not issue another call and does not place a new segment in the input buffer. Instead, the second INPUT statement uses the data placed in the input buffer by the first INPUT statement.
If no variables are named in the first DL/I INPUT statement (as in the statement shown in the previous paragraph), data is not moved from the buffer to SAS variables until another DL/I INPUT statement specifying the variables is executed. Therefore, before executing a second INPUT statement, you can check the value of the STATUS= or PCBF= variable to determine whether the call was successful. You can also check the _ERROR_ automatic variable and the SEGMENT= variable. After checking these values, execute a second DL/I INPUT statement to move data to SAS variables, if appropriate.

Example 2: Using the Trailing @

This example demonstrates the use of the trailing @. This DATA step creates two SAS data sets, Checking and Savings, from data in the CHCKACCT and SAVEACCT segments of the AcctDBD database. The PCB that is used defines CUSTOMER, CHCKACCT, and SAVEACCT as sensitive segments. Since no CALL= or SSA= variables are specified, all calls are unqualified get-next calls, and access is sequential.
Each call is issued by a DL/I INPUT statement with a trailing @, so the retrieved segment is placed in the buffer and held there. Two variables are checked: ST and SEG (the SEGMENT= variable). If a call results in an error, the job terminates. If a call is successful, the program checks SEG to determine the type of the retrieved segment. Because this is a sequential access program, a GB (end-of-file) status code is not treated as an error by the program. Therefore, the program resets _ERROR_ to 0.
When SEG='CUSTOMER', execution returns to the beginning of the DATA step. When the SEG value is CHCKACCT or SAVEACCT, another DL/I INPUT statement moves the data to SAS variables in the program data vector, and the observation is written to the appropriate SAS data set.
data checking savings;
   infile acctsam dli segment=seg status=st 
        pcbno=3;
   input @;
   if st ¬= '  ' and
      st ¬= 'CC' and
      st ¬= 'GA' and
      st ¬= 'GK' then
         do;
            file log;
            put _all_;
            abort;
         end;
   if seg = 'CUSTOMER' then
     return;
   else
     do;
       input @1  account_number $char12.
             @13 amount         pd5.2
             @18 date           mmddyy6.
             @26 balance        pd5.2;
       if seg = 'CHCKACCT' then
         output checking;
       else
         output savings;
   end;
run;

proc print data=checking;
   title2 'Checking Accounts';
run;

proc print data=savings;
   title2 'Savings Accounts';
run;
The following output shows the results of this example:
Results of Using the Trailing @
                                  The SAS System                   
                                 Checking Accounts   
 
                          account_
                 OBS       number        amount     date    balance     

                   1    345620145345    1702.19    12857    1266.34     
                   2    345620154633    1303.41    12870    1298.04     
                   3    345620104732     826.05    12869     825.45     
                   4    345620135872     220.11    12868     234.89     
                   5    345620134564    2392.93    12858    2645.34     
                   6    345620134663       0.00    12866     143.78     
                   7    745920057114    1404.90    12944    1502.78     
                   8    345620123456     353.65    12869     463.23     
                   9    345620131455    1243.25    12871    1243.25     
                  10    382957492811    7462.51    12876    7302.06     
                  11    345620134522     608.24    12867     831.65     
                  12    345620113263     672.32    12870      13.28     


                                   The SAS System                 
                                  Savings Accounts

                          account_
                 OBS       number        amount     date    balance

                   1    459923888253     784.29    12870     672.63
                   2    345689404732    8406.00    12869    8364.24
                   3    144256844728     809.45    12863    1032.23
                   4    345689473762     130.64    12857     261.64
                   5    345689498217    9421.79    12858    9374.92
                   6    345689462413     950.96    12857     946.23
                   7    345689435776     136.40    12869     284.97
                   8    859993641223     845.35    12860    2553.45
                   9    884672297126     945.25    12868     793.25
                  10    345689463822     929.24    12867     924.62
Note: If the DL/I call is issued by a DL/I INPUT statement with a trailing @ and the status code sets _ERROR_, but you do not consider the status code to be an error and you want to issue another get call in the same execution of the DATA step, then you must first execute a blank DL/I statement: input;
The blank DL/I INPUT statement clears the input buffer. If the buffer is not cleared by issuing a blank INPUT statement, the next DL/I INPUT statement assumes that the data to be retrieved is already in the buffer and does not issue a DL/I call. See Example 8: Using the Blank INPUT Statement for an example that includes a blank INPUT statement.