简体   繁体   中英

Retrieve the First True Condition in a SQL Query

I'm running into a problem wherein I need to get the changes of an Employee based on specific conditions.

I should retrieve rows only based on the changes below:

  1. Assignment Category (only based on specific Categories shown in the table xxtest)
  2. Pay Basis (only from Hourly to Salaried and Vice Versa, also shown in the table xxtest)
  3. Both Pay Basis and Assignment Category (based on the same constraints above)

I have created a Table (XXTEST) to restrict the SQL to only reference this mapping (See DML and DDL at the end of the Question).

Basically, the Data I'm Expecting would be:

| --------|-------------------|------------------|---------------|------------- |-------------|
| Sample  | FROM_PAY_BASIS_ID | TO_PAY_BASIS_ID  | FROM_CATEGORY | TO_CATEGORY  | CHANGE_TYPE |
| --------| ------------------| -----------------| --------------|------------- |-------------|
| 1       | 1                 | 2                | FR            | FR           | PAY_BASIS   |
| 2       | 1                 | 1                | FT            | FR           | ASSIGN_CAT  |
| 3       | 1                 | 2                | FT            | FR           | BOTH        |

the table above is based on the conditions below:

1. if BOTH, it should get "Assignment Category and Pay Basis" as its Change_Type
2. if Assignment Category, it should get "Assignment Category" as its Change_Type
3. if Pay Basis, it should get "Pay Basis" as its Change_Type 

I want it to evaluate first the condition 1, and if its false, then evaluate the next until the last.
the problem occurs when both of these columns change, resulting into having 3+ rows with CHANGE_TYPE having "PAY_BASIS", "ASSIGN_CAT", "BOTH" as values.

I've tried a lot of methods but nothing seemed to fully address my need, such as the query below:

select  *
from    (select coalesce((select  CHANGE_TYPE
                          from    XXTEST  
                          where   FROM_CATEGORY   = pax.EMPLOYMENT_CATEGORY    
                          AND     TO_CATEGORY     = pax2.EMPLOYMENT_CATEGORY
                          AND     FROM_ID         = pax.PAY_BASIS_ID
                          and     TO_ID           = pax2.PAY_BASIS_ID),
                          select  CHANGE_TYPE
                          from    XXTEST  
                          WHERE   FROM_CATEGORY   = pax.EMPLOYMENT_CATEGORY    
                          AND     TO_CATEGORY     = pax2.EMPLOYMENT_CATEGORY
                          AND     FROM_ID         = 0
                          and     TO_ID           = 0),
                          select  CHANGE_TYPE
                          from    XXTEST  
                          WHERE   FROM_CATEGORY   = 'N/A'
                          AND     TO_CATEGORY     = 'N/A'
                          AND     FROM_ID         = pax.PAY_BASIS_ID
                          and     TO_ID           = pax2.PAY_BASIS_ID),
                          NULL ) CHANGE_TYPE FROM DUAL ) CHANGE_TYPE
            ,   PPX.FULL_NAME
            ,   PPX.EMPLOYEE_NUMBER
        from    per_people_X          ppx
            ,   per_assignments_X     pax 
            ,   per_assignments_X     pax2
        WHERE   pax.assignment_id     = pax2.assignment_id
        AND     PPX.PERSON_id         = pax2.PERSON_id
        AND     PPX.PERSON_id         = PAX.PERSON_id)
where   CHANGE_TYPE is not null;

This kinda works but it retrieves all records, regardless if it has a match or not (due to the NULL "else" condition) and results into Change_Type = NULL. Is it possible to just filter only those records that do not have Change_Type = NULL without encasing it in another SELECT statement?

I've also tried using CASE, It works fine if the Employee's Pay Basis ID only changed (ex. from 1 to 2) and if only the Employee's Category has changed (ex. from FT to FR), but it evaluates all the cases (returning 3 rows) whenever "both" changes occur.

Any advise?

You are probably after case statements in the where clause, something like:

SELECT  PPX.FULL_NAME
    ,   PPX.EMPLOYEE_NUMBER
    ,   XT.CHANGE_TYPE
FROM    per_people_X          ppx
    ,   XXTEST                XT
    ,   per_assignments_X     pax 
    ,   per_assignments_X     pax2
WHERE   pax.assignment_id     = pax2.assignment_id
AND     PPX.PERSON_id         = pax2.PERSON_id
AND     PPX.PERSON_id         = PAX.PERSON_id
AND     (-- Both Employment Category and Pay Basis records records that were Changed
         case when     XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY    
                   AND XT.TO_CATEGORY   = pax2.EMPLOYMENT_CATEGORY
                   AND XT.FROM_ID       = pax.PAY_BASIS_ID           
                   AND XT.TO_ID         = pax2.PAY_BASIS_ID
                   then 1
              else 0
         end = 1
         OR
         -- all Pay Basis records that were "Updated"
         case when     XT.FROM_CATEGORY = 'N/A'
                   AND XT.TO_CATEGORY   = 'N/A'
                   AND XT.FROM_ID       = pax.PAY_BASIS_ID           
                   AND XT.TO_ID         = pax2.PAY_BASIS_ID
                   then 1
              else 0
         end = 1
         OR
         -- all Assignment Category records that were "Updated"
         case when     XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY    
                   AND XT.TO_CATEGORY   = pax2.EMPLOYMENT_CATEGORY
                   AND XT.FROM_ID       = 0       
                   AND XT.TO_ID         = 0
                   then 1
              else 0
         end = 1);

NB untested, since you didn't provide sample data for the per_people_x or per_assignments tables.

You may have to add extra conditions into the "both categories" case expression to exclude the other two cases; it's not immediately apparent from the data that you have supplied so far.

First of all if you use OR you should use brackets and (... or ... ) or you lose some predicates. Your first query should be rewritten to:

SELECT  PPX.FULL_NAME
,   PPX.EMPLOYEE_NUMBER
,   XT.CHANGE_TYPE
FROM    per_people_X          ppx
,   XXTEST                XT
,   per_assignments_X     pax 
,   per_assignments_X     pax2
WHERE   pax.assignment_id     = pax2.assignment_id
AND     PPX.PERSON_id         = pax2.PERSON_id
AND     PPX.PERSON_id         = PAX.PERSON_id
-- THIS IS THE SECTION OF THE QUERY WHERE I'M HAVING PROBLEMS -- 
-- Both Employment Category and Pay Basis records records that were Changed
AND ( 
     (XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY    
    AND      XT.TO_CATEGORY   = pax2.EMPLOYMENT_CATEGORY
    AND      XT.FROM_ID       = pax.PAY_BASIS_ID           
    AND      XT.TO_ID         = pax2.PAY_BASIS_ID)
    -- all Pay Basis records that were "Updated"
    or      (XT.FROM_CATEGORY = 'N/A'
    AND      XT.TO_CATEGORY   = 'N/A'
    AND      XT.FROM_ID       = pax.PAY_BASIS_ID           
    AND      XT.TO_ID         = pax2.PAY_BASIS_ID)
    -- all Assignment Category records that were "Updated"
    or      (XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY    
    AND      XT.TO_CATEGORY   = pax2.EMPLOYMENT_CATEGORY
    AND      XT.FROM_ID       = 0       
    AND      XT.TO_ID         = 0)
);

EDIT: Add solution with ranking of condition

SELECT FULL_NAME
,   EMPLOYEE_NUMBER
,   CHANGE_TYPE
FROM (
SELECT  PPX.FULL_NAME
,   PPX.EMPLOYEE_NUMBER
,   XT.CHANGE_TYPE
,   DENSE_RANK() OVER (PRTITION BY PPX.PERSON_id,pax.assignment_id ORDER BY
   CASE WHEN XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY    
             AND      XT.TO_CATEGORY   = pax2.EMPLOYMENT_CATEGORY
             AND      XT.FROM_ID       = pax.PAY_BASIS_ID           
             AND      XT.TO_ID         = pax2.PAY_BASIS_ID 
        THEN 1
        WHEN XT.FROM_CATEGORY = 'N/A'
             AND      XT.TO_CATEGORY   = 'N/A'
             AND      XT.FROM_ID       = pax.PAY_BASIS_ID           
             AND      XT.TO_ID         = pax2.PAY_BASIS_ID
        THEN 2
        ELSE 3 
    END
    ) as RNK
FROM    per_people_X          ppx
,   XXTEST                XT
,   per_assignments_X     pax 
,   per_assignments_X     pax2
WHERE   pax.assignment_id     = pax2.assignment_id
AND     PPX.PERSON_id         = pax2.PERSON_id
AND     PPX.PERSON_id         = PAX.PERSON_id
-- THIS IS THE SECTION OF THE QUERY WHERE I'M HAVING PROBLEMS -- 
-- Both Employment Category and Pay Basis records records that were Changed
AND ( 
     (XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY    
    AND      XT.TO_CATEGORY   = pax2.EMPLOYMENT_CATEGORY
    AND      XT.FROM_ID       = pax.PAY_BASIS_ID           
    AND      XT.TO_ID         = pax2.PAY_BASIS_ID)
    -- all Pay Basis records that were "Updated"
    or      (XT.FROM_CATEGORY = 'N/A'
    AND      XT.TO_CATEGORY   = 'N/A'
    AND      XT.FROM_ID       = pax.PAY_BASIS_ID           
    AND      XT.TO_ID         = pax2.PAY_BASIS_ID)
    -- all Assignment Category records that were "Updated"
    or      (XT.FROM_CATEGORY = pax.EMPLOYMENT_CATEGORY    
    AND      XT.TO_CATEGORY   = pax2.EMPLOYMENT_CATEGORY
    AND      XT.FROM_ID       = 0       
    AND      XT.TO_ID         = 0)
)
) WHERE RNK = 1;

I didn't follow all the details of your post and the answers and ensuing comments, but it seems you may be after something like this. To get exactly one answer in all situations, you probably need a CASE expression. In the code below I use nested CASE expressions, but that is only to save typing (and a little bit of execution time); you could rewrite this using a single CASE expression.

The reason this works is that evaluation ends immediately as soon as the first TRUE condition in the when... then... pairs is found. You will have to figure out how to attach this to your existing query - I just put its outputs in a CTE for testing purposes below.

with
     query_output ( empl_id, from_pay_basis_id, to_pay_basis_id, from_category, to_category ) as (
       select 101, 1, 2, 'FR', 'FR' from dual union all
       select 102, 1, 1, 'FT', 'FR' from dual union all
       select 103, 1, 2, 'FT', 'FR' from dual union all
       select 104, 1, 1, 'FR', 'FR' from dual
     )
select empl_id, from_pay_basis_id, to_pay_basis_id, from_category, to_category,
       case when from_category != to_category then
                 case when from_pay_basis_id != to_pay_basis_id then 'Assignment Category and Pay Basis'
                      else                                     'Assignment Category'
                 end
            when from_pay_basis_id != to_pay_basis_id then 'Pay Basis'
       end as change_type
from   query_output;

EMPL_ID FROM_PAY_BASIS_ID   TO_PAY_BASIS_ID FROM_CATEGORY   TO_CATEGORY CHANGE_TYPE
------- ----------------- --------------- ------------- ----------- ---------------------------------
101                     1               2 FR            FR          Pay Basis
102                     1               1 FT            FR          Assignment Category
103                     1               2 FT            FR          Assignment Category and Pay Basis
104                     1               1 FR            FR

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM