Ответ 1
Поместите значения во временную таблицу, а затем выполните выбор, где находится id (выберите id из искушаемого)
Есть ли способ обойти ограничение Oracle 10g на 1000 элементов в статическом разделе IN? У меня есть список разделенных запятыми многих идентификаторов, которые я хочу использовать в предложении IN. Иногда этот список может превышать 1000 элементов, после чего Oracle выдает ошибку. Запрос похож на этот...
select * from table1 where ID in (1,2,3,4,...,1001,1002,...)
Поместите значения во временную таблицу, а затем выполните выбор, где находится id (выберите id из искушаемого)
Вы можете попробовать использовать следующую форму:
select * from table1 where ID in (1,2,3,4,...,1000)
union all
select * from table1 where ID in (1001,1002,...)
Я почти уверен, что вы можете разделить значения на несколько IN с помощью OR:
select * from table1 where ID in (1,2,3,4,...,1000) or
ID in (1001,1002,...,2000)
select column_X, ... from my_table
where ('magic', column_X ) in (
('magic', 1),
('magic', 2),
('magic', 3),
('magic', 4),
...
('magic', 99999)
) ...
Откуда вы получаете список идентификаторов от первого места? Поскольку они являются идентификаторами в вашей базе данных, они были получены из предыдущего запроса?
Когда я видел это в прошлом, это было потому, что: -
Я думаю, что могут быть лучшие способы переделать этот код, чтобы просто заставить этот оператор SQL работать. Если вы предоставите более подробную информацию, вы можете получить некоторые идеи.
Использовать... из таблицы (...:
create or replace type numbertype
as object
(nr number(20,10) )
/
create or replace type number_table
as table of numbertype
/
create or replace procedure tableselect
( p_numbers in number_table
, p_ref_result out sys_refcursor)
is
begin
open p_ref_result for
select *
from employees , (select /*+ cardinality(tab 10) */ tab.nr from table(p_numbers) tab) tbnrs
where id = tbnrs.nr;
end;
/
Это один из редких случаев, когда вам нужен намек, иначе Oracle не будет использовать индекс для идентификатора столбца. Одно из преимуществ этого подхода заключается в том, что Oracle не нуждается в жестком анализе запроса снова и снова. Использование временной таблицы в большинстве случаев медленнее.
изменить 1 упростить процедуру (спасибо jimmyorr) + пример
create or replace procedure tableselect
( p_numbers in number_table
, p_ref_result out sys_refcursor)
is
begin
open p_ref_result for
select /*+ cardinality(tab 10) */ emp.*
from employees emp
, table(p_numbers) tab
where tab.nr = id;
end;
/
Пример:
set serveroutput on
create table employees ( id number(10),name varchar2(100));
insert into employees values (3,'Raymond');
insert into employees values (4,'Hans');
commit;
declare
l_number number_table := number_table();
l_sys_refcursor sys_refcursor;
l_employee employees%rowtype;
begin
l_number.extend;
l_number(1) := numbertype(3);
l_number.extend;
l_number(2) := numbertype(4);
tableselect(l_number, l_sys_refcursor);
loop
fetch l_sys_refcursor into l_employee;
exit when l_sys_refcursor%notfound;
dbms_output.put_line(l_employee.name);
end loop;
close l_sys_refcursor;
end;
/
Это выведет:
Raymond
Hans
Я оказался здесь, ища решение.
В зависимости от высокого количества элементов, с которыми вам нужно запросить, и если ваши объекты уникальны, вы можете разделить запрос на пакетные запросы из 1000 элементов и вместо этого комбинировать результаты на своем конце (здесь псевдокод):
//remove dupes
items = items.RemoveDuplicates();
//how to break the items into 1000 item batches
batches = new batch list;
batch = new batch;
for (int i = 0; i < items.Count; i++)
{
if (batch.Count == 1000)
{
batches.Add(batch);
batch.Clear()
}
batch.Add(items[i]);
if (i == items.Count - 1)
{
//add the final batch (it has < 1000 items).
batches.Add(batch);
}
}
// now go query the db for each batch
results = new results;
foreach(batch in batches)
{
results.Add(query(batch));
}
Это может быть хорошим компромиссом в сценарии, в котором обычно не более 1000 элементов, поскольку более 1000 элементов будут вашим "крайним" краевым сценарием. Например, если у вас есть 1500 элементов, два запроса (1000, 500) не так уж плохи. Это также предполагает, что каждый запрос не является особенно дорогостоящим по своему усмотрению.
Это было бы нецелесообразно, если бы ваше типичное количество ожидаемых предметов стало намного больше - скажем, в диапазоне 100000 - требуется 100 запросов. Если да, то вам, вероятно, следует более серьезно относиться к использованию решения глобальных временных таблиц, представленного выше, как наиболее "правильное" решение. Кроме того, если ваши объекты не уникальны, вам также необходимо будет разрешить дублировать результаты в ваших партиях.
Да, очень странная ситуация для оракула.
если вы укажете 2000 идентификаторов внутри предложения IN, он завершится с ошибкой. это не удается:
select ...
where id in (1,2,....2000)
но если вы просто поместите идентификаторы 2000 в другую таблицу (например, таблицу temp), она будет работать это работает:
select ...
where id in (select userId
from temptable_with_2000_ids )
что вы можете сделать, на самом деле можно разделить записи на много 1000 записей и выполнить их по группам.
Вместо использования предложения IN
вы можете попробовать использовать JOIN
с другой таблицей, которая извлекает идентификатор. таким образом нам не нужно беспокоиться о лимите. просто мысль с моей стороны.
Вот какой-то код Perl, который пытается обойти ограничения, создав встроенное представление, а затем выбрав его. Текст инструкции сжимается, используя строки из двенадцати элементов каждый, вместо того, чтобы выбирать каждый элемент из DUAL отдельно, а затем несжатый путем объединения всех столбцов. UNION или UNION ALL в декомпрессии не должны иметь никакого значения здесь, поскольку все это входит внутрь IN, который наложит уникальность, прежде чем присоединяться к нему в любом случае, но в сжатии UNION ALL используется для предотвращения большого количества ненужного сравнения. Поскольку данные, которые я фильтрую, являются целыми числами, цитирование не является проблемой.
#
# generate the innards of an IN expression with more than a thousand items
#
use English '-no_match_vars';
sub big_IN_list{
@_ < 13 and return join ', ',@_;
my $padding_required = (12 - (@_ % 12)) % 12;
# get first dozen and make length of @_ an even multiple of 12
my ($a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l) = splice @_,0,12, ( ('NULL') x $padding_required );
my @dozens;
local $LIST_SEPARATOR = ', '; # how to join elements within each dozen
while(@_){
push @dozens, "SELECT @{[ splice @_,0,12 ]} FROM DUAL"
};
$LIST_SEPARATOR = "\n union all\n "; # how to join @dozens
return <<"EXP";
WITH t AS (
select $a A, $b B, $c C, $d D, $e E, $f F, $g G, $h H, $i I, $j J, $k K, $l L FROM DUAL
union all
@dozens
)
select A from t union select B from t union select C from t union
select D from t union select E from t union select F from t union
select G from t union select H from t union select I from t union
select J from t union select K from t union select L from t
EXP
}
Можно использовать так:
my $bases_list_expr = big_IN_list(list_your_bases());
$dbh->do(<<"UPDATE");
update bases_table set belong_to = 'us'
where whose_base in ($bases_list_expr)
UPDATE
Вместо SELECT * FROM table1 WHERE ID IN (1,2,3,4,...,1000);
Используйте это:
SELECT * FROM table1 WHERE ID IN (SELECT rownum AS ID FROM dual connect BY level <= 1000);
* Обратите внимание, что вам нужно убедиться, что идентификатор не ссылается на другие внешние IDS, если это зависимость. Чтобы обеспечить доступность только существующих идентификаторов, выполните следующие действия:
SELECT * FROM table1 WHERE ID IN (SELECT distinct(ID) FROM tablewhereidsareavailable);
Приветствия