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

ver*_*ter 8 sql subquery outer-join

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

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

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

当您有需要连接在一起的子查询项"AS"时,编写LEFT OUTER JOIN查询的语法是什么?

这是我有的:

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
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

Clo*_*use 15

让我们慢慢建立起来.

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

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

(这应该看起来很熟悉!)
我们得到一个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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

......我们有所有的星星回来了,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
Run Code Online (Sandbox Code Playgroud)

...除了在大多数RDBMS上,您将收到声明系统无法找到的语法错误starTemp.这是怎么回事?问题是新的列别名(name)在语句的一部分运行之后才会(通常)可用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
Run Code Online (Sandbox Code Playgroud)

(请注意,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
Run Code Online (Sandbox Code Playgroud)

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

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


fie*_*ven 8

SELECT * FROM (SELECT [...]) as Alias1
LEFT OUTER JOIN 
    (SELECT [...]) as Alias2
ON Alias1.id = Alias2.id
Run Code Online (Sandbox Code Playgroud)

  • 这是语法。:D (2认同)