SQL: в подзапросе слишком много столбцов

Я пытаюсь сделать запрос с postgresql. База данных содержит два отношения: "царство", в которое входят некоторые английские короли, и "династия", в которой содержатся некоторые люди из дикой природы Стюарта

Отношение "царство" включает в себя имя короля и когда его царство начиналось и заканчивалось. Отношение "династия" включает имя, пол, рождение и смерть.

То, что я пытаюсь запросить, это король, который был самым старым, когда он умер.

По моему запросу я получаю эту ошибку в LINE 3 (NOT IN): subquery has too many columns

Это запрос:

SELECT kingdom.king, dinasty.birth, dinasty.death
FROM kingdom, dinasty
WHERE kingdom.king = dinasty.name AND kingdom.king NOT IN
(
    SELECT DISTINCT R1.king, R1.birth, R1.death
    FROM
    (
        SELECT DISTINCT R1.king, D1.birth, D1.death
        FROM kingdom AS R1, dinasty AS D1, dinasty AS D2
        WHERE R1.king=D1.name
    ) AS R1, 
    (
        SELECT DISTINCT R1.king, D1.birth, D1.death
        FROM kingdom AS R1, dinasty AS D1, dinasty AS D2
        WHERE R1.king=D1.name
    ) AS R2
    WHERE R1.death-R1.birth < R2.death-R2.birth
);

То, что находится внутри NOT IN, является правильным.

Ответы

Ответ 1

Вы проецируете три столбца в свой подзапрос, но сравниваете один из них в предложении IN. Выберите только нужный столбец (r1.king) для IN в подзапросе:

SELECT kingdom.king, dinasty.birth, dinasty.death
FROM kingdom, dinasty
WHERE kingdom.king = dinasty.name AND kingdom.king NOT IN
(
    SELECT DISTINCT R1.king
    FROM
    (
        SELECT DISTINCT R1.king, D1.birth, D1.death
        FROM kingdom AS R1, dinasty AS D1, dinasty AS D2
        WHERE R1.king=D1.name
    ) AS R1, 
    (
        SELECT DISTINCT R1.king, D1.birth, D1.death
        FROM kingdom AS R1, dinasty AS D1, dinasty AS D2
        WHERE R1.king=D1.name
    ) AS R2
    WHERE R1.death-R1.birth < R2.death-R2.birth
);

Ответ 2

Как уже было сказано, количество столбцов не совпало, но есть гораздо более простой способ написать это.

При написании запросов лучше всего их проработать поэтапно. Во-первых, вам нужно знать, сколько лет каждому королю было, когда они умерли:

SELECT *, death-birth AS lived_for FROM dinasty

Теперь, когда у вас есть это, вы можете использовать DISTINCT ON, чтобы найти самого долгоживущего короля для каждого королевства.

SELECT DISTINCT ON( name ) name, birth, death, lived_for
  FROM (
      SELECT *, death-birth AS lived_for FROM dinasty
    ) a
  ORDER BY name, lived_for DESC
;

Отличительная строка будет принимать первую строку для каждого отдельного значения, поэтому важно, чтобы вы связали ее с правильным ORDER BY. Сначала мы заказываем имя династии, а затем, как долго царь жил в порядке убывания. Это означает, что первый король, показанный для каждой династии, будет самым долгоживущим, и это будет запись, которую DISTINCT ON сохранит для каждой династии.

Заметьте, что я также удалил JOIN в kindgom, но вы можете добавить его обратно при необходимости:

SELECT k.*, oldest.*
  FROM (
    SELECT DISTINCT ON( name ) name, birth, death, lived_for
      FROM (
          SELECT *, death-birth AS lived_for FROM dinasty
        ) a
      ORDER BY name, lived_for DESC
    ) oldest
    JOIN kingdom k ON k.king = oldest.name
;

Наконец, если вам когда-либо понадобится использовать несколько столбцов в подвыборке, вы можете использовать конструкцию ROW():

SELECT ...
  FROM table_a
  WHERE ROW(f1, f2, f3) NOT IN (SELECT f1a, f2a, f3a FROM ... )
;