我使用下表(使用MySQL)进行了设置:
orders
,有很多:order_items
,其中有一个来自:products
表我写了一个查询来选择orders
所有的products
都是某个type
:
SELECT orders.* FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
WHERE products.type = 'FooProduct'
AND (
NOT EXISTS (
SELECT null
FROM products
INNER JOIN order_items ON order_items.product_id = products.id
WHERE order_items.order_id = orders.id
AND products.type != 'FooProduct'
)
)
我运行类似的几次:首先获得由所有FooProduct
s组成的订单,并再次获得所有BarProduct
s的订单。
我的关键点是产生第三个查询以获得所有其他订单,即所有产品的类型不仅仅是FooProduct
s,或者只是BarProduct
s(也就是两者的混合或其他产品类型)。
所以,我的问题是如何获得所有产品类型不仅仅是FooProduct
s或BarProduct
的所有记录。
这是一个小例子数据,我想从中返回ID为3和4的订单:
- orders
id
1
2
3
4
-- order_items
id order_id product_id
1 1 1
2 1 1
3 2 2
4 2 2
5 3 3
6 3 4
7 4 1
8 4 2
-- products
id type
1 'FooProduct'
2 'BarProduct'
3 'OtherProduct'
4 'YetAnotherProduct'
我试过这个,非常好地放置作为一个潜台词,用以下代替现有的AND
(甚至语法都是关闭的):
NOT HAVING COUNT(order_items.*) = (
SELECT null
FROM products
INNER JOIN order_items ON order_items.product_id = products.id
WHERE order_items.order_id = orders.id
AND products.type IN ('FooProduct', 'BarProduct')
)
您可以使用Having
和基于条件聚合函数的过滤,而不是使用相关子查询。
如果产品类型不是,则products.type IN ('FooProduct', 'BarProduct')
将返回0。我们可以使用Sum()
函数进行进一步过滤。
请尝试以下方法:
SELECT orders.order_id
FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
GROUP BY orders.order_id
HAVING SUM(products.type IN ('FooProduct', 'BarProduct')) < COUNT(*)
对于您正在寻找仅具有FooProduct
类型的订单的情况,您可以使用以下代码:
SELECT orders.order_id
FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
GROUP BY orders.order_id
HAVING SUM(products.type <> 'FooProduct') = 0
另一种可能的方法是:
SELECT orders.order_id
FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
GROUP BY orders.order_id
HAVING SUM(products.type = 'FooProduct') = COUNT(*)
您可以使用聚合和having
子句:
SELECT o.*
FROM orders o INNER JOIN
order_items oi
ON oi.order_id = o.id INNER JOIN
products p
ON p.id = oi.product_id
GROUP BY o.id -- OK assuming `id` is the primary key
HAVING SUM(p.type NOT IN ('FooProduct', 'BarProduct')) > 0; -- at least one other product
实际上,这是不对的。这会获得包含其他产品的订单,但它不会获取仅与foo和bar混合的订单。我认为这会得到其他人:
HAVING SUM(p.type = 'FooProduct') < COUNT(*) AND
SUM(p.type = 'BarProduct') < COUNT(*)
这是一个基本的解决方案,不是那么有效但容易:
SELECT * FROM orders WHERE id NOT IN (
SELECT orders.id FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
WHERE products.type = 'FooProduct'
AND (
NOT EXISTS (
SELECT null
FROM products
INNER JOIN order_items ON order_items.product_id = products.id
WHERE order_items.order_id = orders.id
AND products.type != 'FooProduct'
)
)
) AND id NOT IN (
SELECT orders.id FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
WHERE products.type = 'BarProduct'
AND (
NOT EXISTS (
SELECT null
FROM products
INNER JOIN order_items ON order_items.product_id = products.id
WHERE order_items.order_id = orders.id
AND products.type != 'BarProduct'
)
)
)
我建议在连接的subselect中使用count(distinct),如下所示:
SELECT orders.*
FROM orders
inner join (
SELECT orderid, max(products.type) as products_type
FROM order_items
INNER JOIN products ON products.id = order_items.product_id
GROUP BY orderid
-- distinct count of different products = 1
-- -> all order items are for the same product type
HAVING COUNT(distinct products.type ) = 1
-- alternative is:
-- min(products.type )=max(products.type )
) as tmp on tmp.orderid=orders.orderid
WHERE 1=1
-- if you want only single type product orders for some specific product
and tmp.products_type = 'FooProduct'
这是一个关系划分问题。 找到所有产品属于给定类型的订单的一种解决方案是:
SELECT *
FROM orders
INNER JOIN order_items ON order_items.order_id = orders.id
INNER JOIN products ON products.id = order_items.product_id
WHERE orders.id IN (
SELECT order_items.order_id
FROM order_items
INNER JOIN products ON products.id = order_items.product_id
GROUP BY order_items.order_id
HAVING COUNT(CASE WHEN products.type = 'FooProduct' THEN 1 END) = COUNT(*)
)
稍微调整以上内容以查找所有产品来自给定类型列表的订单是:
HAVING COUNT(CASE WHEN products.type IN ('FooProduct', 'BarProduct') THEN 1 END) = COUNT(*)
并且要查找所有产品与给定列表中的所有类型匹配的所有订单,请执行以下操作:
HAVING COUNT(CASE WHEN products.type IN ('FooProduct', 'BarProduct') THEN 1 END) = COUNT(*)
AND COUNT(DISTINCT products.type) = 2