Download as pdf or txt
Download as pdf or txt
You are on page 1of 9

10 Steps to Easier SAS Code Maintenance

Jessica Hampton, CIGNA Corporation, Bloomfield, CT

ABSTRACT
Part of the authors job at CIGNA is to produce on a yearly basis a number of rates used in national healthcare quality surveys and accreditation performance measures. Each year when the revised specs came out, she got tired of having to update processes manually, hunting through programs for diagnosis codes and dates that needed to be changed, copying and pasting the same updates and code blocks so many times she lost count, and then inevitably finding out shed missed a spot when the program either bombed, or worse ran all the way through but returned last years data instead of this years or outdated diagnosis codes. Then when the revisions to the revisions came out and those revised revisions begot new revisions as well, she had to keep going through the same tedious process. This paper describes how to make yearly code maintenance tasks easier in 10 steps, using simple organization techniques, appropriate documentation, macro variables, and parameterized macros. 1. Break large processes into smaller steps. 2. Number programs. 3. Document using comments. 4. Document using program headers. 5. Create an initial startup program. 6. Create a final cleanup program. 7. Use macro variables for date selection criteria. 8. Use macro variables for file naming. 9. Use parameterized macros for similar processes. 10. Use macro variables for long lists that need frequent updating/referencing.

INTRODUCTION
Some of the sections of this paper are targeted toward people in the health insurance industry who work with NCQA-like quality measures which tend to have similarities and variations around a common theme. Such measures often include long lists of diagnoses and procedural codes which get updated on a yearly (or more frequent) basis. However, the larger point of the paper is applicable to many different fields: Think about how to organize programs and process flow before beginning a project (similar to outlining a paper before writing it), use macro variables to store and reference regularly updated values (such as dates) in a consolidated fashion, and minimize coding where possible with parameterized macros.

BREAK UP LARGE PROCESSES


Organize a large process flow by breaking it into logical steps; each smaller step should have its own program. This has the effect of separating a book into chapters, or a large block of text into paragraphs, increasing readability. It makes code easier to maintain since the programmer doesnt have to scroll through thousands of lines of code crammed into one giant program in order to update code.

NUMBER PROGRAMS

Once the process flow is organized into smaller programs, number programs in the order in which they should be run. This is like creating a table of contents or an index. Store each process flow in its own folder.

DOCUMENT USING COMMENTS AND HEADERS


A crucial step to making code easier to maintain (both for the original developer and for anyone else who inherits the code later on), is to liberally sprinkle it with comments. In addition, a program header should include at a minimum the name of the program, the developers name, date developed, a brief description of the purpose of the program, and information about any important updates (programmers name, date updated, and a brief description). It also might include any input tables required or output filenames produced. Sample headers and comments are shown in the examples included within the sections below.

CREATE AN INITIAL STARTUP PROGRAM


For a process flow that is made up of several smaller programs, it helps to create an initial startup program to establish libnames, remote sessions, macro variables that will be referenced during the process, and system options. The setup program in the screenshot below gets named 0_pgm_WHP_setup:

A sample setup program is shown on the next page:

CREATE A FINAL CLEANUP PROGRAM


At the end of the process, create a final program (numbered accordingly) to take care of cleanup: deleting any interim files created earlier in the process which are no longer necessary to store or downloading files that were created on a remote server. Here is also a good place to include statements to create a final table listing and export a data dictionary to Excel, if desired (Charles Patridge).

A sample cleanup program looks like this:

Including field lengths and file sizes in the data dictionary can help the programmer identify at a glance which fields were inadvertently created or extracted with a longer length than needed to store the data (a 200 length field used to store an 8 character string can have a large impact on the size of a file). This information can be used to edit programs later to decrease table sizes and minimize storage space, which is an important consideration when dealing with already large data sets.

USE MACRO VARIABLES FOR DATE SELECTION CRITERIA


The setup program created and initialized macro variables year, yearlast, yearmonth, startdate, birthyear, and birthdate for later reference in the process. Using macro variables for date criteria in the WHERE clauses and CASE WHEN clauses of SELECT statements allows the programmer to update a date or date range once in a single location (the setup program) rather than having to regularly update multiple dates sprinkled throughout code in multiple programs. Examples: where ben_feat_eff_dt <=&startdated and ben_feat_can_dt>&startdated; Case when HRA_Month_Start = 1 Then &yearmonth + HRA_Month_Start Else &yearlastmonth + HRA_Month_Start End as HRA_Prog_Yr_Start,

USE MACRO VARIABLES FOR FILE NAMING


Use macro variables to add dates to the end of file names when creating new files, referencing old files, and also to reference libraries where the path changes yearly or monthly based on date. Examples shown as follows: Create Table qry_Ben_Feat_&year as Proc Download Data = WHP.Internal_EPop_&year OUT=HRA.Internal_EPop_&year; Libname PPO /projects/hedis/ppo&year;

USE PARAMETERIZED MACROS FOR SIMILAR PROCESSES


Sometimes a developer will be asked to write source code which will be used to produce a set of related measures on a regular production cycle. In order to avoid writing unnecessary code for processes which are essentially the same, a developer can perform an analysis by breaking down each measure into smaller pieces and seeing which pieces, if any, are commonly repeated with only slight variations (using a different set of diagnosis codes, for example). The following chart organizes a set of 16 measures, grouping similar measures together: Measure 10,11,12,1 3 Denominator continuously enrolled, 18+ continuously enrolled, 18+ 2+ outpatient/non-acute inpatient events OR 1+ ED/acute inpatient events w/ diabetes principal or secondary diagnosis during measurement year or year prior OR pharmacy event w/diabetes-related prescription continuously enrolled, 18+ 2+ outpatient/non-acute inpatient events OR 1+ ED/acute inpatient events w/ hypertension/CHF/asthma principal or secondary diagnosis during measurement year or year prior continuously enrolled, 18+ w/ appendicitis principal or secondary diagnosis continuously enrolled, 18+ 2+ outpatient/non-acute inpatient events OR 1+ ED/acute inpatient events w/ copd* principal or secondary diagnosis - some diagnoses only qualify with secondary diagnosis codes Measure Numerator include discharges with principal diagnosis code specified: diabetes short term complications diabetes long term complications hypovolemia uncontrolled diabetes Exclusions Exclusions

1,3,14,16

7,8,15 2

1,3,10,14

7*,8,13

include discharges with principal diagnosis code specified: hypertension CHF angina

transfers transfers cardiac procedure codes in any field *For 7 only, also exclude kidney disease diagnoses if accompanied by hemodialysis procs

11,12 2

include discharges with principal diagnosis code specified: bacterial pneumonia UTI include discharges with principal or secondary diagnosis code for perforation or abscess of appendix include discharges with principal diagnosis code for COPD certain diagnoses qualify only if accompanied by secondary diagnosis

transfers immunocompromised state proc codes immunocompromised state diagnoses *For 11, also exclude anemia principal or secondary diagnoses *For 12, also exclude principal or secondary kidney disorder diagnoses transfers

transfers transfers principal or secondary diagnoses of cystic fibrosis

15

16

include discharges with principal diagnosis code for asthma include discharges with principal or secondary diagnosis code for diabetes AND procedure code for lower-extremity amputation

transfers

Doing such an analysis can identify pieces which the developer can convert to parameterized macros to save time coding. Not only does this save time coding initially for the developer, it also saves time maintaining the code, allowing the programmer to revise a single macro rather than copying and pasting the revision in many different places across several programs. In this case, each measure gets its own program, and initial and cleanup programs are also created. Here is an example of the parameterized macro transfer which is then called twice (once for managed care and once for ppo) by all 16 programs in creating the numerators: %macro transfer(filename=)/STORE SOURCE; /*identify and delete transfers from numerator*/ CREATE TABLE &filename._TRANS AS SELECT B.conf_id, B.admit_dt, A.disch_dt FROM PQI.&filename A JOIN PQI.&filename B ON A.rhmo=B.rhmo AND A.bkey=B.bkey AND A.disch_dt BETWEEN B.admit_dt AND intnx('DAY',B.admit_dt,-1) /*re-admitted within 24 hours of discharge date*/ WHERE A.conf_id <> B.conf_id AND A.src_sys_prov_id <> B.src_sys_prov_id /*transferred from different facility*/ ; DELETE FROM PQI.&filename WHERE conf_ID IN (SELECT conf_id FROM &filename._TRANS) ; %mend transfer;

USE MACRO VARIABLES FOR FREQUENTLY UPDATED/REFERENCED LISTS OF CODES


For long lists of procedure and diagnosis codes with no electronic coding table (ECT) available, store them as macro variables all in one place (the startup program) to make them easy to update as the specifications change to include new codes.

%let markets = %str('WA','OR','CA','CO','TN','FL','SC','MD','DC','OH','NJ','NY','CT'); /*eValue8 markets*/ %let diabetes = %str('25000','25001','25002','25003','25010','25011','25012','25013','25020','2502 1','25022','25023','25030','25050','25051','25052','25053','25060','25061','25062' ,'25063','25070','25071','25072','25073','25080','25031','25032','25033','25040',' 25041','25042','25043','25081','25082','25083','25090','25091','25092','25093'); /*diabetes diagnosis codes, PQI 1,3,14,16*/ For long lists of codes with an ECT available, use the SQL procedure to select into macro variables that are easy to reference in multiple places, eliminating the need to join to and select from the ECT more than once. When in development, use system options SYMBOLGEN and MPRINT to check the log to make sure that the macro variables resolve correctly. SELECT code INTO: opnaiprev separated by '","' /*outpatient, non-acute inpatient revenue proc codes*/ FROM ref.ect WHERE description in('outpatient','nonacute_inpatient') AND typeofcode='RevCode' AND tablename='CDC_C' ; SELECT code INTO: aipedcpt separated by '","'/*acute inpatient, ed cpt proc codes*/ FROM ref.ect WHERE description in('acute_inpatient','ed') AND typeofcode='CPT' AND tablename='CDC_C' /*ophthalmological services not needed for non-diabetic measures - 5,7,8,15*/ ; Making use of macro variables makes the resulting code much shorter and easier to understand; instead of having the same long list of diagnosis codes repeated multiple times below, it is easier to see what the WHERE clause is actually doing without having to scroll: WHERE svc_dt BETWEEN "01jan&yearp"d AND "31dec&year"d /*current year or year prior*/ AND (diag_cd1 IN (&diagcd) OR diag_cd2 IN (&diagcd) OR diag_cd3 IN (&diagcd)) /*primary or secondary diagnosis code*/ AND (proc_cd IN("&aipedcpt") /*acute inpatient or ED cpt codes*/ OR (proc_cd IN("&aipedrev") /*3-byte rev codes for mco, 4-byte for ppo*/

Putting together the macro variables with the parameterized macros, the process of building denominators and numerators to create the first measure looks like this:

Instead of having to repeat blocks of nearly identical code, using parameterized macros for similar processes greatly shortens the amount of code requiring maintenance.

CONCLUSIONS
Using simple techniques to organize process flow, macro variables to minimize hard coding, and parameterized macros along with even minimal documentation can greatly improve ease of code maintenance. This approach facilitates code maintenance in three ways: by making portions of frequently updated code easier to locate (like having a good filing system), minimizing hard coding of dates and lists of codes, and by decreasing the overall volume of code created in the first place.

REFERENCES:
SAS Institute Inc. 2009. SAS 9.2 Macro Language Reference. Cary, NC: SAS Institute, Inc. Patridge, C. Best Practices: Using SAS Effectively/Efficiently. HASUG presentation 2/24/2011. Rhodes, D. If You Have Programming Standards, Please Raise Your Hand: An Everymans Guide. http://www.nesug.org/Proceedings/nesug10/ma/ma10.pdf (5/31/2011).

ACKNOWLEDGMENTS
SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. indicates USA registration.

CONTACT INFORMATION
Your comments and questions are valued and encouraged. Contact the author at: Jessica Hampton CIGNA Corporation 900 Cottage Grove Rd Bloomfield, CT 06002 Work Phone: (860) 226-1938 Email: Jessica.Hampton@cigna.com Web: http://www.linkedin.com/profile/view?id=40228999&locale=en_US&trk=tab_pro
************************************************

You might also like