不能在IN Expression CFQUERY中放置超过1000个元素

时间:2011-03-15 13:30:31

标签: sql oracle coldfusion

在Oracle中,我们可以放入“in”表达式中的元素数量限制为1000。我们的代码中有一个查询:

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

这里#somelist#是CF列表变量。

解决此问题的CF最合适的解决方案是什么?

7 个答案:

答案 0 :(得分:9)

这可能是一个驱动程序限制,但当然如果你需要将列表中的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 :(得分:9)

使用临时表。将Ids插入temp,并查询:

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

在SO上查看此问题:How to put more than 1000 values into an Oracle IN clause

看起来你正在做一个简单的替换。从性能的角度来看,这是一个糟糕的主意,从安全的角度来看,这是一个非常糟糕的主意(SQL注入)。我不知道CF,但我很确定你可以使用绑定变量。

答案 2 :(得分:1)

您应该在执行查询之前检查列表的大小。您可以使用listLen(someList)返回长度。我也会在这里回应其他回复,并建议您调查为什么在“IN”列表中可能有超过1000个项目。也许您可以使用子查询。

答案 3 :(得分:1)

你可以:

  1. 使用像Vincent描述的子查询
  2. 将列表分成大小小于1000的块,对每个块进行查询,然后使用CF query on queries并使用UNION
  3. 加入查询
  4. 将列表插入临时数据库表,然后执行#1
  5. 使用cfloop循环遍历列表,并在列表中的每个项目的查询中写入OR ID = #i#
  6. 可能最好的解决方案是避免使用IN运算符,除非您确定列表长度始终小于限制。

答案 4 :(得分:0)

另一种选择,而不是使用DML加载临时表,定义oracle集合类型,用列表填充它,然后使用SQL中的TABLE()函数加入它。

e.g。对于一组数字ID)

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; 

答案 5 :(得分:0)

只需调整列表大小......如果可能的话,不要使用巨大的列表,只需使用子查询,但如果你必须使用大的列表......做这样的事情:

<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>

答案 6 :(得分:-1)

我在UDF中使用SPLIT函数来一直做这样的事情

从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),@ varchar(512)) 退货表 如 返回(     WITH Pieces(pn,start,stop)AS(       SELECT 1,1,CHARINDEX(@ sep,@ s)       UNION ALL       SELECT pn + 1,stop + 1,CHARINDEX(@ sep,@ s,stop + 1)       从片断       停止的地方&gt; 0     )     SELECT pn,       SUBSTRING(@s,start,CASE WHEN STOP&gt; 0 THEN stop-start ELSE 512 END)AS s     从片断   ) GO

创建功能dbo.Split (     @RowData nvarchar(2000),     @SplitOn nvarchar(5) )
返回@RtnValue表 (     Id int identity(1,1),     数据nvarchar(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