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.
答案 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.