SQL-запрос для получения строки, основанной на другой строке в той же таблице - PullRequest
2 голосов
/ 19 мая 2011

У меня есть таблица statuses со столбцами id, name и parent_id.

Я хочу получить строку с parent_id = 9.
Если такой строки нет, я хочу получить строку с id = 9.

Можно ли получить это одним запросом?

Ответы [ 5 ]

2 голосов
/ 19 мая 2011
SELECT
  ISNULL(SP.id, S.parent_id)
FROM
  Statuses S
LEFT JOIN 
  Statuses SP on S.parent_id=SP.id
WHERE
  S.parent_id = 9
2 голосов
/ 19 мая 2011

Вы можете выбрать обе строки, упорядочить совпадения parent_id сверху и взять верхнюю строку:

select  *
from    statuses
where   parent_id = 9 or id = 9
order by
        case when parent_id = 9 then 1 else 2 end
limit   1

Если id не является первичным ключом, вы можете попробовать union, где вторая половина выполняется, только если первая половина пуста:

select  *
from    statuses
where   parent_id = 9
union all
select  *
from    statuses
where   id = 9
        and not exists
        (
        select  *
        from    statuses
        where   parent_id = 9
        )
1 голос
/ 19 мая 2011

Любой из ответов @ Andomar будет работать. Просто убедитесь, что вы используете EXPLAIN ANALYZE , чтобы выяснить, какой из них на самом деле быстрее, когда вы заполнили свой стол. На самом деле, было бы неплохо, если бы вы могли обновить свой пост, результаты которого были быстрее, основываясь на нескольких ОБЪЯСНИТЬ АНАЛИЗ .

  1. ВЫБРАТЬ ВЕРСИЮ ();
  2. ВЫБРАТЬ СЧЕТЧИК (*) ИЗ Состояний;
  3. И последняя строка из EXPLAIN ANALYZE $ {QUERY};
0 голосов
/ 11 августа 2012

Проще, быстрее:

SELECT * FROM COALESCE(
     (SELECT s FROM statuses s WHERE parent_id = 9 LIMIT 1)
    ,(SELECT s FROM statuses s WHERE id = 9 LIMIT 1)
    )

Если ни один из идентификаторов не существует, этот запрос возвращает строку типа statuses, заполненную NULL значениями, в то время как другие решения здесь пока возвращают без строки . Потому что это то, что происходит, когда вы звоните
SELECT * FROM NULL::statuses.

Объяснение

COALESCE не может работать с rows per se, подзапрос вместо переменной должен возвращать только один столбец. Если вы попробуете:

<strike>SELECT COALESCE(
     (SELECT * FROM statuses WHERE parent_id = 9 LIMIT 1)
    ,(SELECT * FROM statuses WHERE id = 9 LIMIT 1)
    )
</strike>

Вы получаете

ОШИБКА: подзапрос должен возвращать только один столбец

Однако для каждой таблицы в PostgreSQL существует составной тип с таким же именем. Вы можете сделать так, чтобы подзапросы возвращали всю строку как составной тип и впоследствии расширяли тип. Вот что делает запрос.

Я выполнил быстрый тест с похожей реальной таблицей из 70 тыс. Строк в PostgreSQL 9.1.4, оба индекса с уникальным идентификатором проиндексированы . Я проверил все три случая: parent_id существует, id существует, ни один не существует. Этот запрос немного быстрее, чем другое решение здесь. Я также попробовал фиксированный эквивалент неправильного ответа @ Parkyprg и упрощенную версию того, что @Endy и @Andomar опубликовали:

SELECT  *
FROM    statuses
WHERE   parent_id = 9 OR id = 9
ORDER   BY (id <> 9);

.. что может быть быстрее для таблиц без индексов.

0 голосов
/ 19 мая 2011
select * from statuses
  where parent_id = 9 or id = 9
  order by case when parent_id = 9 then 1 else 2 end
  limit 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...