[英]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.