简体   繁体   中英

Oracle SQL - Multiplewhere conditions based on each other from the same table

I have a single table ledrow Which has 4 columns.

I want to only select rows where they have (per vnum and comp ) rows that are in both sets of logic.

English Explanation


So for example out of all the data below, explaining the rows in the expected output is as follows:

For comp 99 we have results as all of the vacc numbers fall into either of the sets of logic:

The vacc is either BETWEEN '1544' AND '1567' OR the vacc is BETWEEN '3000' AND '3999'

However for example comp 77 rows aren't output because even though:

comp vtype  vnum    vacc
77   F      1369    3400
77   F      1369    3402

Comes under the second part of the logic the rest of the rows don't fall under any:

comp vtype  vnum    vacc
77   F      1369    1510
77   F      1369    2620
77   F      1369    2620
77   F      1369    2620
77   F      1369    1650

This is because to be part of the output I want all rows per comp MUST fall in either parts of the logic per comp.


I have tried the following code but cannot get my expected output.

Current Code:

SELECT * FROM (
SELECT comp, vtype, vnum, vacc
FROM ledrow
WHERE 
((comp = '55' AND vacc BETWEEN '1544' AND '1567') AND (comp = '55' AND (vacc = '3019' OR vacc = '5222')))
OR
((comp = '66' AND vacc BETWEEN '1544' AND '1567') AND (comp = '66' AND (vacc  = '3013' OR (vacc BETWEEN '6910' AND '6973'))))
OR
((comp NOT IN ('55', '66') AND vacc BETWEEN '1544' AND '1567') AND (comp NOT IN ('55', '66') AND (vacc BETWEEN '3000' AND '3999')))) outputled
WHERE outputled.vtype = 'F'

Expected Output:

comp vtype  vnum    vacc
99   F      1369    1564
99   F      1369    2610
99   F      1369    2610
99   F      1369    3601
99   F      1369    3600

Full Table ( SELECT * FROM ledrow ):

ledrow

comp vtype  vnum    vacc
77   F      1369    1510
77   F      1369    2620
77   F      1369    2620
77   F      1369    2620
77   F      1369    3400
77   F      1369    3402
77   F      1369    1650
99   F      1369    1564
99   F      1369    2610
99   F      1369    2610
99   F      1369    3601
99   F      1369    3600
99   I      1369    2450
99   I      1369    2440
99   I      1369    2640
99   J      1369    5430
99   J      1369    2450
99   J      1369    5430
99   J      1369    2455
99   J      1369    5410
99   J      1369    2455
99   J      1369    5410
22   F      1369    2620
22   F      1369    3500
22   F      1369    2495
22   F      1369    1510
22   F      1369    2620
22   F      1369    2620
22   F      1369    2620
22   F      1369    2620
22   F      1369    2620
22   F      1369    2620
22   F      1369    2620
22   F      1369    2620
22   F      1369    2620
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    3500
22   F      1369    2495
22   F      1369    3500
22   F      1369    2495
22   F      1369    3500
22   F      1369    2495
22   F      1369    3500
22   F      1369    2495
22   F      1369    3500
22   F      1369    2495
22   F      1369    3500
22   F      1369    2495
22   F      1369    3500
22   F      1369    2495
22   F      1369    3500
22   F      1369    2495
22   F      1369    3500
22   F      1369    2495
22   I      1369    2450
22   I      1369    2440
22   I      1369    2640
22   J      1369    5520
22   J      1369    5520
22   J      1369    2455
22   J      1369    2450

Edit

So for each vnum and comp it needs to satisfy both of the two conditions in the WHERE clause.

So for comp 99 which has to satisfy both the conditions:

((comp NOT IN ('55', '66') AND vacc BETWEEN '1544' AND '1567') AND (comp NOT IN ('55', '66') AND (vacc BETWEEN '3000' AND '3999'))))

If I understood what you were trying to do, then this should work :

    WHERE comp = '55' AND (vacc between '1544' and '1567' or vacc in('3019','5222')) OR
          (comp = '66' AND (vacc between '1544' and '1567' or vacc between '6910' AND '6973' or vacc = '3013')) OR
          (comp not IN('55','66') and (vacc BETWEEN '1544' AND '1567' or vacc BETWEEN '3000' AND '3999'))

You specified each of the first conditions twice, which is not necessary as they are correct to both of the next conditions.

(comp not IN('55','66') AND Cond1) AND/OR (comp not IN('55','66') AND Cond2)

Equals to:

comp not IN('55','66') AND (Cond1 AND/OR Cond2)

EDIT: You can try something like this:

SELECT tt.* FROM (
    SELECT t.comp FROM (
        SELECT comp,
               CASE WHEN comp = '55' AND vacc BETWEEN '1544' AND '1567' THEN 1
                    WHEN comp = '55' AND vacc in('3019','5222') THEN 2
                    WHEN comp = '66' AND vacc BETWEEN '1544' AND '1567' THEN 3
                    WHEN comp = '66' AND (vacc  = '3013' OR (vacc BETWEEN '6910' AND '6973')) THEN 4
                    WHEN comp NOT IN ('55', '66') AND vacc BETWEEN '1544' AND '1567' THEN 5
                    WHEN comp NOT IN ('55', '66') AND (vacc BETWEEN '3000' AND '3999') THEN 6
                    ELSE 7
                END as ind_col
        FROM ledrow
        WHERE vtype = 'F') t
    GROUP BY t.comp 
    HAVING (MAX(t.ind_col) = 2 and min(t.ind_col) = 1) OR
           (MAX(t.ind_col) = 4 and min(t.ind_col) = 3) OR
           (MAX(t.ind_col) = 6 and min(t.ind_col) = 5)) s
INNER JOIN ledrow tt
 ON(s.comp = tt.comp)
WHERE tt.vtype = 'F'

This will basically give an indication to each row, first condition is 1,2 corresponding to the part of this condition the satisfied , second one is 3,4 and third is 5,6 . Then you check that each group only has 1+2 or 3+4 or 5+6 with the HAVING clause with MAX() and MIN() which will assure that this comp satisfy both conditions.

This solution works only for rows that satisfy both conditions, not only one of them. If only one of them is enough, that change the having clause to this:

    HAVING (MAX(t.ind_col) = 2 and min(t.ind_col) = 1) OR
           (MAX(t.ind_col) = 4 and min(t.ind_col) = 3) OR
           (MAX(t.ind_col) = 6 and min(t.ind_col) = 5) OR
           (MAX(t.ind_col) = min(t.ind_col) and min(t.ind_col) IN(1,2,3,4,5,6)

May be I have understood what you want :)

We make every special conditions per comp (group by comp) like a bit in a byte (1,2,4,8) and then add them. Than we choose only the combination what we want. (1+2=3, 1+4=5, 1+8=9)

SELECT *
  FROM ledrow
 WHERE vtype = 'F'
   AND comp IN 
      (SELECT comp
         FROM (  SELECT comp,
                        MAX (CASE WHEN (vacc BETWEEN '1544' AND '1567') THEN 1 ELSE 0 END)
                        + MAX (CASE WHEN (comp = '55' AND vacc IN ('3019', '5222')) THEN 2 ELSE 0 END)
                        + MAX (CASE WHEN (comp = '66' AND (vacc = '3013' OR vacc BETWEEN '6910' AND '6973')) THEN 4 ELSE 0 END)
                        + MAX (CASE WHEN (comp NOT IN ('55', '66') AND vacc BETWEEN '3000' AND '3999') THEN 8 ELSE 0 END)
                           x
                   FROM ledrow
                  WHERE vtype = 'F'
               GROUP BY comp)
        WHERE bitand(x,3) = 3 OR bitand(x,5) = 5 OR bitand(x,9) = 9)

Another modification with analytic function MAX() OVER () , but the same logic

SELECT comp, vtype, vnum, vacc
  FROM (SELECT x.*,
               MAX (CASE WHEN (vacc BETWEEN '1544' AND '1567') THEN 1 ELSE 0 END) OVER (PARTITION BY comp) c1,
               MAX (CASE WHEN (comp = '55' AND vacc IN ('3019', '5222')) THEN 1 ELSE 0 END) OVER (PARTITION BY comp) c2,
               MAX (CASE WHEN (comp = '66' AND (vacc = '3013' OR vacc BETWEEN '6910' AND '6973')) THEN 1 ELSE 0 END) OVER (PARTITION BY comp) c4,
               MAX (CASE WHEN (comp NOT IN ('55', '66') AND vacc BETWEEN '3000' AND '3999') THEN 1 ELSE 0 END) OVER (PARTITION BY comp) c8
          FROM testtest2 x
         WHERE vtype = 'F')
 WHERE (c1 = 1 AND c2 = 1)
    OR (c1 = 1 AND c4 = 1)
    OR (c1 = 1 AND c8 = 1)

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