web-dev-qa-db-ja.com

LEFT OUTER JOINとサブクエリ構文

GalaXQLチュートリアルを通じてSQLを学習しています。

次の質問がわかりません(演習12)。

列が「starname」、「startemp」、「planetname」、および「planettemp」で、スターIDが100未満のスターのリストを生成します。リストにはすべての星があり、不明なデータはNULLで埋められています。これらの値は、いつものように架空のものです。 ((class + 7)* intensity)* 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

データベーススキーマは次のとおりです(申し訳ありませんが、担当者が少ないため画像ファイルを投稿できません)。

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

これをゆっくりと構築していきましょう。

まず、星に関する情報だけを取得する方法を見てみましょう。

_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_によってのみ少なくとも1つの惑星が報告されます。しかし、惑星のない星を報告する必要があります!では、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(かつ1回だけ表示)になります。これまでのところ良い!

次に、惑星の温度を追加する必要があります。シンプルであるべきです:

_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を見つけられないことを示す構文エラーが発生します。どうしたの?問題は、afterステートメントの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 mayは実際にはstarTempの計算を行ごとに1回だけ実行するのに十分スマートですが、この場合、このコンテキストでは2回記述する必要があります)。
まあ、それは少し面倒ですが、うまくいきます。必要に応じて、両方の参照を変更することを忘れないでください...

ありがたいことに、この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条件でJOINsおよびWHEREsを処理する場合に特に有益になります。多くの人は、何が影響を受けるか分からなくなります。
また、暗黙的な結合構文(コンマ区切りのFROM句)は、一般に悪い習慣と見なされているか、一部のプラットフォームでは完全に非推奨である(クエリは引き続き実行されますが、dbが君は)。また、_LEFT JOIN_ sなどの特定のことを行うことが困難になり、誤って自分を妨害する可能性が高くなります。ですから、避けてください。

15
Clockwork-Muse
SELECT * FROM (SELECT [...]) as Alias1
LEFT OUTER JOIN 
    (SELECT [...]) as Alias2
ON Alias1.id = Alias2.id
8
fieven
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;
2
SystemFun