2017-01-03 14 views
2

誰でも最大のSQLチャレンジはありますか?これまでの私の努力は問題を簡素化して質問に入れてもそれほどではありません...相互参照と自己参照付きの複数のテーブルからのSELECT [脳クラッカー]

ここに行きます。例では、インクルードする必要があり下回っ:人はその国で支払われ、市内に別の飛行をした場合を(支払われたりしない)国に

  • すべて支払わ便
  • 券を

これは既に難しいことですが、それ以上のことがあります。

  • 人はいない参加料を市に飛ぶが、それが料金を持っています 国にある場合は、その飛行はまだ を支払い、同様に含まれなければならないと考えられています。

enter image description here EDIT:私は不必要に追加無料fligtsを明らかに役立つはず飛行110を、追加しました。以下は

SQLクエリから出てくる必要があり、結果セットです:

+--------------------------------------------------------------+ 
| Desired result set           | 
+--------------------------------------------------------------+ 
| FlightNumber | ID | Name | LocationID | location.Name  | 
+--------------------------------------------------------------+ 
| 102   | 2 | Tom | 500  | NL - NoFee   | -> because Tom has a paid flight to Amsterdam 
| 103   | 2 | Tom | 501  | Amsterdam (NL) - Fee | -> because Amsterdam is a paid location 
| 105   | 4 | Bob | 501  | Amsterdam (NL) - Fee | -> because Amsterdam is a paid location 
| 107   | 6 | Bill | 503  | ITA - Fee   | -> because ITA is a paid location 
| 108   | 7 | Ryan | 503  | ITA - Fee   | -> because ITA is a paid location 
| 109   | 7 | Ryan | 505  | Venice (ITA) - NoFee | -> because Venice is located inside ITA 
+--------------------------------------------------------------+ 

誰もが、この甘い結果はSQLを使用して設定を取得する方法を知っていますか?

開始するには良い場所:

SELECT flights.FlightNumber, people.ID, people.Name, flights.LocationID, locations.Name 
FROM flights 
INNER JOIN people ON (people.ID = flights.ID) 
INNER JOIN locations ON (locations.LocationID = flights.LocationID) 

重要でないノート

CREATE TABLE `people` (
    `ID` INT NOT NULL, 
    `Name` VARCHAR(45) NULL, 
    PRIMARY KEY (`ID`)); 

CREATE TABLE `locations` (
    `LocationID` INT NOT NULL, 
    `Name` VARCHAR(45) NULL, 
    `EntryFee` TINYINT(1) NULL, 
    `ParentLocationID` INT NULL, 
    PRIMARY KEY (`LocationID`)); 

CREATE TABLE `flights` (
    `FlightNumber` INT NOT NULL, 
    `ID` INT NULL, 
    `LocationID` INT NULL, 
    PRIMARY KEY (`FlightNumber`) , 
    INDEX `fk_purchases_buyers_idx` (`LocationID` ASC) , 
    INDEX `fk_flights_people1_idx` (`ID` ASC) , 
    CONSTRAINT `fk_purchases_buyers` 
    FOREIGN KEY (`LocationID`) 
    REFERENCES `locations` (`LocationID`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION, 
    CONSTRAINT `fk_flights_people1` 
    FOREIGN KEY (`ID`) 
    REFERENCES `people` (`ID`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION); 

INSERT INTO `people` (`ID`, `Name`) VALUES 
(1, 'John'), 
(2, 'Tom'), 
(3, 'Kate'), 
(4, 'Bob'), 
(5, 'Mike'), 
(6, 'Bill'), 
(7, 'Ryan'); 

INSERT INTO `locations` (`LocationID`, `Name`, `EntryFee`, `ParentLocationID`) VALUES 
(500, 'NL - NoFee', 0, NULL), 
(501, 'Amsterdam (NL) - Fee', 1, 500), 
(502, 'Rotterdam (NL) - NoFee', 0, 500), 
(503, 'ITA - Fee', 1, NULL), 
(504, 'Rome (ITA) - Fee', 1, 503), 
(505, 'Venice (ITA) - NoFee', 0, 503); 

INSERT INTO `flights` VALUES 
(100, 1, 500), 
(101, 1, 502), 
(102, 2, 500), 
(103, 2, 501), 
(104, 3, 500), 
(105, 4, 501), 
(106, 5, 502), 
(107, 6, 503), 
(108, 7, 503), 
(109, 7, 505), 
(110, 6, 502); 

/INSERTをCREATE:私は、この例では、保管の国の意味で完全に論理的ではないと知っています同じテーブルにある都市と国と都市への飛行記録を持っています。しかし、これは単なる例です。少なくともt1.col1のようなものより読みやすい。

+0

だからあなたの質問はどのようにあります場所の親階層を歩き回る? –

+1

私はあなたにヒントを与えます:第2の条件は第1の条件を包含する。 –

+0

@RC。階層は、この例ではかなり平坦です(最大1レベルの深さ)。これは国>都市の例に完全に反映されています。都市には親の国がありますが、都市や国の中に都市は存在できません。問題は有料のフライトと関連するフリーフライトのみを選択することです。 –

答えて

3

動作するはずです後:

SELECT F.FlightNumber, P.ID, P.Name, F.LocationID, LOC.Name AS Loc_Name 
FROM flights F 
INNER JOIN people P ON P.ID = F.ID 
INNER JOIN (SELECT L1.LocationID, L1.`Name`, L1.`ParentLocationID` 
      FROM locations L1 
      LEFT JOIN locations L2 ON L1.`ParentLocationID` = L2.LocationID 
      WHERE L1.`EntryFee` = 1 OR L2.`EntryFee` = 1) AS LOC ON LOC.LocationID = F.LocationID 
UNION 
SELECT F.FlightNumber, PAID_FL.ID, PAID_FL.Name, F.LocationID, PAID_FL.Loc_Name 
FROM flights F 
INNER JOIN (SELECT F.FlightNumber, P.ID, P.Name, LOC.Name AS Loc_Name,LOC.`ParentLocationID` AS LocationID 
      FROM flights F 
      INNER JOIN people P ON P.ID = F.ID 
      INNER JOIN (SELECT L1.LocationID, L1.`ParentLocationID`, L2.`Name` 
         FROM locations L1 
         LEFT JOIN locations L2 ON L1.`ParentLocationID` = L2.LocationID 
         WHERE L1.`EntryFee` = 1 OR L2.`EntryFee` = 1) AS LOC ON LOC.LocationID = F.LocationID) PAID_FL ON F.ID = PAID_FL.ID AND F.LocationID = PAID_FL.LocationID 
+0

CptMisery:あなたのクエリは、(その国の有料都市に別のフライトを行った場合は、国へのフライト(有料かどうか)を除く)。これで、その国の未払都市へのフライトが返されます。 – meet

+0

CptMiseryのアプローチがそれらを返さない理由を説明できますか?私は、説明で提供したデータセットがすべての可能なリレーションの組み合わせをカバーしているわけではないことを認めますが、このデータセットでは両方のアプローチが同じ行を返します。 UNIONのために違う順序で注文されている場合を除き... –

+0

INSERT INTO 'flights' VALUES(110、5、501)これを行うとあなたが知るでしょう – meet

0
SELECT x.* 
    , COALESCE(y.entryfee,x.entryfee,0) fee 
    FROM locations x 
    LEFT 
    JOIN locations y 
    ON y.parentlocationid = x.locationid; 
+0

これはすべての場所を返します。理解できません。これはどのように役立ちますか? –

+0

これは、その場所パスのどの部分にもentryfeeがあるすべての場所に対して1を返します(クエリを少し詰め込んだためではありません)。 – Strawberry

2

私はどんな検査をしなかったが、私はこれがうまくいくと思う:

SELECT F.FlightNumber, P.ID, P.Name, L.LocationID, L.Name 
FROM flights F 
INNER JOIN people P ON P.ID = F.ID 
INNER JOIN locations L ON L.LocationID = F.LocationID 
WHERE P.ID IN (SELECT F.ID FROM flights F INNER JOIN locations L ON L.LocationID = F.LocationID WHERE L.EntryFee = 1) 

See it at SQLFiddle.com.

+0

どこかにエラーがあります。 MySQLは5行目で 'near '以外のデバッグ情報を多く与えません。それを確認できますか? sqlfiddle.com tot test liveもありますが、今日は動作しませんでした(昨日のこと):/ –

+0

以前はsqlfiddleを使用していませんでした。だから私はちょうどそこに行ったことをどのように共有するのかまだ分かりませんが、このクエリは上記のDBスキーマを使って動作します。編集:私はまた、私のクエリのいくつかの列の大文字を変更しました。 – CptMisery

+0

@ Alph.Devこのエラーは、通常、開いているか閉じているかっこの数が等しくないことを意味します。 – Strawberry