使用子查询语法进行LEFT OUTER JOIN

时间:2014-05-25 04:20:54

标签: sql subquery outer-join

我正在通过GalaXQL教程学习SQL。

我无法弄清楚以下问题(练习12):

  

使用列生成明星ID低于100的星列表   " starname"," startemp"," planetname"," planettemp"。列表   应该拥有所有星星,未知数据填写为NULL。   像往常一样,这些价值观是虚构的。计算温度   恒星((class + 7)*强度)* 1000000,行星的温度是   根据恒星的温度减去50倍轨道距离计算出来。

当您有子查询项时,写入LEFT OUTER JOIN查询的语法是什么" AS"你需要一起加入吗?

这就是我所拥有的:

SELECT stars.name AS starname, startemp, planets.name AS planetname, planettemp 
FROM stars, planets 
LEFT OUTER JOIN (SELECT ((stars.class + 7) * stars.intensity) * 1000000 AS startemp 
                 FROM stars) 
             ON stars.starid < 100 = planets.planetid 
LEFT OUTER JOIN (SELECT (startemp - 50 * planets.orbitdistance) AS planettemp 
                 FROM planets) 
             ON stars.starid < 100

这是数据库架构(抱歉,由于低代表而无法发布图像文件):

CREATE TABLE stars (starid INTEGER PRIMARY KEY,
                    name TEXT,
                    x DOUBLE NOT NULL,
                    y DOUBLE NOT NULL,
                    z DOUBLE NOT NULL,
                    class INTEGER NOT NULL,
                    intensity DOUBLE NOT NULL);

CREATE TABLE hilight (starid INTEGER UNIQUE);

CREATE TABLE planets (planetid INTEGER PRIMARY KEY,
                      starid INTEGER NOT NULL,
                      orbitdistance DOUBLE NOT NULL,
                      name TEXT,
                      color INTEGER NOT NULL,
                      radius DOUBLE NOT NULL);

CREATE TABLE moons (moonid INTEGER PRIMARY KEY,
                    planetid INTEGER NOT NULL,
                    orbitdistance DOUBLE NOT NULL,
                    name TEXT,
                    color INTEGER NOT NULL,
                    radius DOUBLE NOT NULL);

CREATE INDEX planets_starid ON planets (starid);
CREATE INDEX moons_planetid ON moons (planetid);

3 个答案:

答案 0 :(得分:12)

让我们慢慢建立起来。

首先,让我们看看如何获​​取有关星星的信息:

SELECT name AS starName, (class + 7) * intensity * 1000000 AS starTemp 
FROM Stars
WHERE starId < 100

(看起来应该很熟悉!)
我们得到starId小于100(WHERE子句)的所有星的列表,抓住名称并计算温度。在这一点上,我们不需要消除对消息来源的歧义。

接下来,我们需要添加行星信息。那么INNER JOIN(注意实际的关键字INNER是可选的)?

SELECT Stars.name as starName, (Stars.class + 7) * Stars.intensity * 1000000 AS starTemp,
       Planets.name as planetName
FROM Stars
INNER JOIN Planets
        ON Planets.starId = Stars.starId
WHERE Stars.starId < 100

ON子句使用=(等于)条件将行星链接到它们所围绕的恒星;否则,我们说他们正在围绕一颗以上的恒星运转,这是非常不寻常的!每颗恒星都会为它所拥有的每个星球列出一次,但这是预期的。

...除了现在我们遇到了一个问题:我们的第一个查询中的一些星星消失了! (INNER) JOIN导致明星至少有一个行星被报告。但我们仍然需要报告没有任何行星的恒星!那么LEFT (OUTER) JOIN呢?

SELECT Stars.name as starName, (Stars.class + 7) * Stars.intensity * 1000000 AS starTemp,
       Planets.name as planetName
FROM Stars
LEFT JOIN Planets
       ON Planets.starId = Stars.starId
WHERE Stars.starId < 100

......如果没有该星球的行星,我们将所有星星都归还,planetNamenull(并且只出现一次)。好到目前为止!

现在我们需要添加行星温度。应该很简单:

SELECT Stars.name as starName, (Stars.class + 7) * Stars.intensity * 1000000 AS starTemp,
       Planets.name as planetName, starTemp - (50 * Planets.orbitDistance) as planetTemp
FROM Stars
LEFT JOIN Planets
       ON Planets.starId = Stars.starId
WHERE Stars.starId < 100

...除了在大多数RDBMS上,您都会收到语法错误,说明系统无法找到starTemp。发生了什么事?问题是新列别名(名称)通常不可用,直到 语句的SELECT部分运行。这意味着我们需要再次进行计算:

SELECT Stars.name as starName, (Stars.class + 7) * Stars.intensity * 1000000 AS starTemp,
       Planets.name as planetName, 
       ((Stars.class + 7) * Stars.intensity * 1000000) - (50 * Planets.orbitDistance) as planetTemp
FROM Stars
LEFT JOIN Planets
       ON Planets.starId = Stars.starId
WHERE Stars.starId < 100

(请注意,db 可能实际上足够智能,每行只执行一次starTemp计算,但在编写时,您必须在此上下文中提及它两次。)<登记/> 嗯,这有点凌乱,但它确实有效。希望你记得在必要的时候改变这两个引用......

幸运的是,我们可以将Stars部分移动到子查询中。我们只需要列出starTemp一次的计算结果!

SELECT Stars.starName, Stars.starTemp,
       Planets.name as planetName, 
       Stars.starTemp - (50 * Planets.orbitDistance) as planetTemp
FROM (SELECT starId, name AS starName, (class + 7) * intensity * 1000000 AS starTemp 
      FROM Stars
      WHERE starId < 100) Stars
LEFT JOIN Planets
       ON Planets.starId = Stars.starId

是的,这看起来就像我写的那样。应该基本上适用于任何RDBMS。

请注意,Stars.starTemp - (50 * Planets.orbitDistance)中的括号仅为了清晰读者,如果删除它们,数学的含义将保持不变。无论您对运算符优先级规则的了解程度如何,在混合运算时始终都要加上括号。在ORAND条件下处理JOINWHERE s时,这会变得特别有用 - 许多人会忘记将要实施的内容。<登记/> 另请注意,隐式连接语法(逗号分隔的FROM子句)通常被认为是不好的做法,或者在某些平台上完全弃用(查询仍然会运行,但数据库可能会骂你)。它还使某些事情 - 如LEFT JOIN - 难以做到,并增加了意外破坏自己的可能性。所以,请避免它。

答案 1 :(得分:7)

SELECT * FROM (SELECT [...]) as Alias1
LEFT OUTER JOIN 
    (SELECT [...]) as Alias2
ON Alias1.id = Alias2.id

答案 2 :(得分:2)

WITH( 
 SELECT 
   stars.name AS starname, ((star.class+7)*star.intensity)*1000000) AS startemp,
   stars.starid
 FROM
   stars
) AS star_temps
SELECT 
   planets.name AS planetname, (startemp-50*planets.orbitdistance) AS planettemp
   star_temps.starname, star_temps.startemp
FROM 
   star_temps LEFT OUTER JOIN planets USING (star_id)
WHERE
   star_temps.starid < 100;

或者,可以构建一个子查询(我使用公共表表达式)来完成相同的任务,如下所示:

SELECT 
   planets.name AS planetname, (startemp-50*planets.orbitdistance) AS planettemp
   star_temps.starname, star_temps.startemp
FROM 
   (SELECT 
      stars.name AS starname, ((star.class+7)*star.intensity)*1000000) AS startemp,
      stars.starid
    FROM
      stars
 ) AS star_temps
LEFT OUTER JOIN planets USING (star_id)
WHERE
   star_temps.starid < 100;