结合两个SQL语句,其中两者必须在多对多关系中为真

时间:2014-01-30 10:22:59

标签: mysql sql many-to-many

我仍然缺乏SQL经验,我正在试图找出如何结合查询的两个要求。

首先是模型,以简化对要求的理解:

enter image description here

我需要一组结果,获取与一个列表中的任一值匹配的行,我将调用此结果1:

SELECT * FROM
resource r
inner join 
category_resource cr
on (r.id = cr.resource_id)
where cr.category_id in (8,9)

但是还有第二个要求,我将其称为结果2: 类似的语句,但与其他值必须匹配(由相同的行),以便任何这些行匹配:

SELECT * FROM
resource r
inner join 
category_resource cr
on (r.id = cr.resource_id)
where cr.category_id in (10,11,12)

当然我觉得这在某种程度上是多余的,并且应该有一种更简单的方法来在一个语句中编写它。但重点是,这不符合要求:

SELECT * FROM
resource r
inner join 
category_resource cr
on (r.id = cr.resource_id)
where cr.category_id in (8,9,10,11,12)

因为这只相当于说匹配结果1或结果2.但我需要的是匹配结果1和结果2.即,像:

SELECT * FROM
resource r
inner join 
category_resource cr
on (r.id = cr.resource_id)
where cr.category_id in (8,9) AND (10,11,12)

但这不起作用(可能根本不正确)......

最后用简单的英语,我想要的是找到任何类别为8或9的资源, AND 的类别为10 OR 11 OR 12.

那么我怎样才能尽可能简单地完成这样的事情呢?

编辑:

我忘了一个要求:

我还必须得到每个结果行所属的类别(在查询中是否要求这些类别。即使我只是要求任何属于(8 OR 9)和(10)的资源。或者11或12),如果资源匹配,我想知道它所属的所有类别,即使它们也可以包括13和14 ...

我有一个类似的要求,我之前解决了这个问题:

SELECT
  r.id, r.title,
  u.name AS 'created_by',
  GROUP_CONCAT( CONCAT(CAST(c.id as CHAR),',',c.name,',',c.value) separator ';') AS 'Categories'
FROM
  resource r
  INNER JOIN 
/*Select matching records as a table*/
(SELECT
    resource_id
  FROM
    category_resource
  WHERE
    category_id IN (9,10,11,12,13,14,15)) mr
    ON r.id = mr.resource_id
  INNER JOIN category_resource cr
    ON r.id = cr.resource_id
  INNER JOIN category c
    ON cr.category_id = c.id
  INNER JOIN user u
    ON r.created_by = u.id
GROUP BY r.id;

但该声明当然没有纳入这一最新要求。我可以将此结合起来并获得结果,包括每个结果所属的所有类别吗?

mrjink的答案似乎完美无缺。而且我还测试了使用相同的模式在同一行中添加更多标准,它似乎工作得很好:

SELECT
  r.id, r.title,
  u.name AS 'created_by',
  GROUP_CONCAT( CONCAT(CAST(c.id as CHAR),',',c.name,',',c.value) separator ';') AS 'Categories'
FROM
  resource r
  INNER JOIN 
/*Select matching records as a table*/
  (SELECT
   DISTINCT r.id AS id
  FROM
   resource r
  INNER JOIN
   category_resource cr1 ON (r.id = cr1.resource_id)
  INNER JOIN
   category_resource cr2 ON (r.id = cr2.resource_id)
/*For more criteria, I can just add an inner join here with another alias...*/
INNER JOIN
   category_resource cr3 ON (r.id = cr3.resource_id)
INNER JOIN
   category_resource cr4 ON (r.id = cr4.resource_id)
  WHERE
   cr1.category_id IN (8, 9)
  AND
   cr2.category_id IN (10)
/*and add the corresponding ADD clause here for the same alias...*/
  AND
   cr3.category_id IN (12)
  AND
   cr4.category_id IN (14)) mr

    ON r.id = mr.id
  INNER JOIN category_resource cr
    ON r.id = cr.resource_id
  INNER JOIN category c
    ON cr.category_id = c.id
  INNER JOIN user u
    ON r.created_by = u.id
GROUP BY r.id;

这样,如果用户选择它们,我可以以编程方式添加更多条件(就像意图一样)。从我所看到的情况来看,虽然这可能会创建非常复杂的SQL语句,如果用手写或读它们,它似乎运行良好并且不会以负面方式影响性能(如果有的话,添加更多标准似乎加速它起来,大概是因为点击次数越来越少?)

2 个答案:

答案 0 :(得分:1)

加入两次!

这样的事情应该有效:

SELECT
  r.id, r.title,
  u.name AS 'created_by',
  GROUP_CONCAT( CONCAT(CAST(c.id as CHAR),',',c.name,',',c.value) separator ';') AS 'Categories'
FROM
  resource r
  INNER JOIN 
/*Select matching records as a table*/

  (SELECT
   DISTINCT r.id AS id
  FROM
   resource r
  INNER JOIN
   category_resource cr1 ON (r.id = cr1.resource_id)
  INNER JOIN
   category_resource cr2 ON (r.id = cr2.resource_id)
  WHERE
   cr1.category_id IN (8, 9)
  AND
   cr2.category_id IN (10, 11, 12)) mr

    ON r.id = mr.id
  INNER JOIN category_resource cr
    ON r.id = cr.resource_id
  INNER JOIN category c
    ON cr.category_id = c.id
  INNER JOIN user u
    ON r.created_by = u.id
GROUP BY r.id;

答案 1 :(得分:1)

select * from resource
where id in(
select set1.resource_id from
(select resource_id 
     from category_resource where category_id in (8,9)) set1
join (select resource_id 
     from category_resource where category_id in (10,11,12)) set2
on set1.resource_id = set2.resource_id
group by set1.resource_id);

SQL小提琴: http://sqlfiddle.com/#!2/6ce70/5