【问题标题】:Converting aggregate operators from SQL to relational algebra将聚合运算符从 SQL 转换为关系代数
【发布时间】:2011-11-28 03:09:10
【问题描述】:

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

这是架构:

Sailors(sid, sname, rating) 储备(sidbid、价格) 船(出价,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);

允许的关系代数运算:选择、投影、连接、条件连接、重命名、联合、交集、叉积、除法

【问题讨论】:

  • Codd 的原始关系运算符包括集差,Darwen 的包括半差(我不熟悉任何其他关系代数)。我想知道您的允许操作集是否缺少等效操作。

标签: sql aggregate relational-algebra


【解决方案1】:

以 onedaywhen 的回答为基础:

是的,缺少集合差异运算符确实会造成伤害。应该完全允许。但是,我们可以用集补和交集来表示集差:

B - A = B ∩ A'

即B 和 A 的区别实际上是 B 与 A 的补码的交集。我们将交集作为允许的运算符,虽然关系的适当补码是一件丑陋的事情,但 R1 ⊆ 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

所以,这个谜题的解决方案来了:很容易得到两个或更多人保留的所有船:选择保留表中的所有船,将结果的笛卡尔积与自身相乘,然后选择具有不同水手1 和水手2 的每一行。在他们教给我的笨拙的关系代数符号中:

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

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

这会计算两个或更多人预订的所有船只的 ID。现在我要去取所有由两个或更少的人预订的船。为此,我将选择三个或更多人保留的所有船,并通过从原始表中选择该集合中不存在的所有行来获取集合的补充。它不会很漂亮,但它是这样的:

 π(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) )
        )
     )
 )

呃。太丑了,让人心疼。我希望我知道一个更好的符号。

SQLishly,我认为它可能看起来像这样:

(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
)

请注意,这正是 onedaywhen 的解决方案,除了我将集差表示为取与补码的交集。

【讨论】:

  • 很好的解释。
【解决方案2】:

“我正在阅读一本书,其中有一章是关于关系代数的,它根本没有提到它们的聚合函数”。

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

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

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

编辑

关于 GROUP BY ... HAVING 的补充答案。您正确地注意到,当涉及到代数等价物时,这个 SQL 构造是不平凡的东西。它的要点是您提到的代数运算符集缺少实现这些东西所需的运算符,而该运算符是 GROUP。 GROUP 接受一个输入关系,并产生一个输出关系,其中一个属性是 relation-valued

例如,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。

【讨论】:

  • 我同意所有(很好的答案!)除了 MIN 和 MAX,它们不会引入新类型,只有 GROUP BY - MAX - MIN 的查询可以用关系代数来描述。
  • 那么你必须能够写下从任意数量的行中产生最低或最​​高日期(或数字、重量、长度或其他)的关系代数表达式。或者对于生成结果的表达式,将每个客户 ID 与他输入的第一个订单的日期配对。并且能够拼出你使用的代数运算符的正式定义。
【解决方案3】:

这只是答案的一半……

可以使用条件连接和投影找到关系“由两个或多个水手预订的船”,它们都在您允许的操作集中:

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;

If 我们有一个减号运算符,例如EXCEPT 在标准 SQL 中:

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
                     );

...但是您允许的操作集不包括限制、半差或减号:(

【讨论】:

  • 不错。我很好奇,我们可以在关系代数中写SELECT sid, COUNT(*) FROM r GROUP BY sid 吗?
  • @ypercube:关系代数是一组关系运算符。关系运算符将两个或多个关系作为参数并返回一个关系。聚合运算符也将关系作为参数,但会返回一个值。因此,聚合运算符不能是关系代数的成员。但是,关系语言可以(并且可能会)包含聚合运算符,因为它们非常有用,例如所有数据库约束都可以使用COUNT来表示。
  • @onedaywhen 可以编写一个查询来查找由 2 人预订的船只,并且只有 2 名水手?
  • @Imray:考虑“两个或更多水手保留的船”MINUS“三个或更多水手保留的船”EQUALS“由正好是两个水手”。
猜你喜欢
  • 2019-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多