views:

78

answers:

4

Ok this one is realy tricky :D

i have a this table

bills_products:
- bill_id - product_id - action -
|    1    |      4     |   add  |
|    1    |      5     |   add  |
|    2    |      4     | remove |
|    2    |      1     |   add  |
|    3    |      4     |   add  |

as you can see product with the id 4 was added at bill 1 then removed in bill 2 and added again in bill 3

All Bills belong to a bill_group. But for the simplicity sake let's assume all the bills are in the same group.

Now i need a SQL Query that shows all the products that are currently added at this group.

In this example that would be 5, 1 and 4. If we would remove the bill with id 3 that would be 5 and 1

I've tried to do this with DISTINCT but it's not powerful enough or maybe I'm doing it wrong.

A: 
SELECT DISTINCT product_id FROM bills_products WHERE action = 'add';
GSto
this wont work, if you remove the bill 3, it will show you the product_id 4, but it was removed in bill 2.
antpaw
ah, I misunderstood the question.
GSto
+3  A: 

This seems to work in SQL Server at least:

select  product_id
from    (
            select product_id,
                   sum((case when action='add' then 1 else -1 end)) as number
            from   bills_products
            group by product_id
        ) as counts
where   number > 0
Rich
Assuming you've already protected against multiple remove events, e.g., "add, remove, remove, add".
Tim Sylvester
It's true that I assumed that they couldn't remove things that had already been removed.
Rich
Why? It should even work with multiple removes or do I miss something?
Felix Kling
wow thank you! i didn't hard code the action string to this table (it was just for better demonstration), it's actually a fk_action_id. Can you show me how to do a JOIN to get the action string?
antpaw
Suppose someone removes an item twice and then adds it once. The sum would be -1 and the item wouldn't be listed, which it should be if the minimum number is 0.
Rich
You're welcome.To deal with the case with the actions in a separate table, you'd just need to turn the bills_products into an inner join on action_id and add the appropriate table specifier (or alias) in front of the action='add'.
Rich
As far as I understood it, an item should only be shown if there are more 'add's than 'remove's but ok, it is not my code ;) (i.e. your solution gives the right results for my understanding of the problem, that is enough for me). Thank you.
Felix Kling
A: 

GSto almost had it, but you have to ORDER BY bill_id DESC to ensure you get the latest records.

SELECT DISTINCT product_id FROM bills_products
WHERE action = 'add'
ORDER BY bill_id DESC;

(P.S. I think most people would say it's a best practice to have a timestamp column on tables like this where you need to be able to know what the "newest" row is. You can't always rely on ids only ascending.)

Jordan
A: 

REMOVED BY AUTHOR

Erik