[英]How to select records where all joined records aren't a match for criteria
我使用以下表格进行了设置(使用MySQL):
orders
,其中有很多: order_items
表order_items
,其中一个来自: products
表 我写了一个查询来选择所有 products
均为某种type
orders
:
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
的订单,然后再次获取所有BarProduct
的订单。
我的症结一直是生成第三个查询以获取所有其他订单,即,其所有产品的类型都不都是FooProduct
或BarProduct
(又是两种或其他产品类型的混合)。
所以,我的问题是如何获得的所有记录, 所有的产品类型是不完全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
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
这是一个基本解决方案,效率不高,但很容易:
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'
)
)
)
我建议像这样在连接的子选择中使用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'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.