将SQL语句转换为关系代数

时间:2016-04-26 18:06:01

标签: sql relational-algebra

我有SQL语句:

    SELECT need.studentid, need.firstname, need.lastname, need.courseid
FROM 
     (SELECT student.studentid, student.firstname, student.lastname, enrollment.courseid 
      FROM prereq LEFT JOIN enrollment ON prereq.courseid = enrollment.courseid LEFT JOIN student ON enrollment.studentid = student.studentid) AS need

WHERE need.studentid NOT IN (SELECT studentid 
FROM enrollment
WHERE NOT(grade LIKE '%F' OR grade IS NULL OR grade LIKE 'N'))

我的目标是将其转换为关系代数。 我设法转换了Left Joins和内部选择,但我不知道如何将它与第二部分连接以及如何将NOT IN转换为关系代数。

1 个答案:

答案 0 :(得分:1)

如果NOT IN (subquery)中的子查询为`studentid`返回NULL值,我们不依赖于我们得到的行为......

(如果子查询返回NULL,则对于任何行,NOT IN比较永远不会计算为TRUE,因此外部查询不会返回任何行。)

可能有某种保证`studentid`永远不会为NULL,就像它被定义为NOT NULL或它是PRIMARY KEY ......但我们没有看到。并且该子查询中没有studentid IS NOT NULL条件,因此我们必须预测子查询返回NULL时将发生的行为。

如果我们对保留这种行为不感兴趣,

然后NOT IN (subquery)可以表示为反连接模式。

SELECT need.studentid
     , need.firstname
     , need.lastname
     , need.courseid
  FROM ( SELECT student.studentid
              , student.firstname
              , student.lastname
              , enrollment.courseid 
           FROM prereq
           LEFT
           JOIN enrollment
             ON prereq.courseid = enrollment.courseid
           LEFT
           JOIN student
             ON enrollment.studentid = student.studentid
       ) need
  LEFT
  JOIN enrollment aj
    ON aj.studentid = need.studentid
   AND NOT ( aj.grade LIKE '%F' OR aj.grade IS NULL OR aj.grade LIKE 'N')
 WHERE aj.studentid IS NULL

反连接模式看起来像一个外连接(从一侧返回所有行以及从另一侧返回匹配的行),以及WHERE子句中排除所有匹配行的条件。

这是有效的,因为对于找到的任何匹配行,at.studentid保证为非NULL。对于来自needaj中没有匹配行的任何行,这些行将为aj中的列设置NULL值。 (这就是外连接基本上做的...发明一行NULL值作为连接操作的占位符匹配行。)

对于WHERE子句中的条件测试,我们需要做的就是测试aj中保证为非NULL的一列中的NULL值。如果我们知道那是什么,我们可以参考表格的PRIMARY KEY。我们可以使用studentid列,因为在连接条件中满足相等比较的任何行都保证是非NULL的。)

SQL反连接可以转换为关系代数。

可以在没有内联视图的情况下返回等效结果。

SELECT student.studentid
     , student.firstname
     , student.lastname
     , enrollment.courseid 
  FROM prereq
  LEFT
  JOIN enrollment
    ON prereq.courseid = enrollment.courseid
  LEFT
  JOIN student
    ON enrollment.studentid = student.studentid
  LEFT
  JOIN enrollment aj
    ON aj.studentid = student.studentid
   AND NOT ( aj.grade LIKE '%F' OR aj.grade IS NULL OR aj.grade LIKE 'N')
 WHERE aj.studentid IS NULL