You are on page 1of 5

Paper DM01

SAS® and Oracle® Clinical Hand-in-Hand

Ramkumar Krishnamurthy, Allergan, Santa Barbara, California, USA

ABSTRACT
Most research companies use Oracle Clinical to gather and store clinical data and use SAS for analysis purpose. When it
comes to large studies with 100,000 patients and at least 10 CRFs per patient, SAS needs to download and handle million
rows of data from Oracle database. This presentation has 3 goals:
1. Introduction on the usual way to download the data from Oracle Database to SAS for smaller studies.
2. How the usual method to download data from Oracle to SAS compromises the Oracle Clinical server performance for
large studies and
3. The enhanced way to download large data without compromising on the Oracle Clinical performance.

INTRODUCTION
This paper introduces Materialized views available in Oracle database that can be used for faster downloads in SAS.

DETAILS
Usual Extract method:
Assuming that the following steps are done prior to download:
Execute the batch validation in OC (refer to screen shot Figure 1 shown).
Generate Data Extract Views and have the script ready.

Figure 1

A sample of the script generated by the ‘Data Extract Views’ is shown here:
CREATE OR REPLACE VIEW FORM_IC
(STUDY, DCMNAME, DCMSUBNM, SUBSETSN, DOCNUM, INVSITE, INV, PT, ACCESSTS, LOGINTS,

Page 1 of 5
LSTCHGTS, LOCKFLAG, CPEVENT, DCMDATE, DCMTIME, REPEATSN, ACTEVENT, SUBEVENT_NUMBER, VISIT_NUMBER,
QUALIFYING_VALUE, QUALIFYING_QUESTION, LAB, LABRANGE_SUBSET_NUMBER, LAB_ASSIGNMENT_TYPE_CODE,
LAB_ID, PREGNANT_NURSING, FIBROCYSTIC_DISEASE, CARCINOMA_NO_MASTE, ABSCESS_INFECTION,
DISEASE_IMPA_W_HEAL, TISSUE_INCOMPATIBLE, TREATMENT_SURG_RISK, PSYCHO_INCOMPATIBLE,
WISHES_AUG_NOT_ELIG, NOT_WILLING_REVISION, LUPUS_SCLERODERMA, REPLACE_COSMETIC, DATE_OF_BIRTH,
FEM_UND18_PARENTIC, COND_SITUATIONS, R_POST_MASTECT, L_POST_MASTECT, L_POST_TRAUMA,
R_POST_TRAUMA, L_SEVERE_PTOSIS, R_SEVERE_PTOSIS, L_PECTUS_EXCAVATUM, R_PECTUS_EXCAVATUM,
R_PECTUS_CARINATUM, L_PECTUS_CARINATUM, R_THOR_HYPOPLASIA, L_THOR_HYPOPLASIA, R_SCOLIOSIS,
L_SCOLIOSIS, R_RIB_DEFORMITIES, L_RIB_DEFORMITIES, R_TUBEROUS_BREAST, L_TUBEROUS_BREAST,
R_CONG_BRE_ABSENCE, L_CONG_BRE_ABSENCE, R_CONTRA_MAMMA, L_CONTRA_MAMMA, R_REVISION_IMPLANT,
L_REVISION_IMPLANT, ADEQUATE_TISSUE, SIGNED_INF_CONSENT, SALINE_INAPPR_CHOICE, HEIGHT, WEIGHT,
PHYS_EXAM_NORMAL, PHYS_EXAM_SPEC, ECG_NORMAL, ECG_ABNORMAL, ECG_NOT_APPLICABLE,
CHEST_XRAY_NORMAL, CHEST_XRAY_ABNORMAL, CHEST_XRAY_NA, DATE_OF_EXAM, INVESTI_SIGNATURE,
INVESTI_DATE_SIGNED, COMMENT_OTH, FORM_REV_DATE, PHYS_EXAM_ABNORMAL, DUMMY)
AS
select
/*+ INDEX(R RESPONSE_RDCM_NFK_IDX) */
'<STUDY_NAME>' STUDY, DCMS.NAME DCMNAME, DCMS.SUBSET_NAME DCMSUBNM, DCMS.DCM_SUBSET_SN
SUBSETSN, RDCM.DOCUMENT_NUMBER DOCNUM, RDCM.SITE INVSITE,
RDCM.INVESTIGATOR INV, RDCM.PATIENT PT, RDCM.ACCESSIBLE_TS ACCESSTS,
RDCM.LOG_IN_TS LOGINTS, RDCM.LAST_DATA_CHANGE_TS LSTCHGTS,
RDCM.DATA_LOCK_FLAG LOCKFLAG, RDCM.CLIN_PLAN_EVE_NAME CPEVENT,
RDCM.DCM_DATE DCMDATE, RDCM.DCM_TIME DCMTIME,
R.REPEAT_SN REPEATSN, to_number(RDCM.VISIT_NUMBER||'.'|| ltrim(to_char(RDCM.SUBEVENT_NUMBER,'09')))
ACTEVENT,
RDCM.SUBEVENT_NUMBER SUBEVENT_NUMBER, RDCM.VISIT_NUMBER VISIT_NUMBER,
RDCM.QUALIFYING_VALUE QUALIFYING_VALUE, DCMS.QUAL_QUESTION_ID QUALIFYING_QUESTION,
RDCM.LAB LAB, RDCM.LAB_RANGE_SUBSET_NUM LABRANGE_SUBSET_NUMBER,
RDCM.LAB_ASSIGNMENT_TYPE_CODE LAB_ASSIGNMENT_TYPE_CODE, RDCM.LAB_ID LAB_ID,
max(decode(r.dcm_question_id,10511,SUBSTR(R.VALUE_TEXT,1,2),NULL)) PREGNANT_NURSING,
max(decode(r.dcm_question_id,10611,SUBSTR(R.VALUE_TEXT,1,2),NULL)) FIBROCYSTIC_DISEASE,
max(decode(r.dcm_question_id,10711,SUBSTR(R.VALUE_TEXT,1,2),NULL)) CARCINOMA_NO_MASTE,
max(decode(r.dcm_question_id,10811,SUBSTR(R.VALUE_TEXT,1,2),NULL)) ABSCESS_INFECTION,
max(decode(r.dcm_question_id,10911,SUBSTR(R.VALUE_TEXT,1,2),NULL)) DISEASE_IMPA_W_HEAL,
max(decode(r.dcm_question_id,11011,SUBSTR(R.VALUE_TEXT,1,2),NULL)) TISSUE_INCOMPATIBLE,
max(decode(r.dcm_question_id,11111,SUBSTR(R.VALUE_TEXT,1,2),NULL)) TREATMENT_SURG_RISK,
max(decode(r.dcm_question_id,11211,SUBSTR(R.VALUE_TEXT,1,2),NULL)) PSYCHO_INCOMPATIBLE,
max(decode(r.dcm_question_id,11311,SUBSTR(R.VALUE_TEXT,1,2),NULL)) WISHES_AUG_NOT_ELIG,
max(decode(r.dcm_question_id,11411,SUBSTR(R.VALUE_TEXT,1,2),NULL)) NOT_WILLING_REVISION,
max(decode(r.dcm_question_id,11511,SUBSTR(R.VALUE_TEXT,1,2),NULL)) LUPUS_SCLERODERMA,
max(decode(r.dcm_question_id,11611,SUBSTR(R.VALUE_TEXT,1,2),NULL)) REPLACE_COSMETIC,
max(decode(r.dcm_question_id,15811,SUBSTR(R.VALUE_TEXT,1,8),NULL)) DATE_OF_BIRTH,
max(decode(r.dcm_question_id,11711,SUBSTR(R.VALUE_TEXT,1,2),NULL)) FEM_UND18_PARENTIC,
max(decode(r.dcm_question_id,11811,SUBSTR(R.VALUE_TEXT,1,2),NULL)) COND_SITUATIONS,
max(decode(r.dcm_question_id,11911,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_POST_MASTECT,
max(decode(r.dcm_question_id,12011,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_POST_MASTECT,
max(decode(r.dcm_question_id,12111,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_POST_TRAUMA,
max(decode(r.dcm_question_id,12211,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_POST_TRAUMA,
max(decode(r.dcm_question_id,12311,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_SEVERE_PTOSIS,
max(decode(r.dcm_question_id,12411,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_SEVERE_PTOSIS,
max(decode(r.dcm_question_id,12511,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_PECTUS_EXCAVATUM,
max(decode(r.dcm_question_id,12611,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_PECTUS_EXCAVATUM,
max(decode(r.dcm_question_id,12711,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_PECTUS_CARINATUM,
max(decode(r.dcm_question_id,12811,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_PECTUS_CARINATUM,
max(decode(r.dcm_question_id,12911,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_THOR_HYPOPLASIA,
max(decode(r.dcm_question_id,13011,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_THOR_HYPOPLASIA,
max(decode(r.dcm_question_id,13111,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_SCOLIOSIS,
max(decode(r.dcm_question_id,13211,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_SCOLIOSIS,
max(decode(r.dcm_question_id,13311,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_RIB_DEFORMITIES,
max(decode(r.dcm_question_id,13411,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_RIB_DEFORMITIES,
max(decode(r.dcm_question_id,13511,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_TUBEROUS_BREAST,
max(decode(r.dcm_question_id,13611,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_TUBEROUS_BREAST,
max(decode(r.dcm_question_id,13711,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_CONG_BRE_ABSENCE,
max(decode(r.dcm_question_id,13811,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_CONG_BRE_ABSENCE,
max(decode(r.dcm_question_id,13911,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_CONTRA_MAMMA,
max(decode(r.dcm_question_id,14011,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_CONTRA_MAMMA,

Page 2 of 5
max(decode(r.dcm_question_id,14111,SUBSTR(R.VALUE_TEXT,1,2),NULL)) R_REVISION_IMPLANT,
max(decode(r.dcm_question_id,14211,SUBSTR(R.VALUE_TEXT,1,2),NULL)) L_REVISION_IMPLANT,
max(decode(r.dcm_question_id,14311,SUBSTR(R.VALUE_TEXT,1,2),NULL)) ADEQUATE_TISSUE,
max(decode(r.dcm_question_id,14411,SUBSTR(R.VALUE_TEXT,1,2),NULL)) SIGNED_INF_CONSENT,
max(decode(r.dcm_question_id,14511,SUBSTR(R.VALUE_TEXT,1,2),NULL)) SALINE_INAPPR_CHOICE,
max(decode(r.dcm_question_id,14611,to_numbeR(SUBSTR(R.VALUE_TEXT,1,2)),NULL)) HEIGHT,
max(decode(r.dcm_question_id,14711,to_numbeR(SUBSTR(R.VALUE_TEXT,1,3)),NULL)) WEIGHT,
max(decode(r.dcm_question_id,14811,SUBSTR(R.VALUE_TEXT,1,2),NULL)) PHYS_EXAM_NORMAL,
max(decode(r.dcm_question_id,15011,SUBSTR(R.VALUE_TEXT,1,100),NULL)) PHYS_EXAM_SPEC,
max(decode(r.dcm_question_id,15111,SUBSTR(R.VALUE_TEXT,1,2),NULL)) ECG_NORMAL,
max(decode(r.dcm_question_id,15211,SUBSTR(R.VALUE_TEXT,1,2),NULL)) ECG_ABNORMAL,
max(decode(r.dcm_question_id,15311,SUBSTR(R.VALUE_TEXT,1,2),NULL)) ECG_NOT_APPLICABLE,
max(decode(r.dcm_question_id,15411,SUBSTR(R.VALUE_TEXT,1,2),NULL)) CHEST_XRAY_NORMAL,
max(decode(r.dcm_question_id,15511,SUBSTR(R.VALUE_TEXT,1,2),NULL)) CHEST_XRAY_ABNORMAL,
max(decode(r.dcm_question_id,15611,SUBSTR(R.VALUE_TEXT,1,2),NULL)) CHEST_XRAY_NA,
max(decode(r.dcm_question_id,15711,SUBSTR(R.VALUE_TEXT,1,8),NULL)) DATE_OF_EXAM,
max(decode(r.dcm_question_id,15911,SUBSTR(R.VALUE_TEXT,1,2),NULL)) INVESTI_SIGNATURE,
max(decode(r.dcm_question_id,16011,SUBSTR(R.VALUE_TEXT,1,8),NULL)) INVESTI_DATE_SIGNED,
max(decode(r.dcm_question_id,73111,SUBSTR(R.VALUE_TEXT,1,200),NULL)) COMMENT_OTH,
max(decode(r.dcm_question_id,684811,SUBSTR(R.VALUE_TEXT,1,8),NULL)) FORM_REV_DATE,
max(decode(r.dcm_question_id,14911,SUBSTR(R.VALUE_TEXT,1,2),NULL)) PHYS_EXAM_ABNORMAL,
max(decode(r.dcm_question_id,1216911,SUBSTR(R.VALUE_TEXT,1,10),NULL)) DUMMY from
rdcms_view rdcm, dcms, responses_view r
where rdcm.clinical_study_id = 2011 and r.received_dcm_id =rdcm.received_dcm_id and
r.response_entry_ts <= rdcm.as_of_ts and r.end_ts > rdcm.as_of_ts and dcms.dcm_subset_sn = rdcm.dcm_subset_sn and
dcms.dcm_layout_sn = 1 and (dcms.dcm_id = 611 and rdcm.dcm_id = 611 and r.dcm_question_group_id in (2311, 2411 , 2511 ,
2611 , 9311))
group by '<STUDY_NAME>', DCMS.NAME, DCMS.SUBSET_NAME, DCMS.DCM_SUBSET_SN, RDCM.DOCUMENT_NUMBER,
RDCM.SITE, RDCM.INVESTIGATOR, RDCM.PATIENT, RDCM.ACCESSIBLE_TS, RDCM.LOG_IN_TS,
RDCM.LAST_DATA_CHANGE_TS, RDCM.DATA_LOCK_FLAG, RDCM.CLIN_PLAN_EVE_NAME,
RDCM.DCM_DATE, RDCM.DCM_TIME, R.REPEAT_SN,
to_number(RDCM.VISIT_NUMBER||'.'|| ltrim(to_char(RDCM.SUBEVENT_NUMBER,'09'))),
RDCM.SUBEVENT_NUMBER, RDCM.VISIT_NUMBER, RDCM.QUALIFYING_VALUE, DCMS.QUAL_QUESTION_ID, RDCM.LAB,
RDCM.LAB_RANGE_SUBSET_NUM, RDCM.LAB_ASSIGNMENT_TYPE_CODE, RDCM.LAB_ID

The only purpose of showing the above script is to indicate the level of complexity involved.

Using Oracle ODBC drivers, use the sas code to download the data from Oracle Clinical database stable views (SV) to
SAS datasets. eg.
%LET prompt = "dsn=ocdsn;uid=ops$user1;pwd=xyz";
Proc sql;
Connect to odbc(&prompt);
Create table Informed_Consent as select * from connection to odbc
(select * from studyname$stable.form_IC);
Disconnect from odbc;
Quit;

PERFORMANCE ISSUES:
For small studies meaning less number of subjects (like 1000 subjects) with 10 CRFs per subject, the above method is
useful to download data on a daily basis or on-demand. But for large studies (like 100,000 subjects) with 10 CRFs, the
download takes much longer time. The CPU usage will be maximum on the Oracle Server as well as on the SAS
machine. Because the stable view is a complicated view with multiple joins to the responses table, the Oracle server CPU
usage will be high during download. Similarly, as the download from the view takes a much longer time, the SAS machine
will also be running for a much longer time with full CPU usage as the connection is open between the two machines.

Enhanced Method:
Oracle database has a special object called Materialized Views (MV). This is used to migrate/mirror the data from stable
views to MVs. Since this is a built-in object, it takes much less time and less CPU usage for the migration to take place.
Once, when the data is migrated, it is readily available for SAS to download.

Proc sql;
Connect to odbc(&prompt);

Page 3 of 5
Create table study1.Informed_Consent as select * from connection to odbc
(select * from user_schema$MV_study_name_Informed_Consent);
Disconnect from odbc;
Quit;

Performance Metrix:

Usual Method Enhanced Method


To download 100,000 subjects 5 minutes and 30 seconds 19 seconds
informed consent forms from
Oracle Clinical to SAS with 30
variables.

Scripts that are used to generate the MV and MV groups are shown below.
/* create seperate MVs for each CRF */
create materialized view mv_<study>_fm1 parallel (degree 1) build immediate refresh
force with rowid as select * from <study_name>$STABLE.fm1 ;
create materialized view mv_<study>_fm2 parallel (degree 1) build immediate refresh
force with rowid as select * from <study_name>$STABLE.fm2 ;
create materialized view mv_<study>_fm3 parallel (degree 1) build immediate refresh
force with rowid as select * from <study_name>$STABLE.fm3 ;
create materialized view mv_<study>_fm4 parallel (degree 1) build immediate refresh
force with rowid as select * from <study_name>$STABLE.fm4 ;

/* Create a MV group and add this study MVs to the group. This sample script starts at
7:30 PM - database server time

Caution: Using sysdate+1 does the same job. However, if the Database server is rebooted,
then the new schedule starts at the server reboot time.
execute dbms_refresh.make('<study>_grp', 'mv_<study>_fm1' , sysdate + 19.5/24, sysdate +
1) ;
Using 'trunc(sysdate + 1)+19.5/24' eliminates this hazzle.
*/

execute dbms_refresh.make('<study>_grp', 'mv_<study>_fm1' , round(sysdate) + 19.5/24,


'trunc(sysdate + 1)+19.5/24') ;
execute dbms_refresh.add ('<study>_grp', 'mv_<study>_fm2') ;
execute dbms_refresh.add ('<study>_grp', 'mv_<study>_fm3') ;
execute dbms_refresh.add ('<study>_grp', 'mv_<study>_fm4') ;

/* Create a simple view so that it can be used in the download script */


create or replace view <study_name>_stable_fm1 as select * from mv_<study>_fm1;
create or replace view <study_name>_stable_fm2 as select * from mv_<study>_fm2;
create or replace view <study_name>_stable_fm3 as select * from mv_<study>_fm3;
create or replace view <study_name>_stable_fm4 as select * from mv_<study>_fm4;

/* Provide the required privilege to the users */


grant select on <study_name>_stable_fm1 to public ;
grant select on <study_name>_stable_fm2 to public ;
grant select on <study_name>_stable_fm3 to public ;
grant select on <study_name>_stable_fm4 to public ;

Other useful tips:


To drop a MV group:
execute dbms_refresh.destroy(name => '<STUDY>_GRP');

To refresh a MV group as an adhoc request:


execute dbms_refresh.refresh(name => '<STUDY>_OTH');

To refresh one MV:


execute dbms_mview.refresh ('MV_<STUDY>_<FM>', 'C');

To look at all the user created MVs:

Page 4 of 5
select * from user_snapshots;

To look at all the MV groups:


select * from user_refresh;

To drop an already existing MV:


drop materialized view <MV name>;

To look at all the scheduled jobs so as to find out if the MV jobs ran successfully for the previous night:
select * from dba_jobs;

Other Uses of this technique:


• Downloading data from Oracle Clinical to Oracle ERP or to any other legacy systems is one easy process.
• To get any adhoc reports out of OC studies any time of the day.

CONCLUSION
By utilizing the Materialized view features in Oracle Database, the performance of downloading the data from Oracle to SAS
becomes much simpler.

CONTACT INFORMATION
Your comments and questions are valued and encouraged. Contact the author at:

Ramkumar “Ramku” Krishnamurthy


Allergan Medical
71 S. Los Carneros Road,
Santa Barbara, CA 93117
Work Phone: 805.683.6761
E-mail: krish_ramku@allergan.com
ramku11@yahoo.com

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.

Other brand and product names are trademarks of their respective companies.

Page 5 of 5