[英]How to select records where all joined records aren't a match for criteria
I've a setup with the following tables (using MySQL): 我使用以下表格进行了设置(使用MySQL):
orders
, which have many: orders
,其中有很多: order_items
, which have one from the: order_items
表order_items
,其中一个来自: products
table products
表 I've written a query to select orders
where all their products
are of a certain type
: 我写了一个查询来选择所有
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'
)
)
I run similar a couple of times: firstly to get orders comprised of all FooProduct
s, and again to get orders with all BarProduct
s. 我也进行了两次类似的操作:首先要获取包含所有
FooProduct
的订单,然后再次获取所有BarProduct
的订单。
My sticking point has been generating a third query to get all other orders, ie where all their products' types are not exclusively FooProduct
s, or exclusively BarProduct
s (aka a mix of the two, or other product types). 我的症结一直是生成第三个查询以获取所有其他订单,即,其所有产品的类型都不都是
FooProduct
或BarProduct
(又是两种或其他产品类型的混合)。
So, my question is how can I get all records where all product types aren't exclusively FooProduct
s or exclusively BarProduct
. 所以,我的问题是如何获得的所有记录, 所有的产品类型是不完全
FooProduct
S或专门BarProduct
。
Here's a little example data, from which I'd like to return the orders with the IDs 3 and 4: 以下是一些示例数据,我想从中返回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'
I've attempted this, awfully so placing as a subtext, with the following in place of the existing AND
(even the syntax is way off): 我已经尝试过了,因此非常遗憾地将其放置为子文本,并用以下内容代替了现有的
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')
)
Instead of using Correlated subqueries, you can use Having
and conditional aggregation function based filtering. 而不是使用相关子查询的,可以使用
Having
和条件聚合函数基于内容的过滤。
products.type IN ('FooProduct', 'BarProduct')
will return 0 if a product type is none of them. 如果产品类型都不是,那么
products.type IN ('FooProduct', 'BarProduct')
将返回0。 We can use Sum()
function on it, for further filtering. 我们可以在其上使用
Sum()
函数进行进一步的过滤。
Try the following instead: 请尝试以下操作:
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(*)
For the case, where you are looking for orders which has only FooProduct
type, you can use the following instead: 对于这种情况,如果要查找仅具有
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
Another possible approach is: 另一种可能的方法是:
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(*)
You can use aggregation and a having
clause for this: 您可以为此使用聚合和
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
Actually, that is not quite right. 实际上,那是不正确的。 This gets orders that have some other product, but it doesn't pick up orders that are mixes only of foo and bar.
这会获得具有其他产品的订单,但不会选择仅由foo和bar混合而成的订单。 I think this gets the others:
我认为这会吸引其他人:
HAVING SUM(p.type = 'FooProduct') < COUNT(*) AND
SUM(p.type = 'BarProduct') < COUNT(*)
This is a relational division problem. 这是一个关系划分问题。
One solution to find orders where all products are of a given type is this: 查找所有给定类型产品的订单的一种解决方案是:
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(*)
)
Tweak the above just a little to find orders where all products are from a list of given types is this: 稍微调整一下上面的内容,即可从给定类型的列表中找到所有产品都来自的订单:
HAVING COUNT(CASE WHEN products.type IN ('FooProduct', 'BarProduct') THEN 1 END) = COUNT(*)
And to find all orders where all products match all types from a given list is this: 从给定列表中查找所有产品与所有类型匹配的所有订单的方法是:
HAVING COUNT(CASE WHEN products.type IN ('FooProduct', 'BarProduct') THEN 1 END) = COUNT(*)
AND COUNT(DISTINCT products.type) = 2
This is a basic solution, not so efficient but easy: 这是一个基本解决方案,效率不高,但很容易:
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'
)
)
)
I would suggest using count(distinct) in joined subselect like this: 我建议像这样在连接的子选择中使用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.