Отримання SELECT для повернення постійного значення, навіть якщо нульові рядки відповідають


15

Розглянемо цю заяву вибору:

SELECT *, 
       1 AS query_id 
FROM players 
WHERE username='foobar';

Він повертає стовпець query_idзі значенням 1разом з іншими стовпцями гравця.

Як би один зробити вище SQL повернення по крайней мере, query_idз 1навіть якщо добірних знахідок немає рядків , які відповідають?

До речі, це PostgreSQL 8.4.

Відповіді:


22
SELECT col1, 
       col2, 
       col3, 
       1 AS query_id 
FROM players 
WHERE username='foobar'
union all 
select null,
       null,
       null,
       1
where not exists (select 1 from players where username = 'foobar');

Або в якості альтернативи ( може бути швидше, оскільки не потрібен другий підбір):

with qid (query_id) as (
   values (1)
) 
select p.*, 
       qid.query_id
from qid 
  left join players as p on (p.useranme = 'foobar');

Ви можете переписати вищезазначене на більш "компактне" представлення:

select p.*, 
       qid.query_id
from (values (1)) as qid (query_id)
  left join players as p on (p.useranme = 'foobar');

Але я думаю, що явний CTE ( with...) є більш читабельним (хоча це завжди в очах того, хто дивиться).


1
Випробовуючи перший приклад, здається, що ВСЕ ключове слово не потрібне?
Натанаель Вайс

2
@NatWeiss: якщо вам потрібно певне замовлення, вам потрібно надати order by. Другий "створює" віртуальну таблицю з рівно одним рядком і одним стовпцем і виконує зовнішнє з'єднання з нею (без будь-якої "реальної" умови приєднання), таким чином ви завжди отримуєте назад принаймні той ряд. Використовувати select *у виробничому коді поганий стиль. Не робіть цього. Завжди перераховуйте потрібні стовпці. select *слід використовувати лише в спеціальних запитах.
a_horse_with_no_name

2
@NatWeiss: про який "альтернативний синтаксис" для "інших приєднань" ви посилаєтесь. І чому ви вважаєте, чому left joinце не читається?
a_horse_with_no_name

2
@NatWeiss: неявна приєднання до пункту де неправильний стиль кодування, і його слід уникати. Це може призвести до небажаних декартових приєднань без помилки. І це чітко розділяє дві (реляційні) концепції приєднання та фільтрації
a_horse_with_no_name

4
re: модифікатор "all" клавіатури "union" не потрібен: UNION ALLіноді може бути більш ефективним, ніж UNION, оскільки ви чітко повідомляєте планувальнику запитів, що або ви очікуєте, що з UNIONредакційних запитів не буде повторюваних рядків, або якщо Ви хочете, щоб вони були виведені. Без ALLмодифікатора він передбачає, що ви хочете видалити повторювані рядки (лише один з кожного повернутого), як і DISTINCTключове слово, і гарантувати, що, можливо, знадобиться вдатися до + переглядати результати додатковий час. Так що використовуйте ALLз , UNIONякщо ви спеціально не потрібно вихідний рядки дедуплікаціі.
Девід Спіллетт

7

Якщо ви очікуєте лише одного чи нульового ряду назад, це також спрацює:

SELECT
  max(col1) col1,
  max(col2) col2, 
  1 AS query_id 
FROM
  players 
WHERE
  username='foobar';

Це поверне один рядок зі всіма значеннями, що мають нульові значення, крім query_id, якщо не знайдено рядка.


2
Гарний трюк. Єдиним недоліком є ​​те, що значення для col1 та col2 можуть не належати до одного рядка, якщо більше одного відповідає умовіusername = 'foobar'
a_horse_with_no_name

1
Чи може coalesce () також використовуватися таким чином?
Натанаель Вайс

1
Coalesce не генерує рядок, де жодна не проектується зі таблиці.
Девід Олдрідж

1
@a_horse_with_no_name так, хоча імена таблиці та стовпців говорять про те, що присудок знаходиться на ключі-кандидата для таблиці, тому нуль або один рядок буде прогнозовано.
Девід Олдрідж

3

Тут знаходяться запізнення, але ось синтаксис, який працює (принаймні в 9.2, не пробував попередні версії).

SELECT (COALESCE(a.*,b.*::players)).*
FROM ( SELECT col1,  col2,  col3, 1 AS query_id 
       FROM players WHERE username='foobar' ) a
RIGHT JOIN (select null col1, null col2, null col3, 1 col4) b
ON a.query_id = b.col4;

Повертає "порожній" рядок лише тоді, коли весь вміст "a" є нульовим.

Насолоджуйтесь. / бітхед


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.