ORA-01795: максимальное количество выражений в списке 1000 - PullRequest
4 голосов
/ 10 марта 2011

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

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

Ответы [ 6 ]

6 голосов
/ 02 августа 2011

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

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

становится

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

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

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

3 голосов
/ 10 марта 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)

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

2 голосов
/ 30 мая 2017

Оригинальный вопрос поста стареет, но в последнее время я сталкивался с этой проблемой несколько раз и нашел решение, которое могло бы помочь другим. И, поскольку ни один из ответов выше не дал ответа с кодом 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...)
0 голосов
/ 10 марта 2011

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

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

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

, как преобразовать CSV в таблицу в оракуле или Как лучше разбить строки CSV в оракуле 9i

0 голосов
/ 10 марта 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
)
0 голосов
/ 10 марта 2011

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

...