我正在通过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);
答案 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
......如果没有该星球的行星,我们将所有星星都归还,planetName
为null
(并且只出现一次)。好到目前为止!
现在我们需要添加行星温度。应该很简单:
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)
中的括号仅为了清晰读者,如果删除它们,数学的含义将保持不变。无论您对运算符优先级规则的了解程度如何,在混合运算时始终都要加上括号。在OR
和AND
条件下处理JOIN
和WHERE
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;