1

I have this query that will return correctly the values that are part of the IN clause, but when I change it to NOT IN, it does not return anything.
Does anyone have any suggestions?

select distinct 
    CAST( w.work_area AS CHAR(4) ) || s.code_id as WATT 
from 
    sys_code s, 
    work_area_master w 
where 
    s.code_type = '590' 
    and (
        CAST( w.work_area AS CHAR(4)) || s.code_id
    )  
    in (
        select substr(misc_flags, 1,6) 
        from sys_code where code_type = 'STA'
    );
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228

4 Answers4

1

The Exclusion Principle (ie, that A or ~A is a tautology) does NOT apply whenever A may be NULL. When nullable fields exist, three valued loigc applies and the Exclusion Principle must be modified to ( A or ~A or A is NULL).

Pieter Geerkens
  • 11,775
  • 2
  • 32
  • 52
1
SELECT DISTINCT
    CAST( w.work_area AS CHAR(4) ) || s.code_id AS what 
FROM sys_code s
JOIN work_area_master w ON 1=1 
WHERE s.code_type = '590' 
AND EXISTS (
    SELECT * FROM sys_code xx
    WHERE xx.code_type = 'STA'
    AND substr(xx.misc_flags, 1,6) = CAST( w.work_area AS CHAR(4)) || s.code_id
    );

Footnote: I deliberately used the JOIN ... ON 1=1 syntax to draw attention to the fact that the original query did not even have a join condition (except for the one in the correlated IN subquery)

wildplasser
  • 43,142
  • 8
  • 66
  • 109
0

The check whether the combination of sys_code and work_area_master is valid can actually be the JOIN condition.

Also using EXISTS instead of IN to avoid problems with NULL like @wilplasser already provided:

SELECT DISTINCT
       CAST( w.work_area AS CHAR(4) ) || s.code_id AS what 
FROM   sys_code s
JOIN   work_area_master w ON EXISTS (
          SELECT 1
          FROM   sys_code x
          WHERE  x.code_type = 'STA'
          AND    substr(x.misc_flags, 1,6)
                 = CAST(w.work_area AS CHAR(4)) || s.code_id
          )
WHERE  s.code_type = '590' ;

For a detailed explanation of the problem with NOT IN and NULL, refer to this closely related question on dba.SE.

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
-1

One could include a coalesce function like this:

select distinct CAST( w.work_area AS CHAR(4) ) || s.code_id as WATT
from sys_code s, work_area_master w
where s.code_type = '590' 
and (CAST( w.work_area AS CHAR(4)) || s.code_id) in (
select substr(coalesce(misc_flags,"    "), 1,6)
from sys_code
where code_type = 'STA'
);
Octopus
  • 144
  • 9
  • 1
    Do not encourage the use of a SQL antipattern of implicit joins. In this case it appears he has a cross join which may or may not be what he wants and the next person to maintain this won't know if it was a bug or intentional. This is especially dangerous if a later change adds and addtional code_type. – HLGEM Mar 13 '13 at 19:08
  • I agree with you HLGEM. However, some crossjoins can be fully implemented if this is the deliberate intention of the programmer. That's why I firmly believe one of the best practices is to comment on code, rather than letting someone else understand what was the author's intent on creating such a code. – Octopus Mar 13 '13 at 19:12
  • 2
    If you use explict joins you can only have a cross join if you write one. That is one of the reasons why the implict join is an extremely poor idea if you intend one as your intentinos are not clear. Even if you comment, comments are so often out of date that many devs won't realize it was still pertinent. If you use code, the intention is always clear. – HLGEM Mar 13 '13 at 19:17
  • Brilliant HLGEM! Thanks for pointing that. I'll look forward on my own code to avoid such a trap. – Octopus Mar 13 '13 at 19:29