-
Notifications
You must be signed in to change notification settings - Fork 75
/
Copy pathsql_datetime.sas
200 lines (149 loc) · 6.54 KB
/
sql_datetime.sas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*=====================================================================
Program Name : sql_datetime.sas
Purpose : Converts a SAS datetime literal to a
SQL Server datetime literal.
SAS Version : SAS 9.3
Input Data : SAS datetime literal (macro variable)
Output Data : SQL Server literal
Macros Called : None
Originally Written by : Scott Bass
Date : 18SEP2017
Program Version # : 1.0
=======================================================================
Scott Bass ([email protected])
This code is licensed under the Unlicense license.
For more information, please refer to http://unlicense.org/UNLICENSE.
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
=======================================================================
Modification History : Original version
=====================================================================*/
/*---------------------------------------------------------------------
Usage:
* this creates DATE7:TIME literal ;
%let dt=%sysfunc(datetime(),datetime16.);
%put %sql_datetime(&dt);
%put %squote(%sql_datetime(&dt));
=======================================================================
* this creates DATE9:TIME literal (length of 19-40 all work) ;
%let dt=%sysfunc(datetime(),datetime19.);
%put %sql_datetime(&dt);
%put %squote(%sql_datetime(&dt));
=======================================================================
* DATE9:TIME literal with milliseconds ;
%let dt=%sysfunc(datetime(),datetime24.3);
%put %sql_datetime(&dt);
%put %squote(%sql_datetime(&dt));
=======================================================================
* this creates ISO8601 (YYYY-MM-DDTHH:MM:SS) literal ;
%let dt=%sysfunc(datetime(),e8601dt.);
%put %sql_datetime(&dt);
%put %squote(%sql_datetime(&dt));
=======================================================================
* ISO8601 literal with milliseconds ;
%let dt=%sysfunc(datetime(),e8601dt24.3);
%put %sql_datetime(&dt);
%put %squote(%sql_datetime(&dt));
=======================================================================
* More extensive test ;
options sastrace=',,,d' sastraceloc=saslog nostsuffix;
options msglevel=I;
options mprint mrecall;
* Change the below to suit your environment ;
* libname foo odbc NOPROMPT="Driver={SQL Server Native Client 10.0};Server=YOURSERVER;Database=YOURDATABASE;Trusted_Connection=yes;";
%macro test(dt);
proc sql;
connect using foo;
select * from connection to foo (
DECLARE @dt DATETIME2(0);
SELECT @dt=%squote(%sql_datetime(&dt));
SELECT @dt as [DATETIME];
);
%mend;
%test(%sysfunc(datetime(),datetime16.));
%test(%sysfunc(datetime(),datetime19.));
%test(%sysfunc(datetime(),datetime24.3));
%test(%sysfunc(datetime(),e8601dt.));
%test(%sysfunc(datetime(),e8601dt24.3));
%test(%sysfunc(datetime(),best.)); * error checking, invalid input, this will fail ;
-----------------------------------------------------------------------
Notes:
SQL Server (as configured at our site) accepts strings such as these as
datetime literals:
25DEC18 20:12:34 (DATE7 + space + 24-hour time)
25DEC18 20:12:34.567 (DATE7 + space + 24-hour time with milliseconds)
25DEC2018 20:12:34 (DATE9 + space + 24-hour time)
25DEC2018 20:12:34.567 (DATE9 + space + 24-hour time with milliseconds)
2018-12-25T20:12:34 (ISO8601 time format (e8601dt. SAS format))
2018-12-25T20:12:34.567 (ISO8601 time format (e8601dt. SAS format) with milliseconds)
2018-12-25 20:12:34 (ISO8601 time format with space instead of "T"
2018-12-25 20:12:34.567 (ISO8601 time format with space instead of "T" with milliseconds)
Milliseconds is just an example; it could be other orders of magnitude as well.
This macro accepts either a DATETIME or E8601DT. ***STRING*** literal
as input.
It does not accept the numeric datetime value - convert it to a string
first using an appropriate format.
This macro returns a string appropriate as a SQL Server datetime
literal in one of these formats:
DDMMMYY HH:MM:SS[.###]
DDMMMYYYY HH:MM:SS[.###]
YYYY-MM-DDTHH:MM:SS[.###]
If you want a datetime ***LITERAL*** that "works" as a literal in
both SAS and SQL Server, use a SAS datetime literal, and pass that
literal "as-is" to this macro.
For example:
%let dt=25DEC2018:12:34:56;
* In SAS:
data test;
datetime="&dt"dt;
format datetime datetime.;
run;
* In SQL Server:
proc sql;
connect using foo;
execute (
insert into dbo.mytable (datetime) values (%squote(%sql_datetime(&dt)))
) by foo;
quit;
It would be nice if SAS also accepted an ISO8601 datetime string as a
datetime literal...but it doesn't.
---------------------------------------------------------------------*/
%macro sql_datetime
/*---------------------------------------------------------------------
Converts a SAS datetime literal to a SQL Server datetime literal.
---------------------------------------------------------------------*/
(SAS_DATETIME /* SAS datetime string in either DATETIME. or */
/* E8601DT. (ISO8601 datetime) format. */
);
%local macro parmerr rx1 rx2 rc1 rc2 sql_datetime;
%let macro = &sysmacroname;
%* check input parameters ;
%parmv(SAS_DATETIME, _req=1,_words=0,_case=U)
%if (&parmerr) %then %goto quit;
%* datetime format ;
%let rx1=%sysfunc(prxparse(/(\d{2})(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(\d{2,4}):(\d{2}):(\d{2}):(\d{2})/io));
%* e8601dt format ;
%let rx2=%sysfunc(prxparse(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/o));
%* is it datetime or e8601dt format? ;
%let rc1=%sysfunc(prxmatch(&rx1,&sas_datetime));
%let rc2=%sysfunc(prxmatch(&rx2,&sas_datetime));
%syscall prxfree(rx1);
%syscall prxfree(rx2);
%if (&rc1) %then %do;
%let sql_datetime=%sysfunc(prxchange(s/:/ /o,1,&sas_datetime)); %* convert first colon to space ;
%end;
%else
%if (&rc2) %then %do;
%let sql_datetime=&sas_datetime; %* return ISO8601 datetime string as is ;
%end;
%else %do;
%put %str(ERR)OR: Input string must be a valid datetime in DATETIME or E8601DT format.;
%let sql_datetime=*** Invalid Input String ***;
%end;
&sql_datetime
%quit:
%mend;
/******* END OF FILE *******/