将聚合运算符从SQL转换为关系代数

时间:2011-09-30 01:01:20

标签: sql aggregate relational-algebra

我写了几个我要转换为关系代数的SQL查询。但是,有些查询使用聚合运算符,我不知道如何转换它们。值得注意的是,他们使用COUNT和GROUP BY .. HAVING运营商。

这是架构:

水手( sid ,sname,rating) 储备( sid 出价,价格) 小船(出价,bname)

以下是我正在做的一个例子:找到正好由2名水手保留的所有船只的出价和姓名。

SELECT B.bid, B.bname
FROM Boats B, Reserves R
WHERE B.bid = R.bid
GROUP BY R.bid
HAVING 2 = (SELECT COUNT(*)
FROM Reserves R2
WHERE R2.bid = B.bid);

允许的关系代数操作:选择,投影,连接,条件连接,重命名,并集,交集,交叉产品,除法

3 个答案:

答案 0 :(得分:4)

这只是答案的一半......

可以使用条件连接和投影找到“由两个或更多水手保留的船只”的关系,这些关系都在您允许的操作集中:

SELECT DISTINCT R1.bid
  FROM Reserves AS R1 
       JOIN Reserves AS R2
          ON R1.bid = R2.bid
             AND R1.sid < R2.sid;

可以使用条件连接(两次)和投影找到“由三个或更多水手保留的船只”的关系,它们都在您允许的操作集中:

SELECT DISTINCT R1.bid
  FROM Reserves AS R1
       JOIN Reserves AS R2
          ON R1.bid = R2.bid
             AND R1.sid < R2.sid
       JOIN Reserves AS R3
          ON R1.bid = R3.bid
          AND R2.sid < R3.sid;

如果我们有一个减号运算符,例如标准SQL中的EXCEPT

SELECT DISTINCT R1.bid
  FROM Reserves AS R1 
       JOIN Reserves AS R2
          ON R1.bid = R2.bid
             AND R1.sid < R2.sid
EXCEPT
SELECT DISTINCT R1.bid
  FROM Reserves AS R1
       JOIN Reserves AS R2
          ON R1.bid = R2.bid
             AND R1.sid < R2.sid
       JOIN Reserves AS R3
          ON R1.bid = R3.bid
          AND R2.sid < R3.sid;

如果我们在SQL中有限制(WHERE)和半差异(又名antijoin)运算符(例如SQL中的NOT IN):

SELECT DISTINCT R1.bid
  FROM Reserves AS R1 
       JOIN Reserves AS R2
          ON R1.bid = R2.bid
             AND R1.sid < R2.sid
 WHERE R1.bid NOT IN (
                      SELECT DISTINCT R1.bid
                        FROM Reserves AS R1
                             JOIN Reserves AS R2
                                ON R1.bid = R2.bid
                                   AND R1.sid < R2.sid
                             JOIN Reserves AS R3
                                ON R1.bid = R3.bid
                                AND R2.sid < R3.sid
                     );

...但您的一组允许操作不包括限制,半差异或减号:(

答案 1 :(得分:4)

“我正在阅读一本关于代数关系代数的书,但它根本没有提到它们的集合函数”。

关于关系代数的文献通常将自己局限于使代数关闭关系的代数部分。如果代数的运算符都没有返回不是该集合成员的类型的值,那么代数在一组类型上是封闭的(我可能表达自己有点邋,,但主要思想是正确的)代数的类型是封闭的。

如果您拥有(或想要在书中考虑)是所有关系类型的集合,并且您想要编写代数的处理,那么您不能定义返回整数的运算符(COUNT),或者一个浮点数(HARMONICMEAN),或一个日期(MIN(&lt; date column&gt;)),或任何类似的东西,而不会破坏代数的“封闭”属性。

这并不是说这种聚合操作是无用的(当然不是)。它们通常在主要目的是解释JOIN,PROJECT,RESTRICT等的上下文中并不完全相关。

修改

关于GROUP BY ... HAVING的补充答案。您正确地注意到,当涉及到代数等价时,这个SQL构造是非常重要的。它的要点是你提到的代数运算符集缺少实现这些东西所需的运算符,而这个运算符是GROUP。 GROUP获取输入关系,并生成输出关系,其中一个属性是关系值

例如,GROUP(RESERVES,SAILORS_AND_THEIR_BID(SID,PRICE))将产生度为2的关系,属性为BID和SAILORS_AND_THEIR_BID。后一个属性是关系值,因此表达式COUNT(SAILORS_AND_THEIR_BID)在应用于此关系的RESTRICT条件的上下文中变为有效,这样您就可以写入(GROUP(RESERVES,SAILORS_AND_THEIR_BID(SID,PRICE)))WHERE COUNT (SAILORS_AND_THEIR_BID)= 2。

答案 2 :(得分:1)

立足于onedaywhen的回答:

是的,缺少设定差异运算符会造成伤害。应该完全允许它。但是,我们可以用set complement和intersection表示集合差异:

B - A = B ∩ A'

即。 B和A的差异实际上是B与A的补码的交集。我们将交集作为一个允许的运算符,虽然关系的正确补码是一个丑陋的东西,R1 R的R相对于R的补充(即R中不在R1中的东西)可以通过连接轻松找到:

SELECT DISTINCT R0.x
FROM R as R1
JOIN R as R0 ON R1.x<>R0.x
WHERE R1.x=val

是相对于

的R的补充
SELECT DISTINCT R.x FROM R WHERE R.x=val

所以,这里有一个谜语的解决方案,我想:很容易得到两个或更多人保留的所有船只:选择保留表中的所有船只,自带结果的笛卡尔积,然后选择具有不同sailor1和sailor2的每一行。在笨拙的关系代数符号中,他们教我:

π( R.bid ) (
   σ( R.bid=R2.bid and R.sid<R2.sid )( R x ρ(R, R2) ) 
)

(其中π是投影算子,σ是选择算子,ρ是重命名算子)

这可以捕捉两个或更多人保留的所有船只的身份。现在我要把所有由两个或更少的人保留的船只。为此,我将选择由三个或更多人保留的所有船只,并通过选择原始表格中不存在于该集合中的所有行来获取集合的补充。它不会很漂亮,但在这里:

 π(R.bid)(σ(R.bid<>R1.bid)(
    π(R.bid)(R)
      x
    π(R1.bid) (
        σ( R1.bid=R2.bid and R2.bid=R3.bid and R1.sid<R2.sid and R2.sid<R3.sid )( ρ(R, R1) x ρ(R, R2) x ρ(R, R3) )
     )
  ))

你看,我选择了具有该属性的所有行,然后选择原始表中不是这些行的所有行,将所有没有属性的行联系起来,这里的意思是所有的船都没有由三人或三人以上保留,由两人或少人预留的船只。

为了让两艘船只保留这些船只,只需将其与不止一个人保留的船只相交。

π( R.bid ) (
   σ( R.bid=R2.bid and R.sid<R2.sid )( R x ρ(R, R2) ) 
) ∩ π( R.bid ) (
    σ(R.bid<>R1.bid)(
       π(R.bid)(R)
         x
       π(R1.bid) (
           σ( R1.bid=R2.bid and R2.bid=R3.bid and R1.sid<R2.sid and R2.sid<R3.sid )( ρ(R, R1) x ρ(R, R2) x ρ(R, R3) )
        )
     )
 )

唉。这太难看了。我希望我知道一个更好的符号。

错误的是,我认为它可能看起来像这样:

(SELECT DISTINCT R1.bid
  FROM Reserves AS R1 
    JOIN Reserves AS R2 ON R1.bid = R2.bid AND R1.sid < R2.sid
) INTERSECT (
SELECT DISTINCT R.bid
  FROM Reserves AS R1
    JOIN Reserves AS R2 ON R1.bid = R2.bid AND R1.sid < R2.sid
    JOIN Reserves AS R3 ON R1.bid = R3.bid AND R2.sid < R3.sid
    JOIN Reserves AS R ON R.bid<>R1.bid
)

请注意,这是正确的解决方案,除了我表示设置差异为与补码的交集。