困难的where子句,可能需要使用两个查询?

时间:2014-07-01 10:41:03

标签: php mysql sql

我有一个查询要生产,我似乎无法做对。我有三张桌子:

student:学生详情

link:学生存在的链接,链接的状态可以是活动的或已完成

email:显示通过电子邮件发送的链接。

我需要根据以下标准获取学生ID列表(来自学生表):

  1. link.status =有效且相关的电子邮件不存在(即已创建链接,但尚未通过电子邮件发送)
  2. link.status为null且email为null(即该学生没有现有链接)
  3. link.status =已完成,此学生没有其他具有有效状态的链接
  4. 所以如果我的表中有以下数据:

    student

    +----+
    | id |
    +----+
    |  1 |
    |  2 |
    |  3 |
    |  4 |
    +----+
    

    link

    +----+-----------+------------+
    | id |  status   | student_id |
    +----+-----------+------------+
    |  1 | completed |          1 |
    |  2 | active    |          1 |
    |  3 | completed |          2 |
    |  4 | active    |          3 |
    +----+-----------+------------+
    

    email

    +----+---------+
    | id | link_id |
    +----+---------+
    |  1 |       1 |
    |  2 |       2 |
    |  3 |       3 |
    +----+---------+
    

    然后我的查询应返回以下学生ID:2,3,4

    2 - 因为此学生只有一个完整的链接

    3 - 因为存在没有关联电子邮件的活动链接

    4 - 因为此学生没有链接

    我目前有这个查询,它可以满足我的需求:

    SELECT DISTINCT student.id
    FROM student
    LEFT JOIN link ON link.student_id = student.id
    LEFT JOIN email ON email.link_id = link.id
    WHERE student.course =  'phd'
    AND student.institution_id =  '2'
    AND (
      (link.status !=  "active" AND email.id IS NULL)
    OR 
      (link.status IS NULL AND email.id IS NULL)
    OR 
      (link.status =  "active" AND email.id IS NULL)
    )
    

    这当然没有获得任何学生ID,其中link.status =已完成且学生没有其他链接。我当然可以通过添加:

    来做到这一点
    (link.status = "completed" and email.id IS NOT NULL)
    

    进入WHERE,但如果他们有另一个活动链接或者他们没有活动链接,这将返回学生ID。这是我正在努力的一点。

    我觉得这可能无法通过单个查询完成,所以我需要做两个查询然后相互减去它们吗?即上面的查询和单独的查询选择具有“已完成”状态的链接,然后从第一个查询中减去它们?

    我使用这些查询的应用程序是用PHP构建的,所以我很乐意在PHP中使用两个查询做一些逻辑。如果需要的话。

    (我不知道该怎么做标题所以如果有人能想到更好的东西请编辑它!)

2 个答案:

答案 0 :(得分:2)

因为您的请求基于链接而且一个学生可以有多个链接,所以您应该从查询到链接表开始,然后添加联接和条件。

准备SQL:

CREATE TABLE IF NOT EXISTS student 
  (
    id int auto_increment primary key, 
    course tinytext, 
    institution_id int
  );
INSERT INTO student (id, course, institution_id) VALUES
  (1, 'phd', 2),
  (2, 'phd', 2),
  (3, 'phd', 2),
  (4, 'phd', 2);
CREATE TABLE IF NOT EXISTS link 
  (
    id int auto_increment primary key, 
    status tinytext, 
    student_id int
  );
INSERT INTO link (id, status, student_id) VALUES
  (1, 'completed', 1),
  (2, 'active', 1),
  (3, 'completed', 2),
  (4, 'active', 3);
CREATE TABLE IF NOT EXISTS email 
  (
    id int auto_increment primary key, 
    link_id int
  );
INSERT INTO email (id, link_id) VALUES
  (1, 1),
  (2, 2),
  (3, 3);

查询:

SELECT DISTINCT s.id
FROM link l
LEFT JOIN student s ON l.student_id = s.id
LEFT JOIN email e ON l.id = e.link_id
WHERE s.course =  'phd'
AND s.institution_id =  '2'
AND (
  (l.status !=  "active" AND e.id IS NULL)
  OR 
  (l.status IS NULL AND e.id IS NULL)
  OR 
  (l.status =  "active" AND e.id IS NULL)
)

玩它:http://sqlfiddle.com/#!2/dae16b/2

我真的不明白你的问题,因为你有很多错误。我会尝试深入挖掘,看看你的逻辑是否正常。

编辑:通过过滤JOIN的“草莓”方法可能是你需要的东西

SELECT s.id 
  FROM student s 
  LEFT JOIN link l 
    ON l.student_id = s.id AND l.status = 'active' OR l.status IS NULL 
  LEFT JOIN email e 
    ON e.link_id = l.id 
WHERE 
  e.id IS NULL 
  AND s.course =  'phd' 
  AND s.institution_id =  '2';

玩它:http://sqlfiddle.com/#!2/dae16b/26

我们选择“学生”表并仅添加具有“活动”或“空”状态(LEFT JOIN link l ON l.student_id = s.id AND l.status != 'completed')的链接,这些链接解决了规则#2(该学生没有现有链接)和规则#1的第一部分(已创建链接但尚未通过电子邮件发送)和规则#3的第二部分(link.status =已完成,有此学生没有其他具有活动状态的链接)。之后,要解决第二条规则#2(已创建链接但尚未通过电子邮件发送),我们会删除没有电子邮件的行({ {1}}和JOIN email e ON e.link_id = l.id部分)。

如果您需要解决规则#3的第一部分( link.status =已完成,并且该学生没有其他具有活动状态的链接),因为我不知道,如果“学生没有链接”=“学生有链接完成状态”的情况。

现在,此查询会返回您请求的内容。

答案 1 :(得分:2)

SELECT s.* 
  FROM student s 
  LEFT 
  JOIN link l 
    ON l.student_id = s.id 
   AND l.status <> 'completed' 
  LEFT 
  JOIN email e 
    ON e.link_id = l.id 
 WHERE e.id IS NULL;