SQL Union query simplification

时间:2015-07-28 15:46:09

标签: mysql sql optimization

I don't know if this question is apropriate on this forum.

I have a huge query :

SELECT threshold.id, brand.id, COUNT(brand.id), threshold
FROM current_stock, article, product, brand, delivery, threshold
    WHERE current_stock.article_id = article.id
    AND article.product_product_code = product.product_code
    AND product.brand_id = brand.id
    AND article.delivery_id = delivery.id
    AND delivery.store_id = 'E260'
    AND threshold.brand_id = brand.id
    GROUP BY brand.id
    HAVING COUNT(brand.id) <= threshold
UNION
SELECT threshold.id, brand.id, 0, threshold
FROM current_stock, article, product, brand, delivery, threshold
    WHERE threshold.store_id = 'E260'
    AND threshold.brand_id NOT IN (
        SELECT brand_id FROM current_stock, article, product, delivery
        WHERE current_stock.article_id = article.id
        AND article.product_product_code = product.product_code
        AND article.delivery_id = delivery.id
        AND delivery.store_id = 'E260')

And I think it's possible to do better but after a entire day of try I haven't found a better query giving the same result.

For clarify, I have a stock (with current_stock, article, product and delivery). I also have thresholds. what I want is to check for each thresholds if there is the given minimum amount of stock for the given brand.

My problem is that if there is 0 article of a brand, the first part of the query will not take care about the threshold on this brand. It's why I have added an uggly Union.

Someone have an idea for a better way to do this ?


EDIT

This what I have done after the reading of comments and answers :

SELECT t.id, b.id, t.threshold, count(b.id) stock

FROM threshold t
    inner join brand b on b.id = t.brand_id 
    left join product p on p.brand_id = b.id 
    inner join article a on a.product_product_code = p.product_code 
    inner join delivery d on d.id = a.delivery_id 
    inner join current_stock cs on cs.article_id = a.id

WHERE
    t.store_id = 'E260' AND
    d.store_id = 'E260'

GROUP BY b.id
HAVING stock <= t.threshold

My problem is that it don't gives all threholds... only ones that have at least one 'current_stock'. I have perhaps don't understand how joins are working.

Here an example of threshold table :

| id | brand_id | threshold |
-----------------------------
| 1  |   86     |     1     |
| 2  |   28     |     1     |
| 3  |   12     |     1     |

What I want as result this :

# with 2 entries in 'current_stock' for the brand id 28, 1 for 12 and 0 for 86

| t.id | b.id | threshold | stock |
-----------------------------------
|  1   |  86  |    1      |   0   |
|  3   |  12  |    1      |   1   |

1 个答案:

答案 0 :(得分:1)

Guessing a few parts here since you've used implicit joins in your sample. An explicit version would look something like this (provided I guessed correctly for how you are joining the threshold table).

SELECT
t.id,
b.id,
COUNT(b.id),
t.threshold

FROM
current_stock c
inner join article a on a.id = c.article_id
inner join delivery d on d.id = a.delivery_id
inner join product p on p.product_code = a.product_product_code
inner join brand b on b.id = p.brand_id
inner join threshold t on t.brand_id = b.id

WHERE
d.store_id = 'E260'

GROUP BY b.id
HAVING COUNT(b.id) <= t.threshold

Now to get your results to include rows where there aren't any 'articles' you can start switching out the inner joins for left joins. However, you can't simply use left outer join article... in the example above, because the store_id in the WHERE clause will just turn it back into a pseudo inner join.

Instead, is there a different field you can join the delivery table on from current_stock?

EDIT - 07/29/15

I think you're close, you may just have one too many filters and you're counting from 'b' when the wanted outcome suggests you should be counting from 'cs' instead. Try this:

SELECT t.id, b.id, t.threshold, count(cs.id) stock

FROM
threshold t
inner join brand b on b.id = t.brand_id
inner join product p on p.brand_id = b.id
inner join article a on a.product_product_code = p.product_code
left outer join delivery d on d.store_id = t.store_id
left outer join current_stock cs on cs.article_id = a.id

WHERE
t.store_id = 'E260'

GROUP BY b.id
HAVING stock <= t.threshold