EXISTS and less than producing different result from NOT EXISTS and greater than

时间:2015-08-07 02:37:05

标签: sql sql-server exists sql-server-2014 not-exists

I'm using SQL Server 2014 Express in the AdventureWorks2012 database, working through a T-SQL textbook to learn. I'm doing the following exercise:

Delete the rows from the dbo.demoCustomer table if the sum of the TotalDue     
from the dbo.demoSalesOrderHeader table for the customer is less than $1,000. 

I came up with the following answer using a CTE. It deletes 5200 rows from the table.

;with broke as
   (select c.customerid
   , sum(soh.totaldue) 'custtotal'
   from democustomer c
   inner join demosalesorderheader soh on soh.customerid = c.customerid
   group by c.customerid
   having sum(soh.totaldue) < 1000)
delete c
from democustomer c
where exists
   (select *
   from broke b
   where b.customerid = c.customerid);

I check the texbook's answer and it gives the following. Unlike my query, it deletes 5696 rows, 496 more than my own.

delete c
from dbo.democustomer c
where not exists
   (select *
   from dbo.demosalesorderheader soh
   where c.customerid = soh.customerid
   group by soh.customerid
   having sum(totaldue) >=1000);

If I turn my method around and use not exists with having sum(totaldue) >= 1000, it also produces 5696 rows. What I'm not understanding is why the queries are producing different results - shouldn't EXISTS and < 1000 produce the same results as NOT EXISTS and >=1000?

I looked at the 496 rows that show up in the NOT EXISTS, >=1000 version and determined that those customerid's don't exist in the salesorderheader table (ie they didn't place an order). But it still doesn't make sense why the NOT EXISTS version would capture this while the EXISTS doesn't? Appreciate any help.

1 个答案:

答案 0 :(得分:2)

This is too long for a comment.

You understand the issue, which is NULL/missing values in demosalesorderheader.

Your version, where you say exists, is requiring that the customer both exist in the table and have a sum less than 1000.

The alternative version does not have a condition on the existence of the customer in the table. Presumably, the default value is 0.

You could alter your query to get the same result:

with broke as (
     select c.customerid, coalesce(sum(soh.totaldue), 0) as custtotal
     from democustomer c left join
          demosalesorderheader soh
          on soh.customerid = c.customerid
     group by c.customerid
     having coalesce(sum(soh.totaldue), 0) < 1000
    )

The left join will keep all the customers.