grigs
grigs

Reputation: 1150

SQL/PLSQL Oracle query: CASE in WHERE statement

I am attempting to query an oracle db but cant quite get the script to work. The script is meant to search for employees with a start_date between two dates. Unfortunately, not all employees have a start_date in the database, but all employees have a create_date. What I would like to do is:

IF START_DATE IS NULL:
    (E.START_DATE >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND E.START_DATE <= TO_DATE('10/29/2016', 'MM/DD/YYYY')
ELSE: 
    (E.CREATE_DATE >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND E.CREATE_DATE <= TO_DATE('10/29/2016', 'MM/DD/YYYY')

Here is what I have so far:

SELECT OC.DESCRIPTION "Description",
sum(S.AMOUNT) "SUM",
COUNT (DISTINCT(E.EMPLOYEE_NUM)) "Employee Nums"
FROM EMPLOYEES E
JOIN EMPLOYEE_SPENDING S ON S.EMPLOYEE_ID = E.EMPLOYEE_ID
WHERE E.EMPLOYEE_CATEGORY IN ('ACTIVE', 'INACTIVE')
AND ((E.START_DATE >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND E.START_DATE <= TO_DATE('10/29/2016', 'MM/DD/YYYY'))
OR (E.CREATE_DATE >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND E.CREATE_DATE <= TO_DATE('10/29/2016', 'MM/DD/YYYY')))
GROUP BY E.EMPLOYEE_CATEGORY;

My case statement looked something like this:

SELECT OC.DESCRIPTION "Description",
sum(S.AMOUNT) "SUM",
COUNT (DISTINCT(E.EMPLOYEE_NUM)) "Employee Nums"
FROM EMPLOYEES E
JOIN EMPLOYEE_SPENDING S ON S.EMPLOYEE_ID = E.EMPLOYEE_ID
WHERE E.EMPLOYEE_CATEGORY IN ('ACTIVE', 'INACTIVE')
AND (CASE WHEN E.START_DATE IS NULL THEN (E.START_DATE >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND E.START_DATE <= TO_DATE('10/29/2016', 'MM/DD/YYYY'))
ELSE (E.CREATE_DATE >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND E.CREATE_DATE <= TO_DATE('10/29/2016', 'MM/DD/YYYY'))
END
GROUP BY E.EMPLOYEE_CATEGORY;

I'm not super familiar with Oracle or PLSQL, so any help you can provide would be much appreciated!

Thanks in advance!

Upvotes: 1

Views: 100

Answers (2)

Daniel Marcus
Daniel Marcus

Reputation: 2686

Use nvl like this:

SELECT OC.DESCRIPTION "Description",
sum(S.AMOUNT) "SUM",
COUNT (DISTINCT(E.EMPLOYEE_NUM)) "Employee Nums"
FROM EMPLOYEES E
JOIN EMPLOYEE_SPENDING S ON S.EMPLOYEE_ID = E.EMPLOYEE_ID
WHERE E.EMPLOYEE_CATEGORY IN ('ACTIVE', 'INACTIVE')
AND ((nvl(E.START_DATE, e.create_date) >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND (nvl(E.START_DATE, e.create_date) <= TO_DATE('10/29/2016', 'MM/DD/YYYY'))
GROUP BY E.EMPLOYEE_CATEGORY;

Upvotes: 0

Gordon Linoff
Gordon Linoff

Reputation: 1269503

I believe you have the logic backwards. This makes much more sense:

IF START_DATE IS NOT NULL:
    (E.START_DATE >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND E.START_DATE <= TO_DATE('10/29/2016', 'MM/DD/YYYY')
ELSE: 
    (E.CREATE_DATE >= TO_DATE('10/29/2014', 'MM/DD/YYYY') AND E.CREATE_DATE <= TO_DATE('10/29/2016', 'MM/DD/YYYY')

Then, you can just express this as:

where (e.start_date >= date '2014-10-29' and e.start_date <= date '2016-10-29') or
      (e.start_date is null and e.create_date >= date '2014-10-29' and e.start_date <= date '2016-10-29')

Notice that I've simplified the "date constant" by using the date keyword followed by an ISO-standard date.

This can be further simplified as:

where coalesce(e.start_date, e.create_date) >= date '2014-10-29' and
      coalesce(e.start_date, e.create_date) <= date '2016-10-29'

In general, you want to avoid case in where clauses, because basic boolean expressions are usually pretty simple to write and understand. The time to use case is when you explicitly want to sequence the conditions, particularly when one condition might be an expensive function call.

Upvotes: 2

Related Questions