SQL SERVER – 2005 Comparison EXCEPT operator vs. NOT IN

The EXCEPT operator returns all of the distinct rows from the query to the left of the EXCEPT operator when there are no matching rows in the right query. The EXCEPT operator is equivalent of the Left Anti Semi Join. EXCEPT operator works the same way NOT IN. EXCEPTS returns any distinct values from the query to the left of the EXCEPT operand that do not also return from the right query.

Example of EXCEPT operator is displayed along Example of NOT IN. If you run both of the Query and compare the Execution plan it is exactly the same. EXCEPT and NOT IN does same functions and have the same execution plan, but EXCEPT has much simpler syntax. The row-by-row comparison provided by EXCEPT, combined with the number of rows being returned remaining consistent, provides compelling evidence that re-factored query is correct. EXCEPT works with * as well as aliases.

Example: (Both of the scripts returns the same number of rows)

----SQL SERVER 2005 Method
USE AdventureWorks;
GO
SELECT ProductID
FROM Production.Product
EXCEPT
SELECT ProductID
FROM Production.WorkOrder; 
GO

----SQL SERVER 2000 Method, which works&nbspIN SQL SERVER 2005
USE AdventureWorks;
GO
SELECT ProductID
FROM Production.Product
WHERE ProductID
NOT IN (
SELECT ProductID
FROM Production.WorkOrder);
GO

Following the execution plan which indicates EXCEPT and NOT IN uses same execution plan.

SQL SERVER - 2005 Comparison EXCEPT operator vs. NOT IN SQLSERVER_EXCEPT_ExecPlan_small

Reference : Pinal Dave (https://blog.sqlauthority.com)

SQL Index, SQL Operator, SQL Scripts, SQL Server
Previous Post
SQL SERVER – Top 15 free SQL Injection Scanners – Link to Security Hacks
Next Post
SQLAuthority News – T-Shirts in Action

Related Posts

63 Comments. Leave new

  • i am having to retrive multipul condition any one can help regading this problem.thanks for advance

    Reply
  • how to retrive the result set i want to search

    Reply
  • i want to search from the result table

    Reply
  • Shrikant Patil
    August 21, 2009 8:32 pm

    Hey pinal i read your blog a lot but never commented or asked question . This is the first time .

    What i came across is this
    i have a table with around 90000 rows .
    45000 where source_flag=1 and 45000 where target_flag=1

    i was using a not in query like this

    SELECT value FROM die_domain_log WHERE source_flag =1 AND log_id=103
    NOT IN ( SELECT i.value FROM die_domain_log i WHERE i.target_flag =1 AND i.log_id=103 )

    When executed it didnt returned any results for like 15-20 mins i stoped it .

    Then i changed it to except it took less than a sec to return the result .

    SELECT value FROM die_domain_log WHERE source_flag =1 AND log_id=103
    EXCEPT
    SELECT i.value FROM die_domain_log i WHERE i.target_flag =1 AND i.log_id=103

    why is this happening ?

    Reply
  • Imran Mohammed
    August 23, 2009 9:24 am

    @Shrikanth Patil.

    Your first select statement is not valid. I am not sure if it could ever execute, because you are not specific about what to be search for NOT IN.

    Anyways below is a working example, consider this example and let us know if this is what you were expecting.

    Example Script :

    create table #die_domain_log ( Value int identity , source_flag int , target_flag int, log_id int )

    insert into #die_domain_log values ( 1, NULL , 103)
    insert into #die_domain_log values ( 1, NULL , 103)
    insert into #die_domain_log values ( 1, NULL , 103)
    insert into #die_domain_log values ( 1, NULL , 103)
    insert into #die_domain_log values ( 1, NULL , 103)

    insert into #die_domain_log values ( NULL , 1, 103)
    insert into #die_domain_log values ( NULL , 1, 103)
    insert into #die_domain_log values ( NULL , 1, 103)
    insert into #die_domain_log values ( NULL , 1, 103)
    insert into #die_domain_log values ( NULL , 1, 103)

    Select * from #die_domain_log

    — Here you need to be specific what to search for NOT IN, I have include Value as a search criteria for NOT IN.

    SELECT value
    FROM #die_domain_log
    WHERE source_flag =1
    AND log_id=103
    AND value
    NOT IN
    (
    SELECT i.value
    FROM #die_domain_log i
    WHERE i.target_flag =1
    AND i.log_id=103
    )

    — OR ( below is same as you posted)

    SELECT value FROM #die_domain_log WHERE source_flag =1 AND log_id=103
    EXCEPT
    SELECT i.value FROM #die_domain_log i WHERE i.target_flag =1 AND i.log_id=103

    ~ IM.

    Reply
  • Shrikant Patil
    August 24, 2009 5:55 pm

    Sorry i forgot to add where value IN in the above post . In actual ‘m using the same query u have return it works for small number of rows also . but when i have 90000 rows as i said above it doesnt work the way it should .

    Reply
  • Can any one help me with such a query below.

    SELECT DISTINCT Building WHERE Building IS NOT NULL
    AND Building NOT LIKE ‘%School%’
    AND Building NOT LIKE ‘%Church%’
    AND Building NOT LIKE ‘%Rectory%’
    AND Building NOT LIKE ‘%Public House%’
    AND Building NOT LIKE ‘%Retail Park%’
    AND Building NOT LIKE ‘%Railway Station%’
    AND Building NOT LIKE ‘%Post Office%’
    AND Building NOT LIKE ‘%Court%’
    AND Building NOT LIKE ‘%Floor%’
    AND Building NOT LIKE ‘%Flat%’
    AND Building NOT LIKE ‘%Former%’
    AND Building NOT LIKE ‘%Lodge%’

    basically as per my requirement, the list in the not like might increase daily, I dont find the query good with the whole list as above. Is there a way I can avoid it.

    Please suggest.

    Reply
  • @Sucharitha

    Ideally there should be a type field instead of searching a name for the type.

    If there is a list that will be managed daily, perhaps another TABLE would be best. Then the query could use a join and not have to change.

    Reply
  • Hello Brian,

    Thank you for the suggestion which was obviously in my mind. I am new into the team and company, and the application has been built and stabilised. I need to study it better before i make any changes to the DB.
    But at this time, I have been asked to get a quick report where I need to filter on something like this. Is there any other quick solution running in your mind?

    Reply
  • @Sucharitha

    I don’t know of any. (Though i am not an expert.)

    It seems that every field would need to be scanned, resulting in a full table scan. Indexing won’t even help because it would be indexing off the description and not the part that you want.

    Reply
  • Thanks for sharing the needed information!

    Reply
  • Is there script in SQL SERVER to support multi fields like Oracle in “WHERE (col1,col2) NOT IN(…)” ?

    Here is the script I want to run bot not support in SQL Server. It works in Oracle. [INSTITUTION],[CODE] ,[EFF_DATE] are composite primary key.

    SELECT *
    from DDEF_CRS_Stage
    where ([INSTITUTION]
    ,[CODE]
    ,[EFF_DATE])
    NOT IN
    (
    select [INSTITUTION]
    ,[CODE]
    ,[EFF_DATE] from DDEF_CRS_Stage
    group by [INSTITUTION]
    ,[CODE]
    ,[EFF_DATE]
    Having COUNT(*) > 1
    )

    Thank you.

    Reply
    • @david

      Like Pinal said, make it an EXISTS. I’d like to add, that the NOT IN (or NOT EXISTS) can be IN (or EXISTS) as it can be checked to = 1 instead of being NOT > 1.

      However, the best of all is to use neither. Since the query limits the results to one record per group (as any groups with multiple records are excluded) using aggregates will have no effect on the results:

      SELECT
      [INSTITUTION],
      [CODE],
      [EFF_DATE],
      MAX(Col1),
      MAX(Col2),
      MAX(Col3)
      FROM
      DDEF_CRS_Stage
      GROUP BY
      [INSTITUTION],
      [CODE]
      [EFF_DATE]
      HAVING
      COUNT(*) = 1;

      Reply
  • Hello David,

    Multiple columns in single where clause is not supported in SQL Server. You have to write a saperate NOT IN clause fora each column.
    Another better way to perform the same thing is using NOT EXISTS clause where you would not have to write saperate subquery for each column.

    Kind Regards,
    Pinal Dave

    Reply
  • Hi Sucharitha,

    Eevery time your are checking with only one column, “building”. So create a separate table with a single column. ex tblBuildingList(strBuilding VARCHAR(100).

    INSERT INTO tblBuildingList(strBuilding) VALUES(%School%)
    INSERT INTO tblBuildingList(strBuilding) VALUES(%Church%)
    …….

    select Building from yourTableName tbl
    INNER JOIN tblBuildingList bld ON
    tbl.Building NOT LIKE bld.strBuilding

    I think it ll solve your problem, bcz no need to change the query when ever adding new list. just insert the list value into the tblBuildingList.

    Thanks

    Reply
  • Hi Brian and Pinal

    Your guys are rocks!

    I like the solution such as
    SELECT
    [INSTITUTION],
    [CODE],
    [EFF_DATE],
    MAX(Col1), –solve the problem with column not group by
    MAX(Col2),
    MAX(Col3)
    FROM
    DDEF_CRS_Stage
    GROUP BY
    [INSTITUTION],
    [CODE]
    [EFF_DATE]
    HAVING
    COUNT(*) = 1;

    The following code, I have to use table variables to solve the same problem in SQL Server.

    declare @tbl table([INSTITUTION] varchar(4)
    ,[CODE] varchar(20)
    ,[EFF_DATE] datetime);

    Insert into @tbl
    select [INSTITUTION]
    ,[CODE]
    ,[EFF_DATE] from DDEF_CRS_Stage
    group by [INSTITUTION]
    ,[CODE]
    ,[EFF_DATE]
    Having COUNT(*) > 1;

    select * from DDEF_CRS_Stage
    EXCEPT
    (
    select * from DDEF_CRS_Stage
    where [INSTITUTION] IN
    (select [INSTITUTION] from @tbl)
    AND [CODE] IN
    (select [Code] from @tbl)
    AND [EFF_DATE] IN
    (select [EFF_DATE] from @tbl)
    )

    Have a fun with your guys.

    David

    Reply
  • –following except is not working

    select ad.code ,ad.description, count (di.cod_acct_no)as “No of Cards” from account_master d, accountinfo di, branch ad
    where d.p_acct_no=di.cod_acct_no and ad.branchid=di.branchid
    and di.dat_acct_open between ‘2010-02-27 00:00:00’
    and ‘2010-03-11 00:00:00’
    and d.iss_date between ‘2010-02-27 00:00:00’
    and ‘2010-03-11 23:59:00’
    and accounttypeid in (1,2,3,55)
    group by ad.code, ad.description
    order by ad.code, ad.description
    EXCEPT
    select ad.code ,ad.description, count (di.cod_acct_no)as “No of Cards” from account_master d, accountinfo di, branch ad
    where d.p_acct_no=di.cod_acct_no and ad.branchid=di.branchid
    and di.dat_acct_open between ‘2010-02-20 00:00:00’
    and ‘2010-03-11 00:00:00’
    and d.iss_date between ‘2010-02-20 00:00:00’
    and ‘2010-03-11 23:59:00’
    and accounttypeid in (1,2,3,55)
    group by ad.code, ad.description
    order by ad.code, ad.description

    Reply

  • how to use IN Operater in sQl procedure

    tel if u can………………..

    this is query??????????

    Reply
  • Fatih DoÄŸanay
    June 14, 2010 7:22 pm

    Hi;
    any opinion about why the first query returns data but the second and third query not return data?

    select 1 where 1 in (null, 1, 2, 3);
    select 1 where 10 not in (null, 1, 2, 3);
    select 1 where not 10 in (null, 1, 2, 3);

    Reply
    • Hi,

      This is due the Ansi_nulls property ON.
      In the first statement
      select 1 where 1 in (null, 1, 2, 3);
      The compiler take the value 1 and compares whether it exists in the “in ” list(null,1,2,3) and outputs ‘1’.

      In 2nd stmt

      select 1 where 10 not in (null, 1, 2, 3); the compiler takes the value 10 and tries to compare with null. sinc ethe Ansi_nulls is set On it will not equate to null and hence returns no result set.

      In 3rd statement

      Similar to the 2 nd statement.
      When you turn off the set Ansi_nulls as ‘OFF’ all the 3 select staement will return 1 as value.

      Reply
  • What should i do if i want to return only the columns that do not match for 2 tables ?

    Any help is appreciated.

    Reply
  • Hi all,

    I want to compare two tables from one databases,
    how do i compare both the tables,such that.when compared with table’s columns if any records of column is updated in the either of two table’ columns,it should return me updated records.
    (hints Table1 is old and Table2 to is new updated one and columns are same name as well as data type)

    please help me.

    Reply

Leave a Reply