在Oracle中,我们可以放入“in”表达式中的元素数量限制为1000。我们的代码中有一个查询:
<cfquery>
SELECT * from table1 where ID IN (#somelist#)
</cfquery>
这里#somelist#是CF列表变量。
解决此问题的CF最合适的解决方案是什么?
答案 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)
你可以:
UNION
cfloop
循环遍历列表,并在列表中的每个项目的查询中写入OR ID = #i#
。答案 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