/*-------------------------------------------------------------------\
| Creating a Table and Inserting Data into It;                       |
| 1) create a new table;                                             |
| 2) insert data into the table;                                     |
| INSERT INTO table-name|sas/access-view|proc-sql-view               |
|      <(column<,column>...)><,user-name>...;                        |
|  SET column=sql-expression <,column=sql-expression>...             |
|  ...>;          |
|                                                                    |
| INSERT INTO table-name|sas/access-view|proc-sql-view               |
|      <(column<,column>...)>                                        |
|  VALUES (value <, value>...)                                       |
|  ...)>...;                                 |
|                                                                    |
| INSERT INTO table-name|sas/access-view|proc-sql-view               |
|      <(column<,column>...)> query-expression;                      |
\-------------------------------------------------------------------*/
proc sql;
   create table proclib.paylist
       (IdNum char(4),
        Gender char(1),
        Jobcode char(3),
        Salary num,
        Birth num informat=date7.
                  format=date7.,
        Hired num informat=date7.
                  format=date7.);
 
  insert into proclib.paylist
    values('1639','F','TA1',42260,'26JUN70'd,'28JAN91'd)
    values('1065','M','ME3',38090,'26JAN54'd,'07JAN92'd)
    values('1400','M','ME1',29769.'05NOV67'd,'16OCT90'd)
 
    values('1561','M',null,36514,'30NOV63'd,'07OCT87'd)
    values('1221','F','FA3',.,'22SEP63'd,'04OCT94'd);
 
   title 'PROCLIB.PAYLIST Table';
   select *
   from proclib.paylist;
quit;

/*
Purpose: This example creates a new table, PROCLIB.NEWPAY, 
by concatenating two other tables: PROCLIB.PAYLIST and 
PROCLIB.PAYLIST2; then delete the rows of which some condition
is met; 
1) DELETE statement: delete rows;
2) IS condition;
3) RESET statement option DOUBLE: add new options, double 
            space the report, check sql options for details;
4) UNION set operator;
*/
proc sql;
   create table proclib.newpay as
   select * from proclib.paylist
   union
   select * from proclib.paylist2;
 
   delete
   from proclib.newpay
   where jobcode is missing or salary is missing;
 
   reset double;
   title 'Personnel Data';
   select *
   from proclib.newpay;
quit;

/*-------------------------------------------------------------------\
| Restriction: the engine must support UPDATE processing.            |
| 1) Drop:                                                           |
| DROP TABLE table-name <,table-name>...;                            |
| DROP VIEW view-name <,view-name>...;                               |
| DROP INDEX index-name <,index-name>...                             |
| FROM table-name;                                                   |
| 2) Update:                                                         |
| UPDATE table-name|sas/access-view|proc-sql-view          |
|    SET column=sql-expression <,column=sql-expression>...           |
|    ...>          |
|  ;                                            |
| 3) Case:                                                           |
|   CASE                                               |
|            WHEN when-condition THEN result-expression              |
|            ...         |
|                                            |
|            END                                                     |
| 4) Add Columns or delete Columns:                                  |
|    ALTER TABLE table-name                                          |
|     <, constraint-clause>...>;                  |
|      ...>               |
|      ...>            |
|      ...>;                                   |
|      where each constraint-clause is one of the following:         |
|      ADD  constraint                   |
\-------------------------------------------------------------------*/
/* Example 1 */
proc sql;
   title 'Employees Table';
   select * from Employees;
 
   update employees
      set salary=salary*
      case when jobcode like '__1' then 1.04
           else 1.025
      end;
 
   alter table employees
      modify salary num format=dollar8. /* Change a Column */
      drop phone            /* Delete a Column */
      add keyterm char(10); /* ADd a column */

   update totpt
      set keyterm='Total';

     title 'Updated Employees Table';
   select * from employees;
quit; 

/* Example 2 */
proc sql;
   create table totpt as
   select count(*) as numevt, count(distinct pt) as numpt
   from ae
   where keyterm is not missing;

   alter table totpt
      add keyterm char(10);

   update totpt
      set keyterm='TOTAL';
   
   select distinct a.keyterm, 
          count(*) as numevt label="Number of Events",
          count(distinct pt) as numpt label="Number of Patients", 
          count(distinct pt)*100/t.numpt format=pctfmt. label="Percentage of Patients"
   from ae as a, totpt as t
   where a.keyterm is not missing
   group by a.keyterm
   outer union corresponding
   select * from totpt;
quit;

/*-------------------------------------------------------------------\
| 1) SQL: Retrieve data from database efficiently;                   |
|    Including Oracle Clinical and Device Database:                  |
|    Note: For Oracle Clinical Database, you will have to specify the|
|          schema for 3 or more than 3 levels tables;                |
|    Create Keys and index for a dataset;                            |
|    This is the most efficient way of all;                          |
| 2) Get aroung invalid date problem:                                |
|    if only year is provided, use the Middle of the year ;          |
|    if only year and month is provided, use the Middle of the month;|
|    if the date is missing, do nothing;                             |
|    else entire date is provided;                                   |
| 3) "dquote" option is used to support special column or tables that|
|    are not nornally supported;                                     |
\-------------------------------------------------------------------*/
/* Oracle Clinical */
libname OCdbms odbc dsn="pocl" user="OPS$XIAA1" password="XIAA1" access=readonly 
               connection=GLOBALREAD schema="ACEDAF$CURRENT";
proc sql dquote=ansi;
   select case 
             when missing(condt) then .
             when length(condt) < 4 and not missing(condt) then .
             when length(condt) >= 4 and length(condt) < 6 then (INPUT((SUBSTR(condt,1,4)||'1231'), YYMMDD8.)+INPUT((SUBSTR(condt,1,4)||'0101'), YYMMDD8.)+1)/2
             when length(condt) >= 6 and length(condt) < 8 then (INPUT((SUBSTR(condt,1,6)||'01'), YYMMDD8.) + 15)
             else INPUT(SUBSTR(condt,1,8), YYMMDD8.)
             end as condt format date9.,
          condt as oricondt label="Original CONDT"
   from patdvn;
quit;

/* Device Database */
libname DVdbms odbc dsn='device' user=dr password=dr access=readonly connection=GLOBALREAD;
proc sql dquote=ansi;
   Create table ACEDdev.EPISODE as Select
   COMP_SERNUM as serialno label="serialno",
   EPIS_TYPE_CODE as epitype label="Episode Type",
   EPIS_ID as epiid label="Episode ID",
   EPIS_DETECT_DATE as epidt label="Episode Date"
   from DVdbms.EPISODE(dbkey=comp_sernum)
   where COMP_SERNUM /*in &acedallserial*/
   order by comp_sernum;

   create view DVviews.epiview as
   select *
      from DVdbms2.episode as epi 
      using libname DVdbms2 odbc
         dsn=device user=dr password=dr access=readonly;
quit;

/* Create keys for a Data set */
data mydata; 
   set keyvalues;
   set mydblib.employees(dbkey=(empid jobcode)) 
      key=dbkey;
run;

/* Join two tables from different ODBC data sources                 */
/*************************************************************************/
/* Join two tables via SAS/ACCESS to ODBC.  In this example the two      */
/* tables are not part of the same ODBC datasource, but the use of SQL   */
/* aliases allows them to be joined in the same PROC SQL step            */
/*************************************************************************/
proc sql;
connect to odbc as db_1 (dsn=db1 uid=joe pwd=schmo);
   connect to odbc as db_2 (dsn=db2 uid=joe pwd=schmo);

   create table db1.table3 as
      select * from connection to db_1
      (select * from table1) as table1,
       connection to db_2
      (select * from table2) as table2
      where table1.id = table2.id;
   disconnect from db_1;
disconnect from db_2;
quit;

/*-------------------------------------------------------------------\
| Keyword: Database time                                             |
| 1) Retrieve the Database systime from the remote server;           | 
| 2) Suppress the NOTES in the log window for the all following SAS  |
|    statements;                                                     |
\-------------------------------------------------------------------*/
OPTIONS NONOTES;
proc sql;
  connect to odbc (dsn="pocl" user="OPS$XIAA1" pwd="XIAA1");

  create table systime as select * from connection to odbc
    ( select 
        SYSDATE database_datetime,
        to_char(SYSDATE, 'DD/MM/YYYY') database_date,
        to_char(SYSDATE, 'HH24:MI:SS') database_time
      from 
        DUAL
    );
  disconnect from odbc;
quit;

Data _null_;
   format database_datetime datetime.;
   SET SYSTIME;
   CALL SYMPUT("dbtime", trim(left(put(database_datetime,datetime21.))));
run;
%put Database time is &dbtime..;

/*-------------------------------------------------------------------\
|  Usage of                                                          |
|  1) Proc sql;                                                      |
|  2) case, exists, in, having, group by                             |
|  3) subquery, outer join, union                                    |
|  4) concatenate the variables into a long macro variables          |
|  5) Keyword:                                                       |
|         OUTER UNION - concatenates the query results;              |
|                   a) stack the two data;                           |
|                   b) put the variables side by side even if they   |
|                   have the same variable names;                    |
|         UNION - produces all unique rows from both queries;        |
|         EXCEPT - rows that are part of the first query only;       |
|         INTERSECT - rows that are common to both query results;    |
|     Options:                                                       |
|         CORRESPONDING/CORR: option for OUTER UNION;                |
|         ALL: option for UNION;                                     |
|       CORR: a) stack 2 data sets column by column correspondingly, |
|             if they have the same variable names;                  |
|             b) create missing values for variable names that exist |
|             in only one of the 2 data set;                         |
|       ALL: keep duplicate rows;                                    |
\-------------------------------------------------------------------*/
 proc sql; /* Case */
   select *, case
                when degrees > 80 then 'Hot'
                when degrees < 40 then 'Cold'
                else 'Mild'
                end
   from temperatures;

   select *, case Degrees
                when > 80 then 'Hot'
                when < 40 then 'Cold'
                else 'Mild'
                end
   from temperatures;
quit;


proc sql outobs=10; /* select number of observations */
   title 'All Flights for Each Supervisor';
   select *
        from (select lname, s.idnum, city, jobcat, flight, date
               from proclib.schedule2 s, proclib.staff2 t, proclib.superv2 v
               where s.idnum=t.idnum and t.idnum=v.supid) 
   as three (Surname, Emp_ID, Hometown, Jobtype, FlightNumber, FlightDate);
quit;


proc sql; 
   select *
      from proclib.payroll p
      where exists (select *                 /* Exists */
                    from staff s
                    where p.idnum=s.idnum
                    and state='CT');

   /* Same as Previous */
   select *
      from proclib.payroll
      where idnum in (select idnum           /* In */
                      from staff
                      where state='CT');
quit;

/* Subquery */
proc sql;
   select * from proclib.payroll
   where salary> all(select salary
                     from proclib.payroll
                     where jobcode='ME3');
   /* Same as previous */
   select * from proclib.payroll
   where salary> (select max(salary)
                  from proclib.payroll
                  where jobcode='ME3');
quit;

/* Outer Join */
proc sql;
   title 'Full Outer Join';
   select *
      from lefttab as l full join righttab as r
      on l.continent=r.continent;
quit;

/* Union */
proc sql;
   create table proclib.newpay as
      select * from proclib.paylist
      union
      select * from proclib.paylist2;
 
     delete
      from proclib.newpay
      where jobcode is missing or salary is missing;
 
     reset double;
      title 'Personnel Data';
      select *
         from proclib.newpay;
quit;

/* select records with multiple repeat observations */
proc sql ;
   select *
   from test
   group by user_id
   having count(distinct session) gt 1
   order by user_id, session, sequence, pagename;
quit;