T-SQL where clause case statement - tsql

Need some small help with some SQL. I am using a VARCHAR value to determine in a case which logic to use in WHERE clause but I am having some issues writing this case statement.
where
(CASE WHEN #p_flag = 'ATA'
THEN (#p_start_ata IS NULL AND #p_end_ata IS NULL) OR (vso.poa_ata between #p_start_ata and #p_end_ata)
ELSE (#p_start_atd IS NULL AND #p_end_atd IS NULL) OR (vso.pol_atd between #p_start_atd and #p_end_atd)
)
Line 93 Incorrect syntax near the keyword 'IS'.
Its probably a minor error but its frustrating me. Maybe there is a better way of writing this? Thanks!

Like said before you can not use a case statement like you are doing it. So this might be a suggestion:
WHERE
(
#p_flag = 'ATA'
AND
(
(#p_start_ata IS NULL AND #p_end_ata)
OR (vso.poa_ata between #p_start_ata and #p_end_ata)
)
)
OR
(
NOT #p_flag = 'ATA'
AND
(
(#p_start_atd IS NULL AND #p_end_atd IS NULL)
OR (vso.pol_atd between #p_start_atd and #p_end_atd)
)
)

You can't use the CASE like that!
It's use to get values and not restrictions. Check this to view how to properly use CASE.
In your case you've to change your logic probably using IF's and making the wanted SELECT's.

Related

Approaches to execute PostgreSQL's concat() instead of || in JOOQ?

The ||-operator and the concat(...)-function in PostgreSQL behave differently.
select 'ABC'||NULL||'def';
-- Result: NULL
select concat('ABC', NULL, 'def');
-- Result: 'ABCdef'
concat(...) ignores NULL values, but a NULL whithin a || expression makes the whole result become NULL.
In JOOQ, the DSL.concat() in the PostgreSQL dialect renders expressions using the ||-operator:
Java: dsl.select(
DSL.concat(
DSL.inline("ABC"),
DSL.inline(null, SQLDataType.VARCHAR),
DSL.inline("def"))
).execute();
SQL: select ('ABC' || null || 'def')
Result: NULL
I am looking for (elegant?) ways to invoke the concat(...)-function instead of the ||-operator via JOOQ in PostgreSQL:
Java: dsl.select(???).execute();
SQL: select concat('ABC', null, 'def')
Result: 'ABCdef'
I found two ways to achieve the posed objective.
Approach #1:
dsl.select(
field(
"concat({0})",
SQLDataType.VARCHAR,
list(
inline("ABC"),
inline(null, SQLDataType.VARCHAR),
inline("def")
)
)
).execute();
This has to the intended behavior, but necessitates the in my eyes ugly "concat({0})". A more elegant approach from my point of view is:
Approach #2:
dsl.select(
function(
"concat",
SQLDataType.VARCHAR,
inline("ABC"),
inline(null, SQLDataType.VARCHAR),
inline("def")
)
).execute();
This solution does not involve inline SQL with placeholders as approach #1. Why JOOQ generates || instead of concat(...) in the first place is still to be expounded, though.

Crystal formula case statement with like

I'm trying to convert sql case statement to crystal formula.
The select in sql had this in it:
...
,pf.Status_category AS Category
,CASE WHEN p.degree LIKE '%P%' AND
pf.department_name LIKE 'Occ%' THEN isnull(pf.department2, '') ELSE isnull(pf.department_name, '') END AS Department,
CASE WHEN p.degree LIKE '%P%' AND pf.department_name LIKE 'Occ%' THEN isnull(pf.Section2, '') ELSE isnull(pf.Section_name, '') END AS Section,
CASE WHEN p.degree LIKE '%P%' AND pf.department_name LIKE 'Occ%' THEN isnull(pf.department3, '') ELSE isnull(pf.department2, '') END AS [Department 2],
CASE WHEN p.degree LIKE '%P%' AND pf.department_name LIKE 'Occ%' THEN isnull(pf.Section3, '') ELSE isnull(pf.Section2, '') END AS [Section 2],
pdd.DepartmentName AS DP
,pdv.PrivilegeDetailText AS Privilages
...
FROM dbo.Person_PrivDtl_V AS pdv INNER JOIN
dbo.Person_Privs_Facs_V ON pdv.M_ID = dbo.Person_Privs_Facs_V.Person_Priv_M_ID INNER JOIN
dbo.PrivDefDepartments_PDF AS pdd ON pdd.PDDept_ID = pdv.PDDept_ID INNER JOIN
dbo.Person AS p ON pdv.Person_ID = p.Person_ID INNER JOIN
dbo.Person_Facilities AS pf ON pf.FacCode = dbo.Person_Privs_Facs_V.FacCode AND pdv.Person_ID = pf.Person_ID
When I convert this to crystal, I can't put this in the columns of the report design. So my idea is to use a formula for each column selected. I have this so far for the first formula, but it won't let me save it:
Formula name= Department:
select {Person_Facilities.Department_name}
case is like "Occ%" : {Person_Facilities.Department2}
The error seems to be the like. I looked up crystal like and it seems ok except for they use "is" for the accepted answer, but when I add "is" the error seems to be the "is" when I try to save it.
What is wrong with this formula so I can use like and the case?
Is there a better way to do this? I suppose I can use a view, but my boss doesn't want views cluttering the DB. Is using a formula the way to do this in crystal? The sql also handled null, which I'm not doing, but I'm not sure how to incorporate that at this point. I'm pretty new to crystal and my team doesn't like questions. We have Crystal Reports 2008 and SQL server 2008 R2.
Not sure if this is what you are looking for but try this;
select {Person_Facilities.Department_name}
case "Occ%" : {Person_Facilities.Department2}
default : 'Unknown';
The default is optional.
To use the LIKE operator, it is as follows;
If {Command_Main.Name} LIKE '*Manager*' then Do Something
To handle NULLS in CR, there is an option to do so in the formula editor. See below. Change it from Exception for Nulls to Default Values for Nulls.

SELECT with substring in ON clause?

I have the following select statement in ABAP:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
INTO corresponding fields of table GT_INSTMUNIC_F
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN EVER AS EV on
MUNIC~POD = EV~VREFER(9).
"where EV~BSTATUS = '14' or EV~BSTATUS = '32'.
My problem with the above statement is that does not recognize the substring/offset operation on the 'ON' clause. If i remove the '(9) then
it recognizes the field, otherwise it gives error:
Field ev~refer is unknown. It is neither in one of the specified tables
nor defined by a "DATA" statement. I have also tried doing something similar in the 'Where' clause, receiving a similar error:
LOOP AT gt_instmunic.
clear wa_gt_instmunic_f.
wa_gt_instmunic_f-mandt = gt_instmunic-mandt.
wa_gt_instmunic_f-bis = gt_instmunic-bis.
wa_gt_instmunic_f-ab = gt_instmunic-ab.
wa_gt_instmunic_f-zzelecdate = gt_instmunic-zzelecdate.
wa_gt_instmunic_f-ZZCERTDATE = gt_instmunic-ZZCERTDATE.
wa_gt_instmunic_f-CONSYEAR = gt_instmunic-CONSYEAR.
wa_gt_instmunic_f-ZDIMO = gt_instmunic-ZDIMO.
wa_gt_instmunic_f-ZZONE_M = gt_instmunic-ZZONE_M.
wa_gt_instmunic_f-ZZONE_T = gt_instmunic-ZZONE_T.
wa_gt_instmunic_f-USAGE_M = gt_instmunic-USAGE_M.
wa_gt_instmunic_f-USAGE_T = gt_instmunic-USAGE_T.
temp_pod = gt_instmunic-pod.
SELECT vrefer
FROM ever
INTO wa_gt_instmunic_f-vrefer
WHERE ( vrefer(9) LIKE temp_pod ). " PROBLEM WITH SUBSTRING
"AND ( BSTATUS = '14' OR BSTATUS = '32' ).
ENDSELECT.
WRITE: / sy-dbcnt.
WRITE: / 'wa is: ', wa_gt_instmunic_f.
WRITE: / 'wa-ever is: ', wa_gt_instmunic_f-vrefer.
APPEND wa_gt_instmunic_f TO gt_instmunic_f.
WRITE: / wa_gt_instmunic_f-vrefer.
ENDLOOP.
itab_size = lines( gt_instmunic_f ).
WRITE: / 'Internal table populated with', itab_size, ' lines'.
The basic task i want to implement is to modify a specific field on one table,
pulling values from another. They have a common field ( pod = vrefer(9) ). Thanks in advance for your time.
If you are on a late enough NetWeaver version, it works on 7.51, you can use the OpenSQL function LEFT or SUBSTRING. Your query would look something like:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN ever AS ev
ON MUNIC~POD EQ LEFT( EV~VREFER, 9 )
INTO corresponding fields of table GT_INSTMUNIC_F.
Note that the INTO clause needs to move to the end of the command as well.
field(9) is a subset operation that is processed by the ABAP environment and can not be translated into a database-level SQL statement (at least not at the moment, but I'd be surprised if it ever will be). Your best bet is either to select the datasets separately and merge them manually (if both are approximately equally large) or pre-select one and use a FAE/IN clause.
They have a common field ( pod = vrefer(9) )
This is a wrong assumption, because they both are not fields, but a field an other thing.
If you really need to do that task through SQL, I'll suggest you to check native SQL sentences like SUBSTRING and check if you can manage to use them within an EXEC_SQL or (better) the CL_SQL* classes.

Class::DBI, Class::DBI::AbstractSearch, and Oracle to_date

I'm trying to produce the following SQL using CDBI:
select * from mytable
where ref = "foo"
and to_date(received_date, 'DD-MM-YYYY') < to_date('01-04-2011', 'DD-MM-YYYY')
The closest syntax I can think of is:
mycdbi->search_where({
ref => 'foo',
received_date => { '<' => ["to_date(?, 'DD-MM-YYYY')", '01-04-2011'] }
});
However that's not converting the column mytable.received_date using Oracle function to_date.
So what's the correct way to do it?
Ps: please don't say use DBIC as the code needs to stay in CDBI.
thanks!
Might be easiest to just use retrieve_from_sql and do it by hand:
my #things = mycdbi->retrieve_from_sql(qq{
ref = 'foo'
AND to_date(received_date, 'DD-MM-YYYY') < to_date('01-04-2011', 'DD-MM-YYYY')
});
I usually end up pushing the ORMs out of the way for any instantiation more complicated than my $thing = Thing->retrieve(x), I already know SQL so I don't need to learn yet another API that tries to implement SQL and usually does so rather poorly.

SELECT..CASE - Refactor T-SQL

Can I refactor the below SQL CASE statements into single for each case ?
SELECT
CASE RDV.DOMAIN_CODE WHEN 'L' THEN CN.FAMILY_NAME ELSE NULL END AS [LEGAL_FAMILY_NAME],
CASE RDV.DOMAIN_CODE WHEN 'L' THEN CN.GIVEN_NAME ELSE NULL END AS [LEGAL_GIVEN_NAME],
CASE RDV.DOMAIN_CODE WHEN 'L' THEN CN.MIDDLE_NAMES ELSE NULL END AS [LEGAL_MIDDLE_NAMES],
CASE RDV.DOMAIN_CODE WHEN 'L' THEN CN.NAME_TITLE ELSE NULL END AS [LEGAL_NAME_TITLE],
CASE RDV.DOMAIN_CODE WHEN 'P' THEN CN.FAMILY_NAME ELSE NULL END AS [PREFERRED_FAMILY_NAME],
CASE RDV.DOMAIN_CODE WHEN 'P' THEN CN.GIVEN_NAME ELSE NULL END AS [PREFERRED_GIVEN_NAME],
CASE RDV.DOMAIN_CODE WHEN 'P' THEN CN.MIDDLE_NAMES ELSE NULL END AS [PREFERRED_MIDDLE_NAMES],
CASE RDV.DOMAIN_CODE WHEN 'P' THEN CN.NAME_TITLE ELSE NULL END AS [PREFERRED_NAME_TITLE]
FROM dbo.CLIENT_NAME CN
JOIN dbo.REFERENCE_DOMAIN_VALUE RDV
ON CN.NAME_TYPE_CODE = RDV.DOMAIN_CODE AND RDV.REFERENCE_DOMAIN_ID = '7966'
No, you will require 8 separate statements as case and other such variants can only be used in a select to modify the results of a single column, not a series of columns.
If RDV.DOMAIN_COD can only by 'P' or 'L' use NULLIf. It's cleaner.
NULLIF ( expression , expression )
NULLIF is equivalent to a searched CASE expression in which the two expressions are equal and the resulting expression is NULL.
SELECT
NullIf('P', RDV.DOMAIN_CODE) AS [LEGAL_FAMILY_NAME],
...
NullIf('L', RDV.DOMAIN_CODE) AS [PREFERRED_FAMILY_NAME],
...
Since a CASE expression returns a single value, you cannot take eight CASE expressions returning 8 values and make a single CASE expression that returns all eight.
A less efficient alternative with no cases:
SELECT LEGAL_FAMILY_NAME, LEGAL_GIVEN_NAME, LEGAL_MIDDLE_NAMES, LEGAL_NAME_TITLE,
PREFERRED_FAMILY_NAME, PREFERRED_GIVEN_NAME, PREFERRED_MIDDLE_NAMES, PREFERRED_NAME_TITLE
FROM dbo.REFERENCE_DOMAIN_VALUE RDV
LEFT OUTER JOIN
( SELECT
NAME_TYPE_CODE,
FAMILY_NAME AS [LEGAL_FAMILY_NAME],
GIVEN_NAME AS [LEGAL_GIVEN_NAME],
MIDDLE_NAMES AS [LEGAL_MIDDLE_NAMES],
NAME_TITLE AS [LEGAL_NAME_TITLE]
FROM dbo.CLIENT_NAME
WHERE NAME_TYPE_CODE = 'L') LN ON RDV.DOMAIN_CODE = LN.NAME_TYPE_CODE
LEFT OUTER JOIN
( SELECT
NAME_TYPE_CODE,
FAMILY_NAME AS [PREFERRED_FAMILY_NAME],
GIVEN_NAME AS [PREFERRED_GIVEN_NAME],
MIDDLE_NAMES AS [PREFERRED_MIDDLE_NAMES],
NAME_TITLE AS [PREFERRED_NAME_TITLE]
FROM dbo.CLIENT_NAME
WHERE NAME_TYPE_CODE = 'P') PN ON RDV.DOMAIN_CODE = PN.NAME_TYPE_CODE
WHERE RDV.REFERENCE_DOMAIN_ID = '7966'
You could also use a temp table or table variable with all 8 columns and then do two inserts. You could also use a UNION ALL. My guess is that the 8 case statements are the most efficient way. This is especially true if you have some key where you will want some type of ClientID, Legal Names, Preferred Names so you will wrap a MAX around the cases or something and group by a ClientID......
You could generate the SQL script using syscols/INFORMATION_SCHEMA.columns with two case whens for each column 'CASE WHEN DOMAIN_CODE = ''P'' THEN ' + COLUMN_NAME + ' ELSE NULL END AS PREFERRED_' + COLUMN_NAME and then another for L. You could make a LOOP so that the same code executes once for P and once for L and get it down to one loop. Then you could EXEC the string directly, or PRINT it and put it into your SQL script. Anyway for just 8 columns I would cut/paste the case statements...
But anyway in general T-SQL is limited on being able to do for eaches over columns. Either you generate the script using a code generator (done in t-sql or another programming language) or you rethink your problem in another way. But many times you get better performance from duplicate cut/paste code. And many times it isn't worth the hassle of writing an external code generator (ie just for 8 case statements).