Select Logic in Query. Is this possible in Oracle? - sql

Im trying to implement some sort of logic on the select statement of the query. I want it so that if no attribute is given, or if the inAttribute is 'NONE'; it will return the date and ALL of the values (compprice, compspread,price,spread,run).
If a value was given to in attribute then i want it to return the value it requested for (Refer to the case statement i tried to do).
Below is my attempt at it, and it is just not working. Any help please?
SELECT
mi.date,
IF inAttribute = '' THEN
mi.compprice,
mi.compspread,
mi.price,
mi.spread,
mi.run
ELSE
CASE inAttribute
WHEN 'CP' THEN mi.compprice,
WHEN 'CS THEN mi.compspread,
WHEN 'MP' THEN mi.price,
WHEN 'MS' THEN mi.spread,
WHEN 'R' THEN mi.run
END
END IF
FROM userValueTable mi
WHERE mi.index_family = inIdxFamily
AND mi.index_id = inIdxId
AND mi.date_>= inStartDate
AND mi.date_<= inEndDate
ORDER by mi.date_ ASC;

Few remarks You can't have variable column list from one line to another. '' is equal to NULL, comparing NULL with equality ( = ) is always false. you can have some fixed number of columns and set value of any column using CASE clause.

Oracle doesn't have an IF for SQL. Use CASE instead - it can act like a C/C#/Java/etc switch - case as you have it in your query, and it can also act like an if.
Also, as mentioned above:
You're stuck with returning a constant number of columns unless you use dynamic SQL
In Oracle, '' is treated as NULL so instead of = '' use IS NULL.
If you don't want to go to Dynamic SQL, you could add a "type" column to your select list and then null out any inapplicable values. Your downstream logic could pick the values to use (or ignore) based on the type. Here's an example:
SELECT
mi.date,
CASE
WHEN inAttribute IS NULL THEN 'inAttrNull'
ELSE 'inAttrNotNull'
END AS RecordType,
CASE WHEN inAttribute IS NULL THEN mi.compprice END AS compprice,
CASE WHEN inAttribute IS NULL THEN mi.compspread END AS compspread,
CASE WHEN inAttribute IS NULL THEN mi.price END AS price,
CASE WHEN inAttribute IS NULL THEN mi.spread END AS spread,
CASE WHEN inAttribute IS NULL THEN mi.run END AS run,
CASE inAttribute
WHEN 'CP' THEN mi.compprice
WHEN 'CS' THEN mi.compspread
WHEN 'MP' THEN mi.price
WHEN 'MS' THEN mi.spread
WHEN 'R' THEN mi.run
END AS SpecialValue
FROM userValueTable mi
... and your WHERE and ORDER BY clauses
You'll get a result set something like this:
RecordType compprice compspread price spread run specialvalue
------------- ---------- ---------- ---------- ---------- ---------- ------------
inAttrNotNull (null) (null) (null) (null) (null) 1234.56
inAttrNull 111.11 222.22 333.33 444.44 555.55 (null)
I know this isn't what you wanted, but it may be something you can work with as an alternative.

There is no IF in Oracle SQL, only in PL/SQL. You have to rewrite it to CASE. The same as you did in your query in ELSE part (you missed a single closing quote after 'CS' THEN...).
SELECT ...
(CASE WHEN inAttribute IS NULL THEN mi.compprice
WHEN .... THEN...
WHEN .... THEN...
...
ELSE ...
END) AS some_column_alias
FROM ...

Related

Is there a way to use a variable to comment out a line in the where clause?

I have a line in my WHERE clause that would be a nice added feature to be able to toggle. This way the end user doesn't have to run two reports. I haven't found anything like it so I'm thinking you cannot but maybe I am looking at the problem the wrong way and someone else knows a better way.
I have tried to run the code below with the last line as #AND and defining the variable as 'AND' & '--'giving me incorrect syntax error x2.
I also tried IS #NULL and defining the variable as 'NULL' & 'NOT NULL' giving me incorrect syntax error x2
SELECT
item_id
FROM
cba_item_location
WHERE
discontinued='Y'
AND qty_on_hand = '0'
AND (select abc_code from abc_miscdata where abc_code=item_id and abc_type='ITEM' and data_1 ='DISCON_ZERO_SENT') IS NULL
order by item_id
Example 1:
SELECT
item_id
FROM
cba_item_location
WHERE
discontinued='Y'
AND qty_on_hand = '0'
**#AND** (select abc_code from abc_miscdata where abc_code=item_id and abc_type='ITEM' and data_1 ='DISCON_ZERO_SENT') IS NULL
Example 2:
SELECT
item_id
FROM
cba_item_location
WHERE
discontinued='Y'
AND qty_on_hand = '0'
AND (select abc_code from abc_miscdata where abc_code=item_id and abc_type='ITEM' and data_1 ='DISCON_ZERO_SENT') IS **#NULL**
Any ideas?
Option 1: dynamic SQL
Option 2: include a flag into your WHERE clause which will switch it on and off
DECLARE #and bit = 0
IF (some condition) SET #and = 1
...
WHERE
discontinued='Y'
AND qty_on_hand = '0'
AND (#and = 1 AND (select abc_code from abc_miscdata where abc_code=item_id and abc_type='ITEM' and data_1 ='DISCON_ZERO_SENT') IS NULL)

Unable to Correct Oracle Error ORA-00905 Missing Keyword in SQL

I've been grinding on this small snippet of simple code for a day and can't find the issue causing the error. As the title states, I am getting error ORA-00905 Missing Keyword when trying to execute the following code:
SELECT DISTINCT DM.DESCRIPTION AS "AGENCY",
DM.DEPT_NO AS "DEPT NO",
CASE
WHEN VMP.RESERVE_DT IS NULL THEN
NULL
ELSE
VMP.RESERVE_DT
END AS "RESV_DT",
CASE
WHEN VMP.EST_PICKUP_DT IS NULL THEN
NULL
ELSE
VMP.EST_PICKUP_DT
END AS "EST_PKUP_DT",
CASE
WHEN VMP.EST_RETURN_DT IS NULL THEN
NULL
ELSE
VMP.EST_RETURN_DT
END AS "EST_RETN_DT",
VMP.EMP_NAME AS "EMPL_NAME",
VMP.UNIT_NO AS "UNIT_NUMBER",
VMP.RENTAL_CLASS_DESCRIPTION AS "RENT_CLS",
VMP.MP_TICKET_NO AS "MP_TKT_NO"
FROM DEPT_MAIN DM
INNER JOIN VIEW_MPOOL VMP ON VMP.DEPT_ID = DM.DEPT_ID
WHERE CASE
WHEN VMP.EST_PICKUP_DT IS NULL THEN
NULL
ELSE
TRUNC(VMP.EST_PICKUP_DT) = TRUNC(SYSDATE)
END
GROUP BY DM.DESCRIPTION,
DM.DEPT_NO,
CASE
WHEN VMP.RESERVE_DT IS NULL THEN
NULL
ELSE
VMP.RESERVE_DT
END,
CASE
WHEN VMP.EST_PICKUP_DT IS NULL THEN
NULL
ELSE
VMP.EST_PICKUP_DT
END,
CASE
WHEN VMP.EST_RETURN_DT IS NULL THEN
NULL
ELSE
VMP.EST_RETURN_DT
END,
VMP.EMP_NAME,
VMP.UNIT_NO,
VMP.RENTAL_CLASS_DESCRIPTION,
VMP.MP_TICKET_NO
ORDER BY CASE
WHEN VMP.EST_PICKUP_DT IS NULL THEN
NULL
ELSE
VMP.EST_PICKUP_DT
END ASC
The basis of this code was generated through an adhoc reporting program and was originally fully-qualified. I stripped out the extraneous quotation marks and assigned table aliases to clean it up. Though I hoped these efforts would help me find the issue, I am unable to find the cause. Thank you for your consideration.
Try to replace this :
WHERE
CASE
WHEN VMP.EST_PICKUP_DT IS NULL THEN NULL
ELSE TRUNC(VMP.EST_PICKUP_DT) = TRUNC(SYSDATE)
END
With :
WHERE
CASE
WHEN VMP.EST_PICKUP_DT IS NULL THEN NULL
ELSE TRUNC(VMP.EST_PICKUP_DT)
END
= TRUNC(SYSDATE)
Please note that this whole expression (and others similar in the query) could be simplified as as :
WHERE
TRUNC(VMP.EST_PICKUP_DT) = TRUNC(SYSDATE)
When VMP.EST_PICKUP_DT is NULL, TRUNC will return NULL, which will not be equal to TRUNC(SYSDATE).
Here:
WHERE
CASE
WHEN VMP.EST_PICKUP_DT IS NULL
THEN NULL
ELSE TRUNC(VMP.EST_PICKUP_DT) = TRUNC(SYSDATE)
END
The END is after a comparison and the case is not being compared against a value.
Switch it like:
WHERE
CASE
WHEN VMP.EST_PICKUP_DT IS NULL
THEN NULL
ELSE TRUNC(VMP.EST_PICKUP_DT)
END = TRUNC(SYSDATE)
Which as horse says, can just be simplified as
WHERE TRUNC(VMP.EST_PICKUP_DT) = TRUNC(SYSDATE)
since a NULL value on VMP.EST_PICKUP_DT will never match TRUNC(SYSDATE).
can't find the issue causing the error
Although I suppose that #EzLo and #a_horse_with_no_name maybe already found error in this case, I can propose a general procedure in debugging such queries.
Step 1: Debug your JOIN- and WHERE- predicates
Comment everything in your SELECT-statement, leave only JOINs, substitute fields with * or constant expression.
E.g.
SELECT 1
-- DISTINCT DM.DESCRIPTION AS "AGENCY",
-- DM.DEPT_NO AS "DEPT NO",
-- CASE
-- ....
-- VMP.RENTAL_CLASS_DESCRIPTION AS "RENT_CLS",
-- VMP.MP_TICKET_NO AS "MP_TKT_NO"
FROM DEPT_MAIN DM
INNER JOIN VIEW_MPOOL VMP ON VMP.DEPT_ID = DM.DEPT_ID
WHERE CASE
WHEN VMP.EST_PICKUP_DT IS NULL THEN
NULL
ELSE
TRUNC(VMP.EST_PICKUP_DT) = TRUNC(SYSDATE)
END
-- GROUP BY DM.DESCRIPTION,
-- ....
-- ORDER BY ..
In case of multiple complex predicates - uncomment one predicate at a time.
Step 2: Debug your GROUP BYs and HAVINGs
Uncomment GROUP BY section and edit fields section of your query.
If you have complex groupby's - uncomment by one field at time.
Start from simplest to complex
SELECT
DM.DESCRIPTION
,DM.DEPT_NO
-- ...
FROM DEPT_MAIN DM
INNER JOIN VIEW_MPOOL VMP ON VMP.DEPT_ID = DM.DEPT_ID
WHERE CASE
WHEN VMP.EST_PICKUP_DT IS NULL THEN
NULL
ELSE
TRUNC(VMP.EST_PICKUP_DT) = TRUNC(SYSDATE)
END
GROUP BY DM.DESCRIPTION
,DM.DEPT_NO
Copy-paste your GROUP BYs into SELECT fields section.
Step 3: Debug you aggregates, field transformation and renames
Now you have correct SELECT-query but probably not in the shape you want.
Step 4: Debug ORDER BYs
You have correct shape and in last step you need to ORDER BY your data.
If you have decent editor/IDE you can find source of error in 5-10 minutes even in cases of complex queries (and even RDBMS engine bugs)
P.S.
It's better to note which version of RDBMS you are using.

How to write multiple conditions(Combination of and / or) in CASE statement?

I am trying to select values based on the following case statment
CASE
when ty.type ='Catalog'
and zs.name='Aries'
or zs.name='Leo'
AND ( CASE
when actual_finish_date is not null
then actual_finish_date
when updated_finish_date is not null
then updated_finish_date
else baseline_finish_date
END ) is not null
and visibility.ty_visibility !='Private'
then 1
else 0
END as PARTICIPANT,
Now the problem is it's not checking the entire condition,it's stopping at the first line itself(ty.type='Catalog').
Even when ty_visibility is equal to Private it's selecting the value as 1 instead of 0
Can someone point where I went wrong?
You need to add parentheses to get 'Catalog' and one of the names:
ty.type ='Catalog' and (zs.name='Aries' or zs.name='Leo')
Or switch to IN:
ty.type ='Catalog' and zs.name IN ('Aries', 'Leo')
You can also use COALESCE to simplify the is not null tests:
coalesce(actual_finish_date , updated_finish_date , baseline_finish_date ) is not null
Specify or condition correctly. Hope following will work correct.
CASE
when ty.type ='Catalog'
and (zs.name='Aries'
or zs.name='Leo')
AND ( CASE
when actual_finish_date is not null
then actual_finish_date
when updated_finish_date is not null
then updated_finish_date
else baseline_finish_date
END ) is not null
and visibility.ty_visibility !='Private'
then 1
else 0
END as PARTICIPANT,
Try this.

Using CASE statement name as another reference filed in another CASE statement

Is it possible to refer to another case statement name in another case statement within SQL query?
Example: I have 3 case statements. The first 2 case statements are returning values based off coded fields. My 3rd case statement I would like to refer to the ending case name to return a sum of quantity.
However, I cannot figure how to get the case statement to refer to the previous case names I created. I hope I am explaining this correctly.
Any assistance would be greatly appreciated. Please see attached image for more detail.
SELECT CI_ITEM.ITEMCODE
, CI_ITEM.ITEMCODEDESC
, CASE WHEN DATEDIFF("M",CI_ITEM.DATECREATED,GETDATE()) <60 THEN DATEDIFF("M",CI_ITEM.DATECREATED,GETDATE())
ELSE 60 END AS NO_OF_MONTHS
, CASE WHEN DATEDIFF("M",IM_ITEMTRANSACTIONHISTORY.TRANSACTIONDATE,GETDATE()) <=60
AND IM_ITEMTRANSACTIONHISTORY.TRANSACTIONCODE IN ('BI','II','SO','WI')
THEN IM_ITEMTRANSACTIONHISTORY.TRANSACTIONQTY *-1 ELSE '0' END AS QTY_CONSUMED_60_MONTHS
, CASE WHEN NO_OF_MONTHS = 0 THEN 0 ELSE SUM([QTY_CONSUMED_60_MONTHS])/ [NO_OF_MONTHS] END AS MONTHLY_AVE_ON_60MONTHS_DATA
FROM CI_ITEM
INNER JOIN IM_ITEMTRANSACTIONHISTORY ON CI_ITEM.ITEMCODE = IM_ITEMTRANSACTIONHISTORY.ITEMCODE
Simply wrap your dependent cases within a sub query and reference them as fields of the sub query result.
SELECT
*,
CASE WHEN NO_OF_MONTHS = 0 THEN 0 ELSE SUM([QTY_CONSUMED_60_MONTHS])/ [NO_OF_MONTHS] END AS MONTHLY_AVE_ON_60MONTHS_DATA
FROM
(
SELECT CI_ITEM.ITEMCODE
, CI_ITEM.ITEMCODEDESC
, CASE WHEN DATEDIFF("M",CI_ITEM.DATECREATED,GETDATE()) <60 THEN DATEDIFF("M",CI_ITEM.DATECREATED,GETDATE())
ELSE 60 END AS NO_OF_MONTHS
, CASE WHEN DATEDIFF("M",IM_ITEMTRANSACTIONHISTORY.TRANSACTIONDATE,GETDATE()) <=60
AND IM_ITEMTRANSACTIONHISTORY.TRANSACTIONCODE IN ('BI','II','SO','WI')
THEN IM_ITEMTRANSACTIONHISTORY.TRANSACTIONQTY *-1 ELSE '0' END AS QTY_CONSUMED_60_MONTHS
FROM CI_ITEM
INNER JOIN IM_ITEMTRANSACTIONHISTORY ON CI_ITEM.ITEMCODE = IM_ITEMTRANSACTIONHISTORY.ITEMCODE
)AS X

CASE Statement in where clause using equal to and IN

WHERE CONDITION1='ABC'
AND Status =
CASE #Option
WHEN 1 THEN 'True'
WHEN 2 THEN 'False'
WHEN 3 THEN NULL
WHEn 4 THEN **IN ('True', 'False', NULL)**
END
How do I write a query where my first options match directly using = but my last option needs an IN
The above query gives error, but I want something similar to it, which I am not able to find out.
A CASE statement can't return a set of values... but this query should give you the same results:
WHERE CONDITION1='ABC'
AND Status =
CASE
WHEN 1 THEN 'True'
WHEN 2 THEN 'False'
WHEN 3 THEN NULL
WHEN 4 THEN Status
END
Also, note that unless you have ANSI_NULLS OFF, Status will never = NULL... you would need to use IS NULL for this comparison, and you'd need to forgo the CASE statement altogether.
Skip the CASE statement and use OR. And as per ANSI standard don't compare with NULL:
WHERE CONDITION1='ABC'
AND ((#Option = 1 AND Status = 'True') OR
(#Option = 2 AND Status = 'False') OR
(#Option = 3 AND Status IS NULL) OR
(#Option = 4 AND (Status IS NULL OR Status IN ('True', 'False'))))