Как проверить, если подзапрос имеет точно один отдельный результат и указанное значение кратко?

10

Я обнаружил, что пишу следующее:

select 'yes' 
where exists(select * from foo where val=1)
and not exists(select * from foo where val<>1);

и интересно, есть ли более краткий способ, не жертвуя слишком большой читабельностью.

Я нашел один способ, который я публикую в качестве ответа, но я не совсем доволен им и был бы очень заинтересован в альтернативах

В этом случае valуникален внутри foo- дубликатов нет

Джек говорит, попробуйте topanswers.xyz
источник
Правильно ли я понимаю, что вы хотите ровно одну строку в результате подзапроса?
Эрвин Брандштеттер
Какой подзапрос?
Джек говорит, попробуйте topanswers.xyz
Тот, который вы упоминаете в названии. Я не был уверен, должен ли это быть один результат после или перед "отличным".
Эрвин Брандштеттер,
Ах, да, тот :) Я довольно смущающе ссылался на подзапрос в моем ответе - ваш гораздо более конкретный и гибкий, например, вы также можете использовать count(distinct val), хотя в моем случае из реальной жизни это не имеет значения
Джек говорит, попробуй topanswers.xyz

Ответы:

8

Краткий, быстрый (особенно со многими строками), мой любимый в отношении читабельности и будет работать с дупсами:

SELECT count(*) = 1 AND min(val) = 1 FROM foo;

Возвращает TRUE/ FALSE.. или NULL- только в случае ровно одной строки с val IS NULL, потому что count()никогда не возвращает NULLили нет строки.

Второе 1в этом примере просто совпадает с первым из-за вашего примера.


Запрос в вопросе терпит неудачу со NULLзначениями. Рассмотрим простую демонстрацию:

CREATE TABLE foo (id int, val int);
INSERT INTO foo VALUES (1, 1),(2, NULL);

SELECT 'yes' 
WHERE      EXISTS(SELECT * FROM foo WHERE val =  1)
AND    NOT EXISTS(SELECT * FROM foo WHERE val <> 1);

IS DISTINCT FROMбы исправить это, но он все равно может потерпеть неудачу с дубликатами в val- что вы исключили для этого случая.


Ваш ответ работает отлично.
Возвращает 'yes'/ нет строки.

Я бы предпочел эту более короткую форму. Не забывайте, что PostgreSQL (в отличие от Oracle) имеет правильный booleanтип .

SELECT array_agg(val) = array[1] FROM foo;

Возврат TRUE/ FALSE/ NULL.

Эрвин Брандштеттер
источник
отлично, спасибо, я знал, что будет лучший способ :)
Джек говорит попробуйте topanswers.xyz
5

Вариант ответа @ Эрвина. Нет COUNT()вообще, только MIN()и MAX(). Это может быть немного более эффективно с большой таблицей и (не в вашем случае) дублировать val:

SELECT MIN(val) = 1 AND MAX(val) = 1 FROM foo;
ypercubeᵀᴹ
источник
+1 спасибо. Конечно, он обрабатывает нули и дубликаты по-разному (если они были)
Джек говорит, что попробуйте topanswers.xyz
@ Джек: Да. Ваш стол имеет нулевые значения? Или вы хотите ответы для обоих случаев (с и без)?
ypercubeᵀᴹ
нет мой не - я могу использовать либо :)
Джек говорит попробуйте topanswers.xyz
Будет намного быстрее на больших таблицах с совпадающим индексом, но работает одинаково при отсутствии такого индекса - как при тестировании результатов запроса.
Эрвин Брандштеттер,
3
select 'yes' where (select array_agg(val) from foo)=array[1];
Джек говорит, попробуйте topanswers.xyz
источник
1

Это один возвращается true, falseили пустой результат:

 select j.val is null 
 from foo left join foo as j on j.val <> foo.val 
 where foo.val = 1 limit 1;
grayhemp
источник
на первый взгляд, это не возвращает, falseесли есть значения fooгде val<>1?
Джек говорит, что попробуйте topanswers.xyz
@JackDouglas Ой, прости. Я неправильно понял задачу с первого раза. Исправлена.
Grayhemp
Работает - кроме NULLкак со значением, которое не было исключено в этом случае.
Эрвин Брандштеттер,
@ErwinBrandstetter NULLможет быть решен с использованием, IS [NOT] DISTINCT FROMя думаю.
Grayhemp
1
@grayhemp: не в этом случае. LEFT JOIN foo j ON j.val <> foo.valне может обнаружить строку с j.val IS NULLначала. Если вы включите его вместе с ON j.val IS DISTINCT FROM foo.valвами, то вам нужно будет проверить другой столбец с jописанием, NOT NULLчтобы разделить два случая. Но дополнительный столбец не определен.
Эрвин Брандштеттер,