Невозможно поместить более 1000 элементов в выражение CFQUERY. - PullRequest
3 голосов
/ 15 марта 2011

Количество элементов, которые мы можем вставить в выражение «in», ограничено 1000 в Oracle.У нас есть запрос в нашем коде:

<cfquery>
SELECT * from table1 where ID IN (#somelist#)
</cfquery>

Здесь # somelist # является переменной списка CF.

Какое решение было бы наиболее подходящим в CF для решения этой проблемы?

Ответы [ 7 ]

9 голосов
/ 15 марта 2011

Использовать временную таблицу. Вставьте идентификаторы в temp и выполните запрос:

SELECT * from table1 where ID IN (SELECT id FROM temp_table)

См. Этот вопрос по SO: Как поместить более 1000 значений в предложение Oracle IN .

Также похоже, что вы делаете простую замену. Это плохая идея с точки зрения производительности и действительно плохая идея с точки зрения безопасности (внедрение SQL). Я не знаю CF, но я уверен, что вы можете использовать переменные связывания.

9 голосов
/ 15 марта 2011

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

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

В принципе (если этот запрос неизбежен), я 'Я бы сказал, что вы можете просто сделать что-то вроде:

SELECT * from table1 where ID IN (<cfqueryPARAM value = "#somelist#" CFSQLType = "CF_SQL_VARCHAR">) OR ID IN (<cfqueryPARAM value = "#somelist2#" CFSQLType = "CF_SQL_VARCHAR">) ...

И вы можете разбить его на куски по 500, например.

ОБНОВЛЕНИЕ: Добавлен cfqueryparam, чтобы избежать SQLинъекции

1 голос
/ 15 марта 2011

Вы можете:

  1. Используйте подзапрос, как описывает Винсент
  2. разбить список на куски размером менее 1000, выполнить запрос для каждого чанка, затем объединить запросы с помощью запроса CF для запросов и использовать UNION
  3. вставить список во временную таблицу БД, затем выполнить # 1
  4. используйте cfloop, чтобы пройтись по списку и записать OR ID = #i# в запрос для каждого элемента в вашем списке.
  5. Вероятно, лучшее решение - избегать использования оператора IN, если вы не уверены, что длина списка всегда будет меньше вашего предела.
1 голос
/ 15 марта 2011

Вы должны проверить размер списка до выполнения запроса. Вы можете использовать listLen(someList) для возврата длины. Я также повторил бы другие ответы здесь и предложил бы вам выяснить, почему у вас может быть более 1000 элементов в списке «IN». Возможно, вы могли бы вместо этого использовать подзапрос.

0 голосов
/ 18 марта 2011

Я использую функцию SPLIT в UDF, чтобы постоянно делать подобные вещи

Выбрать элемент из dbo.fnSplit ('1,2,3,4,5,6,7,8,9', ',')

Это означает, что вам даже не нужно ИСПОЛЬЗОВАТЬ предложение IN

Выбрать * из списка сотрудников CROSS APPLY dbo.FnSplit ('1,2,3,4,5,6,7,8,9', ',') Где ManagerID = Элемент

Я рекомендую провести некоторое тестирование, на котором функция разбиения лучше всего подходит для ваших нужд

ALTER FUNCTION dbo.Split (@sep char (1), @s varchar (512)) ВОЗВРАЩАЕТСЯ КАК ВЕРНУТЬ ( С кусочками (pn, start, stop) AS ( ВЫБЕРИТЕ 1, 1, CHARINDEX (@sep, @s) СОЮЗ ВСЕХ ВЫБЕРИТЕ pn + 1, остановка + 1, CHARINDEX (@sep, @s, остановка + 1) Из частей Где остановиться> 0 ) ВЫБЕРИТЕ pn, SUBSTRING (@s, запуск, СЛУЧАЙ, КОГДА ОСТАНОВИТЬ> 0 ТОГДА ОСТАНОВ-СТАРТ ELSE 512 END) AS s Из частей ) GO

или

СОЗДАТЬ ФУНКЦИЮ dbo.Split ( @RowData nvarchar (2000), @SplitOn nvarchar (5) )
ВОЗВРАЩАЕТСЯ @RtnValue table ( Id int identity (1,1), Данные нварчар (100) ) AS
НАЧАТЬ Объявите @Cnt int Установить @Cnt = 1

While (Charindex(@SplitOn,@RowData)>0)
Begin
    Insert Into @RtnValue (data)
    Select 
        Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1)))

    Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
    Set @Cnt = @Cnt + 1
End

Insert Into @RtnValue (data)
Select Data = ltrim(rtrim(@RowData))

Return

END

и т. Д. http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648

0 голосов
/ 18 марта 2011

Просто измените размер списка ... если это вообще возможно, не используйте ОГРОМНЫЙ список, просто используйте подзапрос, но если вам нужно использовать такой большой список ... сделайте что-то вроде этого:

<cffunction name="createInArray" returntype="array" output="false">
    <cfargument name="sList" type="string" required="true" />

    <cfset var aOut = ArrayNew(1) />
    <cfset var iLL = ListLen(arguments.sList) />
    <cfset var bContinue = true />
    <cfset var i = 1 />
    <cfset var x = "" />

    <cfloop condition="bContinue">
        <cfif ListLen(arguments.sList) GT 500>
            <cfset x = ListGetAt(arguments.sList,501) />
            <cfset x = Find(",#x#", arguments.sList) />
            <cfset aOut[i] = Mid(arguments.sList,1,x) />
            <cfset arguments.sList = Replace(arguments.sList,aOut[i],"") />
        <cfelse>
            <cfset aOut[i] = arguments.sList />
            <cfset bContinue = false />
        </cfif>
        <cfset i = i+1 />
    </cfloop>

    <cfreturn aOut />

</cffunction>

<cfset somelist = createInArray(somelist) />

<cfquery>
    SELECT * 
    FROM table1
    WHERE <cfloop from="1" to="#somelist.length()#" index="i"><cfif i NEQ 1>OR</cfif> ID IN (<cfqueryparam cfsqltype="cf_sql_integer" value="#somelist[i]#" list="true" separator=",">) </cfloop>
</cfquery>
0 голосов
/ 15 марта 2011

Другой вариант, вместо того, чтобы использовать DML для загрузки временной таблицы, определить тип коллекции оракулов, заполнить его списком, а затем использовать функцию TABLE () в SQL для присоединения к ней.например, для набора числовых идентификаторов)

CREATE OR REPLACE TYPE GENERIC_ID as object (id_value number)
/

CREATE OR REPLACE TYPE GENERIC_ID_LIST as TABLE of GENERIC_ID
/

-- create a variable of that type, load it up and then

SELECT t1.* from table1 t1, table(your_generic_id_list) c1
where t1.ID = c1.id_value; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...