Oracle SQL CASE expression in WHERE clause only when conditions are met - sql

Cant quite figure this one out, i have a set of conditions that i want to be met only if a value is in a field.
So if the Status is complete i want to have three where clause's, if the status doesn't equal complete then i don't want any where clause.
Code
SELECT *
FROM mytable
WHERE CASE WHEN Status = 'Complete'
THEN (included = 0 OR excluded = 0 OR number IS NOT NULL)
ELSE *Do nothing*

It is usually simple to only use boolean expressions in the WHERE. So:
WHERE (Status <> 'Complete') OR
(included = 0 OR excluded = 0 OR number IS NOT NULL)
If Status could be NULL:
WHERE (Status <> 'Complete' OR Status IS NULL) OR
(included = 0 OR excluded = 0 OR number IS NOT NULL)

You can translate the natural language in SQL, then, if possible, reformulate.
SELECT *
FROM mytable
WHERE (Status = 'Complete' and (included = 0 OR excluded = 0 OR number IS NOT NULL))
or status <> 'Complete'
or status IS NULL;

It doesn't look like you really need a CASE statement, just use it like this:
SELECT *
FROM mytable
WHERE where (Status = 'Complete' and (included = 0 OR excluded = 0 OR number IS NOT NULL)) or (*Your do nothing*)

Related

where clause work only when case is satisfied

I have made a view that has a where clause :
WHERE M.Deleted = 0 AND I.Deleted = 0 AND L.Deleted = 0
is there a possible way to make L.Deleted = 0 only work on a specific case? My plan is when the M.TYPE_ID != 4 then add this L.Delete = 0 to where clause, and without it otherwise
I tried to make something like:
AND CASE WHEN M.TYPE_ID != 4 THEN L.DELETED = 0 ELSE --(i want here to make this entire part of where clause to be invalid if the case is not valid)
or there are other ways to do that like intersect the same select above but without that condition ?
any help would be appreciated, thanks.
Yes, you can do:
WHERE M.Deleted = 0 AND I.Deleted = 0 AND
(M.TYPE_ID = 4 OR L.Deleted = 0)
You don't need a case expression.
You might find the logic simpler to follow as:
WHERE M.Deleted = 0 AND I.Deleted = 0 AND
( NOT (M.TYPE_ID <> 4 AND L.Deleted <> 0) )
The simplest way would be to include the valid condition in two checks and or them together.
e.g.
Select * from x where (x.isvalid = 1 and x.deleted = 0) or (x.isvalid <> 1 and x.othercheck = 1)

Issue with case null in sql

SELECT Id
FROM dbo.OWL_AddlDataFields
WHERE CustomerId = #BaseCustomerId
AND AddlDataFieldCategoryId = #AddlDataFieldCategoryId
AND AddlDataFieldGroupId = CASE
WHEN #AddlDataFieldGroupId = 0 THEN NULL
ELSE #AddlDataFieldGroupId
END
AND Name = #DataFieldName
The above query does not returns any result. I think above query has issue with the 'AddlDataFieldCategoryId =' and the null from the case. Can anybody correct the query?
The issue with your CASE expression is that you are comparing a column to a predicate which might be NULL using the equals operator. This won't behave as expected, because NULL comparisons should use IS NULL instead of equals. One possible workaround would be to coalesce AddlDataFieldGroupId to a numeric value. Then, we can be certain that the CASE expression would only be comparing one number to another.
WHERE ... AND
COALESCE(AddlDataFieldGroupId, 0) = CASE WHEN
#AddlDataFieldGroupId = 0
THEN 0 ELSE #AddlDataFieldGroupId END
If your AddlDataFieldGroupId cannot be zero, then you don't need the CASE at all. This will do the job:
SELECT Id
FROM dbo.OWL_AddlDataFields
WHERE CustomerId = #BaseCustomerId
AND AddlDataFieldCategoryId = #AddlDataFieldCategoryId
AND AddlDataFieldGroupId = #AddlDataFieldGroupId
AND Name = #DataFieldName
If AddlDataFieldGroupId can be zero, but cannot be negative, you can do this:
SELECT Id
FROM dbo.OWL_AddlDataFields
WHERE CustomerId = #BaseCustomerId
AND AddlDataFieldCategoryId = #AddlDataFieldCategoryId
AND AddlDataFieldGroupId = CASE WHEN
#AddlDataFieldGroupId = 0 THEN -1 ELSE #AddlDataFieldGroupId END
AND Name = #DataFieldName
Alternatively, this will work regardless of what AddlDataFieldGroupId can be:
SELECT Id
FROM dbo.OWL_AddlDataFields
WHERE CustomerId = #BaseCustomerId
AND AddlDataFieldCategoryId = #AddlDataFieldCategoryId
AND AddlDataFieldGroupId = CASE WHEN
#AddlDataFieldGroupId = 0 THEN AddlDataFieldGroupId - 1 ELSE #AddlDataFieldGroupId END
AND Name = #DataFieldName
Note: all these solutions assume that AddlDataFieldGroupId cannot be NULL.

CASE syntax inside a WHERE clause with IN on SQL Server

How can I make following query work? This is inside stored procedure #GetClosedMeetings bit parameter passed true to get the meetings which are finished, otherwise get planned and current meetings. (Status: 0 - Planned, 1 - Current, 2 - closed)
SELECT
MeetingId, Status
FROM
dbo.Meeting
WHERE
Status IN (CASE
WHEN #GetClosedMeetings = 1 THEN 1
ELSE #OtherStatuses)
If I understand your requirement correctly, if #GetClosedMeetings is TRUE, you want to return Meetings that are finished, which from your question seems to have a STATUS of 2. On the other hand, if #GetClosedMeetings is FALSE, then you want to get planned and current meetings. Here is one way to achieve this:
SELECT MeetingId, Status
FROM dbo.Meeting
WHERE
(#GetClosedMeetings = 1 AND Status = 2)
OR (#GetClosedMeetings = 0 AND Status IN(0, 1))
use this :
SELECT MeetingId, Status
FROM dbo.Meeting
WHERE (Status = 1 And #GetClosedMeetings = 1)
Or (Status <> 1 And #GetClosedMeetings <> 1 )
You missed end in case
SELECT MeetingId, Status
FROM dbo.Meeting
WHERE Status IN
(CASE
WHEN #GetClosedMeetings = 1 THEN 1
ELSE #OtherStatuses end)

SQL where clause multiple condition on same values

I want to apply different where condition in SQL based on value of other column something like below.
Sorry, I am updating my que with more specific requirement
SELECT * FROM TABLEDetails
WHERE CASE WHEN
ConfigValue = 'FALSE' THEN
(ConfigAmount != 0 OR ConfigVolume != 0)
WHEN ConfigValue = 'TRUE' THEN
(ConfigAmount != 0 OR ConfigVolume <Here, it should allow Zero too with other values>
END
Simply use ANDs and ORs:
SELECT * FROM TABLEDetails
WHERE (ConfigValue = 'FALSE' AND ConfigVolume != 0)
OR (ConfigValue = 'TRUE' AND ConfigVolume = 0)
CASE WHEN returns results expressions, and no comparisons like in your query.
As stated by #Pred, a WHERE clause must contain comparisons.
See MSDN.
After your edit (which completely changed the requirement BTW), I guess something like this could work, but the question is quite unclear now:
SELECT * FROM TABLEDetails
WHERE (ConfigValue = 'FALSE' AND (ConfigAmount != 0 OR ConfigVolume != 0))
OR (ConfigValue = 'TRUE' AND (ConfigAmount != 0 OR ConfigVolume IN (0, 1, 2, ... /* add as many values you need*/)))
#X.L.Ant wrote a correct and excellent answer, but to complete the list of the possible solutions. Here is one with the CASE..WHEN clause:
SELECT
*
FROM
TABLEDetails
WHERE
ConfigValue = CASE
WHEN ConfigVolume != 0 THEN 'FALSE'
WHEN ConfigVolume = 0 THEN 'TRUE'
END
Please note, that the CASE..WHEN clause returns NULL if there is no ELSE condition and none of the WHEN conditions are met. (This will filter all records where ConfigValue < 0 is true, since NULL is not equal to anything).

Oracle query running too slow (approximately taking 5 hrs)

SELECT t1.member_id ,
SUM(t1.paid_amt) AS paid_amt
FROM
(SELECT DISTINCT fm.member_id,
fc.claim_skey_no,
fc.claim_id,
fc.claim_line_no ,
CASE
WHEN fc.claim_type_cd = 'RX'
THEN NVL(fc.rx_paid_amt,0) -- For RX claims use rx_paid_amt as paid amount
ELSE NVL(fc.approved_amt,0)
END AS paid_amt -- For all other claims use approved_amt
,
CASE
WHEN fc.claim_type_cd = 'RX'
THEN fc.submit_dt --For RX claims use submit_dt as paid date
ELSE NVL(fc.paid_dt,NVL(fc.edi_eob_dt,NVL(fc.eob_run_dt,fc.outsource_vndr_paid_dt)))
END AS paid_dt --For all other claims use paid_dt
FROM dwprod.fct_claim fc ,
dwprod.fct_member fm
WHERE fc.mbr_skey_no = fm.member_skey_no
-- The service_from_dt on the claim must be between the reimbursement time period.
AND fc.service_from_dt BETWEEN '31-MAY-2013' AND '30-Jun-2014'
-- The follwong 2 conditions make sure that the calims selected are final-status (unadjusted)
-- For non-RX claims, the adjust_type_cd must be Null and the dw_backout_tag must be Null or 'N'
-- For RX claims only the dw_backout_tag must be Null or 'N', the adjust_type_cd is ignored
AND
CASE
WHEN fc.claim_type_cd = 'RX'
THEN 1
WHEN fc.claim_type_cd <> 'RX'
AND fc.adjust_type_cd IS NULL
THEN 1
ELSE 0
END = 1
AND NVL(fc.dw_backout_tag,'N') = 'N'
-- The claim must be in an 'Approved' status, indicated by a status_type_cd = 'A'
AND fc.status_type_cd = 'A'
-- QNXT claims must be in a 'PAID' status
-- Non QNXT claims in the warehouse are assumed to be paid - There are no pended RX claims.
AND
CASE
WHEN fc.dw_source_cd <> 'QNXT'
THEN 1
WHEN fc.dw_source_cd = 'QNXT'
AND fc.last_status_nm = 'PAID'
THEN 1
ELSE 0
END = 1
-- Dental claims are excluded
AND fc.dw_source_cd <> 'DBP'
-- Excludes any Medicare Non-RCI claims
AND
CASE
WHEN NVL(fc.program_nm,'OTHER') = 'MEDICAID'
AND NVL(fc.enroll_ratecode,'RCI') IN ('RCII','RCV','RCVII')
THEN 0
ELSE 1
END = 0
-- It Fits! claims are excluded
AND NVL(fc.expense_cat_nm,'Other') <> 'FITNESS'
AND NVL(fc.proc1_skey_no,12345) NOT IN (21586,21588,21589)
--
AND
CASE
WHEN NVL(fc.program_nm,'OTHER') = 'MEDICAID'
AND NVL(fc.enroll_ratecode,'RCI') IN ('RCII','RCV','RCVII')
THEN 1
WHEN EXISTS
(SELECT 1
FROM dwprod.fct_member_enroll me
WHERE fm.member_skey_no = me.mbr_skey_no
AND fc.service_from_dt BETWEEN me.segment_effect_dt AND me.segment_term_dt
AND me.program_nm = 'MEDICAID'
AND me.enroll_ratecode IN ('RCII','RCV','RCVII')
)
THEN 1
ELSE 0
END = 1
) t1
--Where t1.paid_dt < '31-JAN-2014'
GROUP BY t1.member_id
HAVING SUM(t1.paid_amt) > 175000
Run an explain plan to see what's causing the slowdown. From the top of my head, this is what's 'killing' you:
WHEN EXISTS
(SELECT 1
FROM dwprod.fct_member_enroll me
WHERE fm.member_skey_no = me.mbr_skey_no
AND fc.service_from_dt BETWEEN me.segment_effect_dt AND me.segment_term_dt
AND me.program_nm = 'MEDICAID'
AND me.enroll_ratecode IN ('RCII','RCV','RCVII')
)
See if you can somehow change this exists logic to something with better performance. The explain plan is a must though!
I'm gonna make a semi-blind guess here, based on similar queries in the DW I'm working with.
Oracle's optimizer gets easily confused by predicates such as:
where (case when ... then ... else ... end) = 1;
The reason is that Oracle grossly over estimate the selectivity.
Check the explain plan like others have said. If you find that the estimated cardinality of table dwprod.fct_claim seems way too low, try unrolling the case statements.
For example, instead of:
AND CASE WHEN fc.dw_source_cd <> 'QNXT' THEN 1
WHEN fc.dw_source_cd = 'QNXT' AND fc.last_status_nm = 'PAID' THEN 1
ELSE 0
END = 1
Write:
and ( fc.dw_source_cd <> 'QNXT'
or (fc.dw_source_cd = 'QNXT' and fc.last_status_nm = 'PAID')
)
Final note. This seems to be less of a problem in version 11, but I have not yet had time to investigate why.
FROM dwprod.fct_claim fc ,
dwprod.fct_member fm
WHERE fc.mbr_skey_no = fm.member_skey_no
This cross join is effectively an inner join. I can't say whether Oracle will optimize this, but there's no reason not to make its job easier:
FROM dwprod.fct_claim fc ,
INNER JOIN dwprod.fct_member fm
ON fc.mbr_skey_no = fm.member_skey_no