I don't really have any advanced sql knowledge on how to phrase if/then statements in sql (whether to use case, for example) and/or formatting variables in the same query so I was wondering if someone could help with this sas code and phrasing it in the correct format into sql:
data convert_code;
format date1 date 2 mmddyy8. code $4.;
set userid.code; (this table is pulled from oracle)
if ID='X' then P='A'; else P='B';
If CAT in ('1','2','3') then CAT_group='ONE'; else CAT_GROUP='TWO';
if CAT_2 > '0' and CAT_2A >='1' then d=1; else d=0;
date1=datepart(date1);
date2=datepart(date2);
if code='3' and type_code in ('A','B','C') THEN DO;
if P_CODE in ('1','2','3') then P='1';
if P_CODE in ('4','5','6') then P='2';
end;
if code='4' and e_code in ('A') then DO;
if B_CODE in ('11','12','13') then P='3';
if B_CODE in ('14','15','16') then P='4';
end;
run;
The example given here uses SAS' proc sql language. If you are using a different SQL implementation then the syntax may be different. However, the case expression examples should be fairly easy to adapt to any SQL implementation as it is part of the SQL standard.
proc sql;
/* Name your output table */
create table convert_code as
select
/*
Unlike the data step variables from the input data are not
selected by default, you can request them individually or
with "select *"
*/
/* Use "format =" after the column definition to set its format */
code format = $4.,
/* Use SAS functions as normal, name the output variable with "as" */
datepart(date1) as date1 format = mmddyy8.,
/* Comma separate each variable you want in your output */
datepart(date2) as date2 format = mmddyy8.,
/* A "case" expression can conditionally set a variable to a value */
case
when CAT in ('1', '2', '3') then 'ONE'
else 'TWO'
/* Close the "case" statement with "end" */
end as CAT_group,
/* You can nest case statements to emulate your "then do" */
case
when code = '3' and e_code in ('A', 'B', 'C') then
case
/* Use multiple "when then"s to emulate "else if" */
when P_CODE in ('1', '2', '3') then '1'
when P_CODE in ('4', '5', '6') then '2'
else ''
end
when code = '4' and e_code in ('A') then
case
when P_CODE in ('11', '12', '13') then '3'
when P_CODE in ('14', '15', '16') then '4'
else ''
end
when ID = 'X' then 'A'
else 'B'
end as P,
/* An alternative to case is to use the "ifn()" or "ifc()" functions */
ifn(CAT_2 > 0 and CAT_2A >= 1, 1, 2) as d
/* Choose the input data and end the query with a ";" */
from userid.code;
/*
Additional processing can be done here, some examples include:
"where": limit the input
"left join", "right join", "inner join", "outer join", ",":
combine with additional data sets
"group by": group based on column values for summary functions
"order by": specify which columns to sort the output by
*/
/* End the "proc sql" processing */
quit;
Using case expressions rather than the the SAS specific ifn() and ifc() functions would be my suggestion, as they are the standard SQL method for conditionally assigning values and will (probably) be the same in other SQL implementations. Be aware that format = and the datepart() function are SAS specific.
A brief exploration of proc sql from a data step perspective can be found here
The case expression here
The ifn() and ifc() functions here
The format = column modifier here
Related
DECLARE #AreaType NVARCHAR(250);
SET #AreaType = 'Test Area 1'; ---Test Area 2, Test Area 3, XYX
SELECT [Area_Category_Id] AreaCategoryId
,[Area_Category] AreaCategoryName
,[Is_Active] IsActive
FROM [dbo].[LK_AreaCategories]
--WHERE [Area_Category_Id] IN(1,2,3,4) ------How do i put this in a case statement
WHERE [Area_Category_Id] IN (CASE
WHEN #AreaType = 'Test Area 1'
THEN 1
WHEN #AreaType = 'XYX'
THEN 3
ELSE -1
END)
I have the above sample T-SQL code and in the IN operator of the where clause I want to use multiple values like IN (1,2,3,4) but also I want to use the CASE operator such that I can pass different values to the IN operator depending on the value of #AreaType parameter, how can I modify the code above to achieve that?
You don't use a CASE expression for that (precisely because its an expression not a statement), you use regular AND/OR logic e.g.
WHERE (#AreaType = 'Test Area 1' AND Area_Category_Id in (1,2,3))
OR (#AreaType = 'Test Area 2' AND Area_Category_Id in (4,5,6))
OR (#AreaType = 'Test Area 3' AND Area_Category_Id in (7,8,9))
I have the following SQL statement. In it I need to convert some numbers stored as varchar to decimal in order to sum them.
When I run this SQL against my constraints I get this message:
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value '1635.34' to data
type int.
Which baffles me because I am casting my data as decimal. It also baffles me because when I use different constraints that have the same type of data in the field (1234.56 type of format) it works. That data is in the TotalPremium field.
(My logic is a bit complex so that is why SQL statement is complex. I am posting all of it for clarity sake. Also, redesigning database table field type is not an option at this point.)
SELECT Account_No, version_num, LineOfBus, ProductNo, QuoteNo, Sum(Cast(TotalPremium as Decimal(16,2))) TotalPremium
FROM
(SELECT t.Account_No, t.version_num,
CASE
WHEN t.PackageIndicator = '1' THEN 'Package' Else t.Lob
END AS LineOfBus,
t.ProductNo, t.QuoteNo, Cast(COALESCE(t.TotalPremium,0) as decimal(16,2)) TotalPremium
FROM uAccountProductInfo as T
WHERE t.version_num IN
(SELECT sqVersionNumber.version_num
FROM
/* this captures unique package product records (or just stand alone records as well) */
(SELECT DISTINCT sqUnique.version_num, Count(sqUnique.version_num) VersionCount
FROM
/* grab list of all uniquer version, product, quote combinations (use distinct to combine package */
(SELECT DISTINCT version_num, productNo, quoteNo
FROM uAccountProductInfo
WHERE Account_No = '1172014' /* pass as parameter */
AND ProductNo IN ('6472930', '6474927') /* pass as parameter */
AND QuoteNo IN ('724185-01', '881957-08') /* pass as parameter */
) AS sqUnique
GROUP BY version_num
HAVING Count(version_num) = 2 /* pass as variable based on number of products, quotes */
) as sqVersionNumber
)
AND t.Account_no = '1172014' /* pass as parameter */
AND t.ProductNo IN ('6472930', '6474927') /* pass as parameter */
AND t.QuoteNo IN ('724185-01', '881957-08') /* pass as parameter */) as sqLOB
GROUP BY Account_No, version_num, LineOfBus, ProductNo, QuoteNo
The problem is that SQL Server does not guarantee the order of evaluation of operations. You clearly have something improper in the field. In SQL Server 2012+, use try_convert():
SELECT Sum(try_convert(decimal(16, 2), TotalPremium ))) as TotalPremium
In earlier versions, use case:
SELECT Sum(case when isnumeric(TotalPremium) = 1 then convert(decimal(16, 2), TotalPremium)) end) as TotalPremium
isnumeric() is not perfect, but it should be good enough for your purposes.
Cast t.TotalPremium to decimal before coalescing. Your query is doing coalesce on a string and an integer, then casting the result to decimal. Try using0.0instead of0as well.
edit I do not actually think using 0.0 rather than 0 is a good idea, aside from readability. If that is the goal, cast it to the same decimal datatype. Otherwise, this could be construed as a datatype dominant over decimal. 0 as int or varchar should not take precedence over our value casted to a decimal.
You could use isnull() instead of coalesce(), though it would still be better practice to use the same datatype as RexMaison points out.
create table t (TotalPremium varchar(16));
insert into t values (''),(null),('1635.34');
/* no error */
select isnull(t.TotalPremium,0)
from t;
/* no error */
select coalesce(t.TotalPremium,'0')
from t;
/* error */
select coalesce(t.TotalPremium,0)
from t;
rextester demo: http://rextester.com/OHEJ71310
Just posting final code that worked after incorporating elements of all 3 answers.
SELECT Account_No, version_num, LineOfBus, ProductNo, QuoteNo,
SUM(CASE
WHEN ISNUMERIC(TotalPremium) = 1 THEN CONVERT(decimal(16,2),TotalPremium)
END) As TotalPremium
FROM
(SELECT t.Account_No, t.version_num,
CASE
WHEN ISNull(t.PackageIndicator,'0') = '1' THEN 'Package' Else t.Lob
END AS LineOfBus,
t.ProductNo, t.QuoteNo,
ISNull(CASE
WHEN ISNUMERIC(t.TotalPremium) = 1 THEN CONVERT(decimal(16,2),t.TotalPremium)
END, 0) TotalPremium
FROM uAccountProductInfo as T
WHERE t.version_num IN
(SELECT sqVersionNumber.version_num
FROM
/* this captures unique package product records (or just stand alone records as well) */
(SELECT DISTINCT sqUnique.version_num, Count(sqUnique.version_num) VersionCount
FROM
/* grab list of all uniquer version, product, quote combinations (use distinct to combine package */
(SELECT DISTINCT version_num, productNo, quoteNo
FROM uAccountProductInfo
WHERE Account_No = '1172014' /* pass as parameter */
AND ProductNo IN ('6472930', '6474927') /* pass as parameter */
AND QuoteNo IN ('724185-01', '881957-08') /* pass as parameter */
) AS sqUnique
GROUP BY version_num
HAVING Count(version_num) = 2 /* pass as variable based on number of products, quotes */
) as sqVersionNumber
)
AND t.Account_no = '1172014' /* pass as parameter */
AND t.ProductNo IN ('6472930', '6474927') /* pass as parameter */
AND t.QuoteNo IN ('724185-01', '881957-08') /* pass as parameter */) as sqLOB
GROUP BY Account_No, version_num, LineOfBus, ProductNo, QuoteNo
I'm having a problem executing this SQL statement. I am new to TSQL and I have no idea how to fix this. Everytime I execute this, I get the error:
An expression of non-boolean type specified in a context where a
condition is expected, near ')'.
Incorrect syntax near the keyword 'else'.
if
SELECT Num from users where SUBSTRING(CAST(Num AS VARCHAR(6)),1,2) = 14
print 'Batch 2014';
else
print 'Batch 2013';
What I'm trying to do here is to search in my table all users with '13' as the first 2 numbers in the column 'Num', and then print 'Batch 2014' else 'Batch 2013' Please help :) thank you
It's best to avoid using if-else with exists. Why?
You need to make sure at least one record exists in your table
The other benefit of EXISTS is that once it finds a single record that matches it stops processing. This doesn't have a huge impact if you're checking on a primary key. It does have a big impact if you're checking for existence based on another field
if exists (SELECT Num from users where SUBSTRING(CAST(Num AS VARCHAR(6)), 1, 2) = 14)
print 'Batch 2014';
else
print 'Batch 2013';`
Your if condition returns non-boolean value (other that 0 or 1),that's the reason for getting the error.
If you are using if .. exists it will print if any of the num in your table satisfies the condition SUBSTRING(CAST(Num AS VARCHAR(6)),1,2) = 14.
If you wanted to see the users with batch information ,use CASE statement.
SELECT userid,Name -- mention the columns you wanted to select
,CASE WHEN SUBSTRING(CAST(Num AS VARCHAR(6)),1,2) =14 THEN 'Batch 2014'
ELSE 'Batch 2013' END Batch
FROM users
What the type of Num?
syntax of IF-ELSE:
-- Syntax for SQL Server, Azure SQL Database, Azure SQL Data Warehouse, Parallel Data Warehouse
IF Boolean_expression
{ sql_statement | statement_block }
[ ELSE
{ sql_statement | statement_block } ]
So, "SELECT Num from users where SUBSTRING(CAST(Num AS VARCHAR(6)),1,2) = 14" should return a Boolean, True or False.
IF EXISTS (SELECT Num from users where SUBSTRING(CAST(Num AS VARCHAR(6)),1,2) = 14)
IF EXISTS ( SELECT Num
FROM users
WHERE SUBSTRING(CAST(Num AS VARCHAR(6)), 1, 2) = 14 )
PRINT 'Batch 2014' ;
ELSE
PRINT 'Batch 2013' ;
I'm looking to convert a date that is in the format of CYYMMDD (where C is either 0 for 20th century or 1 for 21st century) to a standard SAS date. This code will be placed inside of a SAS query using 'proc sql' so that it can compare a SAS date against a date stored in DB2.
Example: Input data=1130101, Output='1Jan2013'd
Examples I've tried are:
(substr(t1.'EffectDate'n,4,2)|| '/' || substr(t1.'EffectDate'n,6,2) || '/' || cast(substr(t1.'EffectDate'n,1,3) AS INTEGER) + 1900)
That fails to the cast() function (appears it doesn't exist?)
Also tried:
convert(varchar(10), convert(datetime, right(t1.'EffectDate'n, 6), 12), 101)
But varchar(10) doesn't exist.
My query looks like this:
proc sql;
create table CLAIMS as select
t1.CID,
t1.MID,
t1.DOS
OTHER_TABLE.ChangeDate AS EffectDate
FROM
SOURCE.REJECTED t1
INNER JOIN
EGTASK.OTHER_TABLE
ON
t1.DOS >= *Converted_Date*
[... goes on a couple more lines...]
Where *Converted_Date* is what I need.
(However, I should clarify that this particular query/join doesn't necessarily need to be SQL)
To convert your variable from it's current coded format into a proper SAS date variable, you will need to turn it into a character string and then read the result using the INPUT function. For example:
data _null_;
do EffectDate = 1130101,0130101;
cEffectDate = put(EffectDate,z7.);
if substr(cEffectDate,1,1) = '0'
then SASEffectDate = input('19' || substr(cEffectDate,2),yymmdd8.);
else SASEffectDate = input('20' || substr(cEffectDate,2),yymmdd8.);
put EffectDate=
/ SASEffectDate=
/ ;
end;
format SASEffectDate yymmdd10.;
run;
This is just an illustration and a bit long-winded; it creates a new SAS variable named SASEffectDate to preserve the original variable. Once you have it as a SAS variable, you don't need to do anything else; the SAS Access product will know how to make the references to the external database.
Here is an example of doing something similar using PROC SQL:
data have; /* Just a dummy data set for illustration */
do EffectDate = 1130101,0130101;
i+1;
output;
end;
run;
proc sql;
create table want as
select t2.*
, case when t2.EffectDate < 999999 /* starts with 0 */
then input('19' || substr(put(EffectDate,z7.),2),yymmdd8.)
else input('20' || substr(put(EffectDate,z7.),2),yymmdd8.)
end as SASEffectDate format=yymmdd10.
from have t2
;
quit;
I've never seen this before... I have a query that starts off like this:
with q1 as
(select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567')
select case
when ... /* rest of query omitted */
When I try to run this, Oracle complains about that a table or view does not exist. But it highlights the ',' on line 3, rather than an actual table/view name:
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_VAL,
*
ERROR at line 3:
ORA-00942: table or view does not exist
The rest of the query I omitted is rather long and complex - I'll sanitize and post it if necessary - for now I'll just say that this error only started when I added a third subquery that referenced q1. In fact, it seems I can remove any one of the 3 subqueries and the whole thing will execute (though with incorrect results) so it feels like I've hit some kind of Oracle error rather than a pure SQL error. It's also interesting that I can run the body of q1 as a stand-alone query and it has no problems when I do that. Only when I run the entire query does it complain about the comma after the case in q1.
Has anyone ever experienced this?
(using Oracle 10g).
Edit: Tried added AS keyword. Results are now:
case when a.perc_fault > 0 THEN 'Y' ELSE 'N' end AS L_VAL, a.C_VAL
*
ERROR at line 3:
ORA-00942: table or view does not exist
It looks like the asterisk is in the same position, but under the V because the word L_VAL has been shifted by 3 characters. Very strange...
Assuming you are hitting the Oracle bug(s) and can't patch the database, you could try moving the subquery to a function. Not entirely sure this will work, and assumes your PL/SQL version is in a package, or there's one available that can have a function added:
In the package spec:
type q1_rec is record(
d_id ab_a_table.v_id%TYPE,
v_id ab_a_table.d_id%TYPE,
c_id ab_a_table.c_id%TYPE,
l_val char(1),
c_val ab_a_table.c_val%TYPE);
type q1_arr is varray(9999); -- assuming you can pick a max size
function q1 return q1_arr pipelined;
pragma restrict_references(q1, wnds);
In the package body:
function q1 return q1_arr pipelined is
cursor c is
select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567');
begin
for r in c loop
pipe row(r);
end loop;
end;
And then in your main query replace the subquery with table(q1()).
Using a ref cursor or nested table might be a bit neater but would need a table type built outside the package, which I guess you want to avoid based on your extra-object comment about using a view.
I don't know for sure if I'm experience Oracle bug 5130732 but it sure feels like it. Anyway, I rewrote the query like this:
select case ...
from
(select ...
from (select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567') q1, <other tables>
where ...) subquery1,
(select ...
from (select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567') q1, <other tables>
where ...) subquery2,
(select ...
from (select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567') q1, <other tables>
where ...) subquery3, <other tables>
where....
Yes, I included a copy of q1 in every subquery that used it and everything works fine now. A real view would have worked too, but this was easier (politically, that is - no code-promotion requests to the environment where the analysis needs to be done, no meetings about late-added object in database, etc...)
UPDATE
And now that I've added the query to my PL/SQL script, Oracle gives me ORA-00600 [qcscpqbTxt], [600], which seems to be related to Oracle bug #5765958.... * sigh *... Can anyone suggest a workaround? I don't have metalink access (well, I might, through a DBA, if this can somehow get onto their radar).