views:

104

answers:

5

I have a list of orders with suboperations. How can I create a list of finished orders? Finished order must have finished all suboperations.

Table "orders":

order_no | suboperation | finished
1        | preparing    | 01/01/2009
1        | scrubbing    | 01/05/2009
1        | painting     | 01/10/2009
2        | preparing    | 02/05/09
2        | painting     | NULL
3        | preparing    | 03/01/2009
3        | scrubbing    | 03/15/2009
3        | painting     | 03/10/2009
4        | bending      | NULL
4        | crashing     | NULL
4        | staining     | NULL
4        | painting     | NULL

Desired output (finished orders):

order_no
1
3
+1  A: 

A good ol' WHERE NOT EXISTS clause ought to work here:

SELECT DISTINCT o.order_no
FROM orders o
WHERE NOT EXISTS (SELECT p.order_no
                  FROM orders p
                  WHERE p.order_no = o.order_no 
                  AND p.finished IS NULL)
lc
This will repeat the order number. You could fix this by making it SELECT distinct o.order_no
pjp
@ pjp - Thanks for spotting that.
lc
A: 
SELECT order_no, suboperation, finished
FROM orders o1
WHERE NOT EXISTS(
 SELECT 1 
  FROM orders o2 
  WHERE o1.order_no = o2.order_no 
  AND o2 IS NULL )
Dewfy
What's the o2 IS NULL. I think this is an error.
pjp
fix 'AND o2.finished IS NULL'Also this is returning a row for each of the subtasks.
pjp
IS NULL selects all unclosed orders, that is why this construct framed by NOT EXISTS
Dewfy
+3  A: 

You'll could also use count, group by and having. This avoids having to do any table joins which is more efficient.

create table #Orders (
    order_no int,
    suboperation varchar(30),
    finished smalldatetime)

insert into #Orders values (1 , 'preparing' , '01/01/2009')
insert into #Orders values (1 , 'scrubbing' , '01/05/2009')
insert into #Orders values (1 , 'painting' , '01/10/2009')
insert into #Orders values (2 , 'preparing' , '02/05/09')
insert into #Orders values (2 , 'painting' , NULL)
insert into #Orders values (3 , 'preparing' , '03/01/2009')
insert into #Orders values (3 , 'scrubbing' , '03/15/2009')
insert into #Orders values (3 , 'painting' , '03/10/2009')
insert into #Orders values (4 , 'bending' , NULL)
insert into #Orders values (4 , 'crashing' , NULL)
insert into #Orders values (4 , 'staining' , NULL)
insert into #Orders values (4 , 'painting' , NULL)

select 
    order_no, 
    count(1) As NoOfSubtasks --count(1) gives the number of rows in the group
    count(finished) As NoFinished, --count will not count nulls
from #Stuff
group by 
     order_no
having 
    count(finished) = count(1) --if finished = number of tasks then it's complete

drop table #Orders
pjp
Thank you a lot. This one is excellent :-)
A: 
SELECT o.order_no
FROM Orders AS o
GROUP BY o.order_no
HAVING (((Sum(IsNull(o.finished)))=0))
ORDER BY o.order_no;
HansUp
Is it not the case that when you group by a field, the result is by default ordered by that field, so that your ORDER BY is redundant?
David-W-Fenton
I understood that ordering is not guaranteed with GROUP BY, so I explicitly set the ORDER. Now you got me wondering if I just dreamed that up.
HansUp
I think 'guaranteed' is the operative word here i.e. I don't think ordering is guaranteed unless an ORDER BY clause is used. Regardless, it makes clear to the human reader the intent of the query. (Of course there may be bugs in the engine -- e.g. http://support.microsoft.com/kb/837148 -- that will cause the sort order to be wrong but at least the wrongness is guaranteed!)
onedaywhen
In limited searching, the only reference I found was for SQL Server. "The GROUP BY clause does not order the result set. Use the ORDER BY clause to order the result set." technet.microsoft.com/en-us/library/ms177673.aspx But I couldn't find a similar reference for Jet/ACE.
HansUp
A: 

I was trying to find a way to use the MIN function, and came up with this:

SELECT order_no

FROM Orders

GROUP BY order_no

HAVING (MIN(ISNULL(finished, 0)) <> 0)

null can be annoying...

not as performant, maybe, but easier for me to understand.

Beth
This one look good too but I had to change it for Acccess.SELECT order_noFROM OrdersGROUP BY order_noHAVING (MIN(ISNULL(finished)) = 0);