hurit
hurit

Reputation: 21

Excluding records from table based on rules from another table

I'm using Oracle SQL and I have a product table with diffrent attributes and sales volume for each product and another table with certain exclusion rules for different level of aggregation. Let's look at the example:

Here is our main table with sales data on which we want to perform some calculations:

enter image description here

And the other table contains diffrent rules which are supposed to exclude certain rows from table above:

enter image description here

When there is an "x", this column shouldn't be considered so our rules are: 1. exclude all rows with ATTR_3 = 'no' 2. exlcude all rows with ATTR_1 = 'Europe' and ATTR_2 = 'snacks' and ATTR_3 = 'no' 3. exlcude all rows with ATTR_1 = 'Africa'

And based on that our final output should be like that:

enter image description here

How this could be achived in SQL? I was thinking about join but I have no idea how to handle different levels of aggregation for exclusions.

Upvotes: 0

Views: 102

Answers (4)

Gordon Linoff
Gordon Linoff

Reputation: 1269563

I would do this with three different not exists:

select p.*
from product p
where not exists (select 1
                  from rules r
                  where r.attr_1 = p.attr_1 and r.attr_1 <> 'x'
                 ) and
      not exists (select 1
                  from rules r
                  where r.attr_2 = p.attr_2 and r.attr_2 <> 'x'
                 ) and
      not exists (select 1
                  from rules r
                  where r.attr_3 = p.attr_3 and r.attr_3 <> 'x'
                 ) ;

In particular, this can take advantage of indexes on (attr_1), (attri_2) and (attr_3) -- something that is quite handy if you have a moderate number of rules.

Upvotes: 0

Barbaros &#214;zhan
Barbaros &#214;zhan

Reputation: 65218

You can use NOT EXISTS

SELECT *
  FROM sales s
 WHERE NOT EXISTS ( 
                   SELECT 0
                     FROM attributes a
                    WHERE ( ( a.attr_1 = s.attr_1 AND a.attr_1 IS NOT NULL ) 
                       OR     a.attr_1 IS NULL )
                      AND ( ( a.attr_2 = s.attr_2 AND a.attr_2 IS NOT NULL ) 
                       OR     a.attr_2 IS NULL )
                      AND ( ( a.attr_3 = s.attr_3 AND a.attr_3 IS NOT NULL ) 
                       OR     a.attr_3 IS NULL )
                  )

where I considered the x values within the attributes table as NULL. If you really have x characters, then you can use :

SELECT *
  FROM sales s
 WHERE NOT EXISTS ( 
                   SELECT 0
                     FROM attributes a
                    WHERE ( ( NVL(a.attr_1,'x') = s.attr_1 AND NVL(a.attr_1,'x')!='x' ) 
                       OR     NVL(a.attr_1,'x')='x' )
                      AND ( ( NVL(a.attr_2,'x') = s.attr_2 AND NVL(a.attr_2,'x')!='x' ) 
                       OR     NVL(a.attr_2,'x')='x' )
                      AND ( ( NVL(a.attr_3,'x') = s.attr_3 AND NVL(a.attr_3,'x')!='x' ) 
                       OR     NVL(a.attr_3,'x')='x' )
                  )

instead.

Demo

Upvotes: 0

Littlefoot
Littlefoot

Reputation: 142705

I think your expected output is wrong. None of the rules excludes the 2nd row (Europe - snacks - yes).

SQL> with
  2  -- sample data
  3  test (product_id, attr_1, attr_2, attr_3) as
  4    (select 81928 , 'Europe', 'beverages', 'yes' from dual union all
  5     select 16534 , 'Europe', 'snacks'   , 'yes' from dual union all
  6     select 56468 , 'USA'   , 'snacks'   , 'no'  from dual union all
  7     select 129921, 'Africa', 'drinks'   , 'yes' from dual union all
  8     select 123021, 'Africa', 'snacks'   , 'yes' from dual union all
  9     select 165132, 'USA'   , 'drinks'   , 'yes' from dual
 10    ),
 11  rules (attr_1, attr_2, attr_3) as
 12    (select 'x'     , 'x'     , 'no' from dual union all
 13     select 'Europe', 'snacks', 'no' from dual union all
 14     select 'Africa', 'x'     , 'x'  from dual
 15    )
 16  -- query you need
 17  select t.*
 18  from test t
 19  where (t.attr_1, t.attr_2, t.attr_3) not in
 20    (select
 21       decode(r.attr_1, 'x', t.attr_1, r.attr_1),
 22       decode(r.attr_2, 'x', t.attr_2, r.attr_2),
 23       decode(r.attr_3, 'x', t.attr_3, r.attr_3)
 24     from rules r
 25    );

PRODUCT_ID ATTR_1 ATTR_2    ATT
---------- ------ --------- ---
     81928 Europe beverages yes
     16534 Europe snacks    yes
    165132 USA    drinks    yes

SQL>

Upvotes: 1

Popeye
Popeye

Reputation: 35900

You can use the join using CASE .. WHEN statement as follows:

SELECT P.* 
  FROM PRODUCT P
  JOIN RULESS R ON 
        (R.ATTR_1 ='X' OR P.ATTR_1 <> R.ATTR_1)
        AND (R.ATTR_2 ='X' OR P.ATTR_2 <> R.ATTR_2) 
        AND (R.ATTR_3 ='X' OR P.ATTR_3 <> R.ATTR_3) 

Upvotes: 0

Related Questions