ORA-01795: максимальное количество выражений в списке — 1000

В С# мы создаем запрос для NHibernate, содержащий оператор «in». Количество выражений превышает 5000. Если я выполняю запрос, я получаю сообщение об ошибке.

Мне нужно найти хороший способ сломать большой построитель строк и сохранить их в массиве построителя строк и выполнить несколько запросов, если это необходимо, чтобы получить желаемый результат. У нас есть только одна учетная запись с более чем 5000 записей, а остальные меньше 100. Может ли кто-нибудь предложить способ решить эту проблему?


person VolleyBall Player    schedule 09.03.2011    source источник


Ответы (6)


Решение, которое я использовал, состоит в том, чтобы разделить IN на OR.

where A in (a,b,c,d,e,f)

становится

where (A in (a,b,c) OR a in (d,e,f)) ...

Это просто и не предъявляет особых требований к формату запроса.

Мне кажется, что это проще реализовать в вашем конструкторе строк (SQLQuerybuilder или как он там называется в вашем случае), чем в некоторых других предлагаемых решениях.

person lokori    schedule 02.08.2011

Вы можете преобразовать IN в JOIN, используя этот трюк

SELECT * FROM maintable
JOIN (
    SELECT v1 a FROM DUAL UNION ALL
    SELECT v2 a FROM DUAL UNION ALL
    SELECT v3 a FROM DUAL UNION ALL
    ...
    SELECT v2000 a FROM DUAL) tmp

    on tmp.a = maintable.id

Этот запрос идентичен

SELECT * FROM maintable 
WHERE id IN (v1, v2, v3, ...., v2000)

Я знаю, что это увеличивает «количество текста» в запросе, но это не влияет на производительность, так как в любом случае оба используют временный набор результатов без хлопот с созданием глобальной временной таблицы.

person d-live    schedule 10.03.2011

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

В нашем программном продукте пользователи делают выбор в пользовательском интерфейсе. Затем передняя часть передает список соответствующих идентификаторов задней части для запроса данных и обработки.

Во-первых, мне нужна была функция для разделения списка элементов на части по 1000 элементов.

Обратите внимание, это VB.NET, но сама функция была найдена в другом месте StackOverflow на C#:

Public Shared Iterator Function Partition(Of T)(source As IList(Of T), Optional size As Int32 = 1000) As IEnumerable(Of List(Of T))
        For i As Integer = 0 To CInt(Math.Ceiling(source.Count / CDbl(size)))
            Yield New List(Of T)(source.Skip(size * i).Take(size))
        Next
End Function

Эту функцию я использовал несколькими способами. Одним из способов является цикл по разделам списка для изменения QueryOver для создания объединения всех результатов частей, например:

Dim allPartitions As IEnumerable(Of List(Of Integer)) = Partition(idList, SplitSize)
        Dim foundObjects As IEnumerable(Of MyEntity) = New List(Of MyEntity)

        For Each part As List(Of Integer) In allPartitions
            foundObjects = foundObjects.Union(
                _session.QueryOver(Of MyEntity) _
                     WhereRestrictionOn(Function(x) x.ID).IsIn(part).Future())
        Next

Еще один способ, который я использовал, — создать ограничение, которое можно применить в QueryOvers. Следующая функция создает такое ограничение (ICriterion):

Public Shared Function GetRestrictionOnIds(ids As List(Of Integer), propertyName As String) As ICriterion

        Dim allParts As IEnumerable(Of List(Of Integer)) = Partition(ids, SplitSize)

        Dim restriction As Disjunction = Restrictions.Disjunction()
        For Each part As List(Of Integer) In allParts
            restriction.Add(Restrictions.In(propertyName, part))
        Next

        Return Restrictions.Conjunction().Add(restriction)

    End Function

Я вызываю эту функцию следующим образом:

Dim wellIdRestriction As ICriterion = GetRestrictionOnIds(wellIdsList, "WellId") 
Dim bleedOffs As IList(Of BleedOff) = _session.QueryOver(Of BleedOff)() _
                .Where(wellIdRestriction) _
                .Where(..... more restrictions...) _
                .And(...yet more restrictions....) _
                .List().GroupBy(...some function...) _
                .ToDictionary(.. key-function, value-function...)
person NilsAndreas    schedule 30.05.2017
comment
Я проголосовал за: довольно необычно видеть такой отличный ответ от пользователя с низкой репутацией. Молодец Нильс! - person Noel Widmer; 30.05.2017

Вы можете создать временную таблицу, заполнить ее элементами списка IN и присоединиться к ней.

person Tim    schedule 10.03.2011
comment
создание хранимых процедур или временных таблиц невозможно. Нет доступа к бд вообще. код должен быть тестируемым с помощью nUnit и должен иметь CI. - person VolleyBall Player; 10.03.2011

Вы можете объединить выборки с 1000 IN в каждом выборе. Не так эффективно, как создание временной таблицы, но для специальных запросов работает.

SELECT A, B, C from BLAH WHERE A IS IN (
    0 - 999
)

UNION

SELECT A, B, C from BLAH WHERE A IS IN (
    1000 - 1999
)
person Michael Shimmins    schedule 10.03.2011
comment
Я попробую это и дам вам знать, как все прошло. Судя по всему, одного запроса будет достаточно, чтобы получить желаемый результат. - person VolleyBall Player; 10.03.2011

когда у вас так много элементов в предложении where, возможно, пришло время подумать о рефакторинге.

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

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

как преобразовать csv в таблицу в оракуле или Как лучше всего разделить строки csv в oracle 9i

person clyc    schedule 10.03.2011