Должен ли я использовать Top (1) в подзапросе - PullRequest
4 голосов
/ 30 января 2009

Пример запроса:

select * 
from A join B on A.ID = B.SOMEVALUE
where A.VALUE="something" and
B.ID = 
       (select ID from B where SOMEVALUE = A.ID and 
              THISDATE = (select max(SOMEDATE) from B where ...))

Итак, если вы можете читать SQL, вы должны увидеть, что я делаю пару коррелированных подзапросов, чтобы сузить результаты объединения. (и да, это ужасно упрощенно).

В некоторых случаях подзапрос:

select ID from B where SOMEVALUE = A.ID and 
    THISDATE = (select max(SOMEDATE) from B where ...)

может вернуть более 1 значения, что приводит к ошибке

"Подзапрос возвратил более 1 значения. Это не разрешено, когда подзапрос следует =,! =, <, <=,>,> = или когда подзапрос используется как выражение ".

что я полностью ожидаю. Это, очевидно, не очень хорошая вещь, и у меня есть код, чтобы (надеюсь) предотвратить попадание этих дубликатов в базу данных (т. Е. В таблице B должна быть только 1 строка, соответствующая

SOMEVALUE = A.ID and max(SOMEDATE)

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

Итак, теперь на мой вопрос:

Было бы лучше изменить первый подзапрос на

select top 1 * from B ...

чтобы пользователь не мог увидеть ошибку, когда / если (возможно, никогда) возникнет эта ситуация, или пусть ошибка возникнет. Я склоняюсь к тому, чтобы не добавлять верхний оператор и не допускать появления ошибки, а не позволять пользователю видеть потенциально неверные данные. Мне интересно, есть ли у кого-нибудь мысли о наилучшей практике в такой ситуации ...

Ответы [ 6 ]

11 голосов
/ 30 января 2009

Обычно TOP 1 - хорошая идея.

Рассмотрим большую таблицу с миллионами строк без индекса для соответствующего столбца, однако вы ищете только одну строку.

SELECT TOP 1 будет означать, что сканирование таблицы останавливается, как только будет найден один элемент.

Без TOP 1 сканирование таблицы будет продолжаться до самого конца.

Как и все, что связано со сканированием (или грубой силой) для поиска. При использовании TOP 1 он должен быть в среднем на 50% быстрее, чем при использовании TOP 1.

Однако, в зависимости от того, что вам нужно вернуть, реальный прирост производительности обычно можно получить с помощью EXISTS.

Вместо написания

SELECT * FROM table t
WHERE t.id = (SELECT TOP 1 foreignid from table2)

Вы можете использовать

SELECT * FROM table t
WHERE EXISTS (SELECT 1 from table2 WHERE foreignid = t.id)
4 голосов
/ 30 января 2009

Практика кодирования: размещение top1 в подзапросе, который необходим для возврата одного значения.

Плюсы:

  • Позволяет продолжить выполнение.
  • Позволяет запросу игнорировать незначительные дополнительные значения.
  • Останавливает поиск новых значений, когда найдено первое значение (исполнитель).

Минусы:

  • Запрещает запросу жаловаться на значительные дополнительные значения.
  • Если в запросе не указан порядок, элемент управления top1 не будет выбран. Это может привести к другому результату при повторном выполнении запроса.
4 голосов
/ 30 января 2009

Почему вы объединяете таблицы A и B ... затем выбираете из B в подзапросе ... и сравниваете столбец в A ???

Не будет ли это эквивалентно:

select * 
from A join B on A.ID = B.SOMEVALUE
where A.VALUE="something" and
THISDATE = (select max(SOMEDATE) from B where ...))

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

select top 1 * 
from A join B on A.ID = B.SOMEVALUE
where A.VALUE="something" 
Order by B.SOMEDATE DESC
1 голос
/ 30 января 2009

Я бы рекомендовал подход ТОП 1. Это, вероятно, поможет производительности (вряд ли повредит).

Идея, что без нее вы будете ловить ошибки, достойна уважения, но немного неуместна Если ошибка возникнет здесь через несколько месяцев, она НЕ будет интуитивно понятна, почему она произошла или что происходит. Вместо этого я бы сосредоточился на обеспечении целостности данных в других местах.

0 голосов
/ 30 января 2009

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

select ID from B where SOMEVALUE = A.ID and 
THISDATE IN (select max(SOMEDATE) from B where ...)

(обратите внимание на IN)

Во-вторых, вы могли бы сделать

select TOP 1 ID from B where SOMEVALUE = A.ID and 
THISDATE IN (select max(SOMEDATE) from B where ...)

, если вы ищете только одно значение.

Или, как вы сказали, вы можете изменить подзапрос на SELECT TOP 1, что, на мой взгляд, нормально, поскольку до тех пор, пока предложение WHERE не зависит от внешнего запроса, оно, вероятно, выполнит вложенный запрос. только один раз и полагаться на статически сохраненное значение оттуда, как это

select ID from B where SOMEVALUE = A.ID and 
THISDATE = (select TOP 1 * from B where ...)

Так что есть несколько вариантов, но я не совсем уверен в их эффективности.

0 голосов
/ 30 января 2009

Почему бы не использовать LIMIT 1 в конце вашего дополнительного выбора?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...