问题
I have the following SQL:
SELECT `table1`.`value`, `table2`.*
FROM `table2`
INNER JOIN `table1` ON `table2`.`product_id` = `table1`.`entity_id`
WHERE `table2`.`created_at` > '2012-04-23' and
(`table1`.`value` = 264 OR `table1`.`value` = 260)
order by order_id
Which returns a result set like this (This is only a part of the returned results):
value order_id ...
260 1234
260 1235
260 1236
264 1236
260 1237
260 1238
260 1239
264 1239
264 1240
260 1241
What I want is a query that will take these results and only return orders where the order_id contains both value 260 and 264. Based on this example, the end result I am looking for is
260 1236
264 1236
260 1239
264 1239
My thought is this can be done with a subset but I am not exactly sure on the details to accomplish it.
回答1:
This can be accomplished with relational division:
select r.order_id from (
select
dividend.*
from your_table_or_query as dividend -- assumes no duplicates in `dividend`; use `distinct` if there are any
inner join divisor
on dividend.value = divisor.value
) as r
group by r.order_id
having count(*) = (select count(*) from divisor);
result:
+----------+
| order_id |
+----------+
| 1236 |
| 1239 |
+----------+
2 rows in set (0.00 sec)
where your query is your_table_or_query
and
select 260 as value from dual union select 264 as value from dual
is divisor
.
This will return the order ids 1236 and 1239; they can then be join
ed to original query to get all the rows with those order ids if that's what you want.
Full query along with insert statements:
create table divisor (value int);
insert into divisor values (260), (264);
create table your_table_or_query (value int, order_id int);
insert into your_table_or_query values (260, 1234), (260, 1235), (260, 1236), (264, 1236), (260, 1237), (260, 1238), (260, 1239), (264, 1239), (264, 1240), (260, 1241);
select y.* from (
select r.order_id from (
select
dividend.*
from your_table_or_query as dividend
inner join divisor
on dividend.value = divisor.value
) as r
group by r.order_id
having count(*) = (select count(*) from divisor)
) as quotient
inner join your_table_or_query y
on quotient.order_id = y.order_id;
Result:
+-------+----------+
| value | order_id |
+-------+----------+
| 260 | 1236 |
| 264 | 1236 |
| 260 | 1239 |
| 264 | 1239 |
+-------+----------+
4 rows in set (0.00 sec)
回答2:
How about this?
SELECT table1.value, table2.*
FROM table2
INNER JOIN table1
ON table2.product_id = table1.entity_id
WHERE table2.created_at > '2012-04-23' AND
(table1.value = 264 OR table1.value = 260)
AND table2.order_id IN (
SELECT table2.order_id
FROM table2
INNER JOIN table1
ON table2.product_id = table1.entity_id
WHERE table2.created_at > '2012-04-23' AND
(table1.value = 264 OR table1.value = 260)
GROUP BY table2.order_id
HAVING COUNT(*) = 2
)
(sorry for omitting apostrophes, I am not aware what are mysql requirements in this field; this syntax is ms-sql compliant)
回答3:
this will return a list of orders that have both values 260 and 264 - if someone needs just this result instead of duplicated results for each of the value:
SELECT `table2`.*
FROM `table2`
INNER JOIN `table1` as `table1_264`
ON `table2`.`product_id` = `table1_264`.`entity_id`
AND `table1_264`.`value` = 264
INNER JOIN `table1` as `table1_260`
ON `table2`.`product_id` = `table1_260`.`entity_id`
AND `table1_260`.`value` = 260
WHERE `table2`.`created_at` > '2012-04-23'
order by order_id
or if someone wanted faster solution without costly
having
clauses (+ of course you can store the query or use a CTE instead of just repeating it)
SELECT 264 as `value`, `table2`.*
FROM `table2`
INNER JOIN `table1` as `table1_264` ON `table2`.`product_id` = `table1_264`.`entity_id` AND `table1_264`.`value` = 264
INNER JOIN `table1` as `table1_260` ON `table2`.`product_id` = `table1_260`.`entity_id` AND `table1_260`.`value` = 260
WHERE `table2`.`created_at` > '2012-04-23'
UNION ALL
SELECT 260, `table2`.*
FROM `table2`
INNER JOIN `table1` as `table1_264` ON `table2`.`product_id` = `table1_264`.`entity_id` AND `table1_264`.`value` = 264
INNER JOIN `table1` as `table1_260` ON `table2`.`product_id` = `table1_260`.`entity_id` AND `table1_260`.`value` = 260
WHERE `table2`.`created_at` > '2012-04-23'
order by order_id
回答4:
SELECT `table1`.`value`, `table2`.*
FROM `table1`
LEFT JOIN `table2` ON `table1`.`entity_id` = `table2`.`product_id`
WHERE
(`table1`.`value` = 264 OR `table1`.`value` = 260)
AND `table2`.`created_at` > '2012-04-23'
ORDER BY `table2`.order_id
Left join so it only matches the table1.value results with matches and leave the info from the other table out
来源:https://stackoverflow.com/questions/10485971/sql-to-return-a-merged-set-of-results