#sql #select
#sql #выберите
Вопрос:
Мне нужно написать запрос для получения большого списка идентификаторов.
Мы поддерживаем многие серверные части (MySQL, Firebird, SQLServer, Oracle, PostgreSQL …), поэтому мне нужно написать стандартный SQL.
Размер набора идентификаторов может быть большим, запрос будет сгенерирован программно. Итак, каков наилучший подход?
1) Написание запроса с использованием В
SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)
Мой вопрос вот в чем. Что произойдет, если n будет очень большим? Кроме того, как насчет производительности?
2) Написание запроса с использованием ИЛИ
SELECT * FROM TABLE WHERE ID = id1 OR ID = id2 OR ... OR ID = idn
Я думаю, что у этого подхода нет предела n, но как насчет производительности, если n очень велико?
3) Написание программного решения:
foreach (var id in myIdList)
{
var item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " id);
myObjectList.Add(item);
}
Мы столкнулись с некоторыми проблемами при таком подходе, когда к серверу базы данных запрашивается по сети. Обычно лучше выполнить один запрос, который извлекает все результаты, чем делать много маленьких запросов. Возможно, я ошибаюсь.
Каким было бы правильное решение этой проблемы?
Комментарии:
1. Вариант 1 значительно сокращает время ответа SQL Server, выбирая идентификаторы 7k, некоторые из которых не существовали. Обычно запрос занимал около 1300 мс, при использовании он сокращается до 80 мс
IN
! Я сделал свое решение в качестве вашего решения 1 3. Просто последний запрос был одной длинной строкой запроса, отправленной в SQL для выполнения.
Ответ №1:
Вариант 1 является единственным хорошим решением.
Почему?
-
Вариант 2 делает то же самое, но вы повторяете имя столбца много раз; кроме того, механизм SQL не сразу узнает, что вы хотите проверить, является ли значение одним из значений в фиксированном списке. Однако хороший движок SQL мог бы оптимизировать его, чтобы иметь такую же производительность, как с
IN
. Однако все еще существует проблема с удобочитаемостью… -
Вариант 3 просто ужасен с точки зрения производительности. Он отправляет запрос в каждом цикле и загружает базу данных небольшими запросами. Это также предотвращает использование каких-либо оптимизаций для «значения является одним из значений в заданном списке»
Комментарии:
1. Я согласен, но обратите внимание, что список in ограничен во многих RDM, и поэтому вам нужно, чтобы мы использовали решение @Ed Guiness, но здесь временные таблицы действительно различаются в разных СУБД. (Эффективно для сложных задач вы не можете использовать только чистый стандартный SQL)
2. @ThiefMaster если таблица содержит n строк, а список содержит m элементов, является ли сложность n * m, если в столбце ID нет индекса?
Ответ №2:
То, что предложил Эд Гиннесс, действительно повышает производительность, у меня был запрос, подобный этому
select * from table where id in (id1,id2.........long list)
что я сделал :
DECLARE @temp table(
ID int
)
insert into @temp
select * from dbo.fnSplitter('#idlist#')
Затем inner объединил временную таблицу с основной таблицей :
select * from table inner join temp on temp.id = table.id
И производительность значительно улучшилась.
Комментарии:
1. Привет, является ли fnSplitter функцией из MSSQL? Потому что я не смог его найти.
2. Это не стандартная вещь. Они должны означать, что они написали эту функцию для этой цели или, например, имели приложение, которое уже предоставило ее.
3. fnSplitter — это функция, созданная Ritu, вы можете найти похожую в Интернете / Google
Ответ №3:
Альтернативным подходом может быть использование другой таблицы для хранения значений id. Затем эта другая таблица может быть объединена внутри вашей ТАБЛИЦЫ, чтобы ограничить возвращаемые строки. Это будет иметь главное преимущество в том, что вам не понадобится динамический SQL (проблематичный в лучшие времена), и у вас не будет бесконечно длинного предложения IN .
Вы бы сократили эту другую таблицу, вставили большое количество строк, затем, возможно, создали индекс, чтобы повысить производительность объединения. Это также позволило бы вам отделить накопление этих строк от извлечения данных, возможно, предоставляя вам больше возможностей для настройки производительности.
Обновление: Хотя вы могли бы использовать временную таблицу, я не имел в виду, что вы должны или даже обязаны. Постоянная таблица, используемая для временных данных, является распространенным решением, достоинства которого выходят за рамки описанного здесь.
Комментарии:
1. Но как бы вы передали список идентификаторов, которые вам нужны? (Видя, что вы не можете выбрать диапазон или что-то в этом роде).
2. @raam86: список идентификаторов мог быть получен с помощью
select
инструкции в другой таблице. Список передается как другая таблица, с которой выinner join
работаете.
Ответ №4:
Первый вариант, безусловно, лучший вариант.
SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)
Однако, учитывая, что список идентификаторов очень огромен, скажем, миллионы, вам следует учитывать размеры блоков, как показано ниже:
- Разделите список идентификаторов на фрагменты с фиксированным числом, скажем, 100
- Размер блока должен определяться на основе объема памяти вашего сервера
- Предположим, у вас 10000 идентификаторов, у вас будет 10000/100 = 100 блоков
- Обрабатывать по одному фрагменту за раз, что приводит к 100 вызовам базы данных для select
Почему вы должны разделять на куски?
Вы никогда не получите исключение переполнения памяти, которое очень распространено в сценариях, подобных вашему. У вас будет оптимизированное количество обращений к базе данных, что приведет к повышению производительности.
Для меня это всегда работало как шарм. Надеюсь, это сработает и для моих коллег-разработчиков 🙂
Ответ №5:
Выполнение команды SELECT * FROM MyTable, где идентификатор в (), в таблице SQL Azure с 500 миллионами записей привело к времени ожидания > 7 минут!
Вместо этого это немедленно вернуло результаты:
select b.id, a.* from MyTable a
join (values (250000), (2500001), (2600000)) as b(id)
ON a.id = b.id
Используйте соединение.
Ответ №6:
В большинстве систем баз данных IN (val1, val2, …)
и ряд OR
систем оптимизированы по одному и тому же плану.
Третьим способом было бы импортировать список значений во временную таблицу и объединить ее, что более эффективно в большинстве систем, если значений много.
Возможно, вы захотите прочитать эти статьи:
Ответ №7:
Я думаю, вы имеете в виду SQLServer, но в Oracle у вас есть жесткое ограничение на количество элементов IN, которые вы можете указать: 1000.
Комментарии:
1. Даже SQL Server перестает работать после ~ 40 КБ элементов. Согласно MSDN: включение чрезвычайно большого количества значений (многих тысяч) в предложение IN может потреблять ресурсы и возвращать ошибки 8623 или 8632. Чтобы обойти эту проблему, сохраните элементы в списке IN в таблице.
Ответ №8:
Пример 3 был бы худшим исполнителем из всех, потому что вы запускаете базу данных бесчисленное количество раз без видимой причины.
Загрузка данных во временную таблицу и последующее объединение в ней были бы, безусловно, самыми быстрыми. После этого IN должен работать немного быстрее, чем группа редакторов.
Ответ №9:
- Для первого варианта
Добавьте идентификаторы во временную таблицу и добавьте внутреннее соединение с основной таблицей.
CREATE TABLE #temp (column int)
INSERT INTO #temp (column)
SELECT t.column1 FROM (VALUES (1),(2),(3),...(10000)) AS t(column1)
Ответ №10:
Попробуйте это
SELECT Position_ID , Position_Name
FROM
position
WHERE Position_ID IN (6 ,7 ,8)
ORDER BY Position_Name