metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2014-08-09 08:06 pm

SQL 'in' clause

Скажите мне такую вещь: список значений в in выражении в SQL можно вообще передать параметром, хоть в какой-нибудь СУБД?

В Firebird нельзя, ну я как-то не интересовался, как оно в других СУБД, использовал временную табличку и подзапрос с where exists (select 1 from TmpTbl ...) но что-то вот сегодня пришло в голову - а почему собственно нельзя?

[identity profile] tzirechnoy.livejournal.com 2014-08-09 05:11 pm (UTC)(link)
Видимо, потому, что ни у одного API нет варианта подставлять список вместо placeholderа.

[identity profile] metaclass.livejournal.com 2014-08-09 05:16 pm (UTC)(link)
Да хотя бы строку сунуть и один раз ее распарсить на список.
Ни у кого нет, хотя фича самоочевидная.

[identity profile] cmotruc.livejournal.com 2014-08-09 05:41 pm (UTC)(link)
в оракле можно передать коллекцию:
select * from user_table1 where id in (select * from table(:my_collection))

table(:my_collection) -- это оператор, который коллекцию приводит к таблице

[identity profile] dennab.livejournal.com 2014-08-09 05:44 pm (UTC)(link)
Нэа. Приходицца генерить нное количество вопросов, да.

[identity profile] pit0n.livejournal.com 2014-08-09 06:06 pm (UTC)(link)
в MSSQL передаешь строкой или XML (потом конкатенируешь в строку), конструируешь запрос в строке и делаешь exec sql

[identity profile] brainslugs.blogspot.com (from livejournal.com) 2014-08-09 06:07 pm (UTC)(link)
Ага и в постгресе тоже есть такое, там этот оператор unnest() называется

[identity profile] off roaming (from livejournal.com) 2014-08-09 06:14 pm (UTC)(link)
Не надо строк и exec sql. Команду OPENXML ввели в 2005 версии.

[identity profile] vromanov.livejournal.com 2014-08-09 06:17 pm (UTC)(link)
Я делал хранимую процедуру, которая принимала строку а выдавала табличку. Вроде через yeld.

[identity profile] jakobz.livejournal.com 2014-08-09 06:19 pm (UTC)(link)
Все делали

[identity profile] swamp-agr.livejournal.com 2014-08-09 06:32 pm (UTC)(link)
c1 IN (a1, a2, ...)
раскрывается как
c1 = a1 or c1 = a2 or ...

Можно сделать так
c1 = ANY(array)
что в принципе равносильно предыдущей записи с отличием, что в ANY можно передавать списки, массивы.

[identity profile] http://users.livejournal.com/_slw/ 2014-08-09 06:51 pm (UTC)(link)
параметром? в SQL? откуда в SQL параметры?
противогаз покусал?

[identity profile] metaclass.livejournal.com 2014-08-09 07:01 pm (UTC)(link)
Всю жизнь там были: sql bind variables

[identity profile] autoench.livejournal.com 2014-08-09 07:03 pm (UTC)(link)
$stmt = $db->prepare("select id from table where gender in (?, ?, ?, ?)");
$stmt->execute(array("male", "female", "transgender", "developer"));

Увы, да(

[identity profile] anonim-legion.livejournal.com 2014-08-09 07:23 pm (UTC)(link)
Да, можно в постгресе, проверено. Использовались jdbc, java 7.

Запрос: select * from data.street where id_street in (select unnest(?)) где ? является параметром

Сам код:
PreparedStatement selectPs = connection.prepareStatement("select * from data.street where id_street in (select unnest(?))");
Integer[] a = new Integer[]{927,929,930,931,932,933,934,935,936,937};
Array a2 = connection.createArrayOf("int",a);
selectPs.setArray(1,a2);

Используется именно Integer, а не int, потому что требуется ссылочный тип. Значение параметра устанавливается по индексу, потому что это голый JDBC.

Функция unnest разворачивает массив в набор строк (http://www.postgresql.org/docs/9.3/static/functions-array.html)

[identity profile] anonim-legion.livejournal.com 2014-08-09 07:25 pm (UTC)(link)
>c1 IN (a1, a2, ...) раскрывается как c1 = a1 or c1 = a2 or ...

Это как-то очень уж неэффективно. Вроде нигде оно так не раскрывается.

[identity profile] vp.livejournal.com 2014-08-09 07:29 pm (UTC)(link)
>противогаз покусал?

Вы просто так хуйню пишете от жары али заказал такое кто?

[identity profile] shaman237.livejournal.com 2014-08-09 07:37 pm (UTC)(link)
В оракле динамическим SQL это решается без проблем

[identity profile] shaman237.livejournal.com 2014-08-09 07:44 pm (UTC)(link)
Разговор об sql, вас понесло в pl/sql

[identity profile] shaman237.livejournal.com 2014-08-09 07:46 pm (UTC)(link)
Именно так раскрывается, поэтому эффективней делать union all, если в IN несколько значений

[identity profile] shaman237.livejournal.com 2014-08-09 07:58 pm (UTC)(link)
Хотя и меня ниже туда же дернуло :)

[identity profile] slavae.livejournal.com 2014-08-09 08:07 pm (UTC)(link)
Пора Еманову писать )
Кажется, ещё на ib.demo.ru я возмущался, что нелья делать группировку как и ордер бай - по номерам полей, и они это сделали.

[identity profile] lakedaemon.livejournal.com 2014-08-09 08:43 pm (UTC)(link)
не совсем то, но есть Table-Valued Parameters в хранимках для MSSQL 2008+

[identity profile] veonn.livejournal.com 2014-08-09 08:45 pm (UTC)(link)
В MSSQL делали похожее через кастом типы вроде
CREATE TYPE [dbo].[IdsTableType] AS TABLE (
[Id] INT NULL);
В дот.нете все работает через SqlMetaData и SqlDataRecord, в общем в итоге можно in засунуть в переменную собственного типа.
Только один забавный глюк - т.к. sql сервер строит планы запросов по имени переменных, то внезапно когда в in 2 значения и 10.000 значений - планы одинаковые.
Бороли это буквально таким кодом
var parameterName = name + GetRangePostfix(records.Count);
который по логарифму делает параметру разные имена :)

[identity profile] swamp-agr.livejournal.com 2014-08-09 09:25 pm (UTC)(link)
Приведите пример того, как по-вашему должна обрабатываться конструкция
IN (a1, a2, ...)

[identity profile] cmotruc.livejournal.com 2014-08-09 09:26 pm (UTC)(link)
любое решение будет расширением sql для соответствующей бд

в примере таки sql, но использующий коллецию pl/sql

[identity profile] anonim-legion.livejournal.com 2014-08-09 09:58 pm (UTC)(link)
Нужно понять, находится ли элемент в списке IN. Это делается либо через хэш, либо двоичным поиском по отсортированному списку IN. Если же просто сравнивать с каждым элементом, то это очевидно дольше хэша или поиска.

[identity profile] Дмитрий Васильев (from livejournal.com) 2014-08-09 10:50 pm (UTC)(link)
В постгресе
select * from foo where id = any(?)

В качеестве параметра передавать array

[identity profile] berezovsky.livejournal.com 2014-08-10 07:19 am (UTC)(link)
По-моему, у тебя в вопросе уже содержится в ответ. То есть это будет попытка запихать задачу SQL в его же параметры. Выходит не операция реляционной алгебры, а пони с хуем во лбу.

[identity profile] nivanych.livejournal.com 2014-08-10 09:33 am (UTC)(link)
Ну слава богу.
Теперь знаю хоть одну точную, достоверную деталь — противогаз не кусается.

[identity profile] berezovsky.livejournal.com 2014-08-10 10:22 am (UTC)(link)
прирос?

[identity profile] swamp-agr.livejournal.com 2014-08-10 11:06 am (UTC)(link)
Как конкретно это делается через хэш?

[identity profile] anonim-legion.livejournal.com 2014-08-10 11:09 am (UTC)(link)
В СУБД - не знаю. В программе я бы построил по значениям в IN обычную хэш-таблицу, где ключом является хэш от значения, значением - само значение. Если хэш совпал, сравниваем напрямую, если сравнение успешно - значит, есть вхождение.

[identity profile] swamp-agr.livejournal.com 2014-08-10 12:42 pm (UTC)(link)
В Oracle, например, что в IN clause, что в нескольких OR, сравниваются хэши, если колонка проиндексирована. Там IN (a1, a2, ...) эквивалентен OR.

В T-SQL чуть ли не в MSDN пишут об эквивалентности запросов.

В PostgreSQL - точно не помню, но в плане выполнения тоже хэши видел, однако думаю, что конструкции эквивалентны.

А когда `IN (SQL statement)` встречается, то тут тот же принцип с той разницей, что в памяти надо хранить результат его выполнения. И PostgreSQL, и Oracle могут оказаться чувствительны к нему, если он достаточно велик, а в памяти они ограничены.

[identity profile] denis dmitriev (from livejournal.com) 2014-08-10 02:07 pm (UTC)(link)
Фигасе, вы же интеллигентный человек. У себя в журнале, вона, не ругаетесь :)
Интеллигентные люди используют эвфемизм who knew.

[identity profile] yantayga.livejournal.com 2014-08-10 03:09 pm (UTC)(link)
Он засасывает

[identity profile] nivanych.livejournal.com 2014-08-10 03:30 pm (UTC)(link)
(Испуганно) Насмерть!?

[identity profile] e3c9bx.livejournal.com 2014-08-10 04:13 pm (UTC)(link)
Березофсский, а все-таки чем твои родетели болели в момент твоего зачания?
ты зря отмалчиваешься.

[identity profile] e3c9bx.livejournal.com 2014-08-10 04:14 pm (UTC)(link)
ты плохой человек. старайся быть добрее.

[identity profile] e3c9bx.livejournal.com 2014-08-10 04:15 pm (UTC)(link)
псих, а что ты тут опять делаешь? иди пей таблеточки.

[identity profile] nivanych.livejournal.com 2014-08-10 04:21 pm (UTC)(link)
> У себя в журнале, вона, не ругаетесь :)
На него нашло что-то русское.

[identity profile] berezovsky.livejournal.com 2014-08-10 04:32 pm (UTC)(link)
Или китайское.

[identity profile] nivanych.livejournal.com 2014-08-10 04:50 pm (UTC)(link)
;-) Вы показываете какую-то хуйню!

[identity profile] sbj-ss.livejournal.com 2014-08-10 09:28 pm (UTC)(link)
Можно сджойнить на TVF, разбирающую строку.
В MSSQL у решения есть очевидный недостаток: отсутствие статистики, т.е. в зависимости от объёма данных для "IN" может потребоваться подсказка "INNER MERGE JOIN". По умолчанию явно будет LOOP JOIN.

[identity profile] zmila.livejournal.com 2014-08-11 09:36 am (UTC)(link)
а у нас главная беда тут была: "How to put more than 1000 values into an Oracle IN clause" :)