从具有交叉引用和自引用的多个表中进行选择[脑裂解器]

时间:2017-01-03 17:09:11

标签: mysql sql

任何接受SQL挑战的人?因为到目前为止我所做的一切努力都不足以简化问题并将其置于一个问题......

在这里。在下面的示例中,我们需要包括:

  • 所有付费航班
  • 飞往某个国家/地区的航班(有偿或无偿)如果某人已经再搭乘该航班前往该国的付费城市

这已经很棘手,但还有更多。

  • 如果一个人飞到一个没有入场费的城市,但它位于一个 如果该国有收费,该航班仍被视为已付费 并且也必须包括在内。

enter image description here 编辑: 我已添加航班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)

创建/ INSERT

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

不重要的说明:我知道这个例子并不完全合乎逻辑,因为它将国家和城市存放在同一张桌子中,并将航班记录存放到一个国家和一个城市。但这只是一个例子。至少它比t1.col1更具可读性 - 就像其他东西一样。

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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.

答案 2 :(得分:0)

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