Введение
Иногда вместо соединения вы можете намеренно использовать скалярный подзапрос, чтобы проверить, что найдено не более одной строки. Например, у вас может быть этот запрос для поиска национальности в некоторых person
строках.
select p.name,
c.iso
from person p
join person_country_map pcm
on p.id = pcm.person
join country c
on pcm.country = c.id
where p.id in (1, 2, 3)
Теперь предположим, что person_country_map
не является функциональным отображением. Данный человек может отображаться в более чем одной стране, поэтому объединение может найти более одной строки. Или, действительно, человек может вообще не быть в таблице сопоставления, по крайней мере, в том, что касается каких-либо ограничений базы данных.
Но для этого конкретного запроса я знаю, что у людей, которых я запрашиваю, будет ровно одна страна. Это предположение, на котором я основываю свой код. Но я хотел бы проверить это предположение, где это возможно - чтобы, если что-то пойдет не так, и я в конечном итоге попытаюсь выполнить этот запрос для человека с более чем одной страной или без строки сопоставления страны, он умрет.
Добавление проверки безопасности не более одной строки
Чтобы проверить наличие более одной строки, вы можете переписать соединение как скалярный подзапрос:
select p.name,
(
select c.iso
from person_country_map pcm
join country c
on pc.country = c.id
where pcm.person = p.id
) as iso
from person p
where p.id in (1, 2, 3)
Теперь СУБД выдает ошибку, если человек запрашивал карты двух или более стран. Он не вернет несколько записей для одного и того же человека, как при простом соединении. Так что я могу спать немного легче, зная, что этот случай ошибки проверяется еще до того, как какие-либо строки будут возвращены в приложение. Как осторожный программист, я, конечно, тоже могу проверить приложение.
Можно ли проверить безопасность не строки?
Но что делать, если в person_country_map для человека нет строки? В этом случае скалярный подзапрос вернет null, что делает его примерно эквивалентным левому соединению.
(В качестве аргумента предположим, что внешний ключ от person_country_map.country к country.id и уникальный индекс для country.id, так что это конкретное соединение всегда будет успешным и найдет ровно одну строку страны.)
Мой вопрос
Есть ли способ выразить в SQL, что мне нужен один и только один результат? Обычный скалярный подзапрос - «ноль или единица». Я хотел бы иметь возможность сказать
select 42, (select exactly one x from t where id = 55)
и запрос не будет выполнен во время выполнения, если подзапрос не вернет строку. Конечно, приведенный выше синтаксис является вымышленным, и я уверен, что это будет не так просто.
Я использую MSSQL 2008 R2, и на самом деле этот код находится в хранимой процедуре, поэтому при необходимости я могу использовать TSQL. (Очевидно, что предпочтительнее использовать обычный декларативный SQL, так как его можно использовать и в определениях представлений.) Конечно, я могу выполнить exists
проверку или выбрать значение в переменной TSQL, а затем явно проверить его на наличие null и т. Д. Я мог бы даже выделить результаты во временную таблицу, а затем построить уникальные индексы для этой таблицы в качестве проверки. Но нет ли более удобочитаемого и элегантного способа отметить мое предположение, что подзапрос возвращает ровно одну строку, и проверить это предположение СУБД?
GetCountryForUser( UserId )
. Он либо возвращает уникальныйCountryId
для указанного пользователя, либо жалуется. К сожалению,RaIsError
иThrow
не разрешены в UDF, поэтому остается что-то вроде this. - person HABO   schedule 06.03.2015