【问题标题】:I want to pass a valuelist to my SQL Server stored procedure我想将 valuelist 传递给我的 SQL Server 存储过程
【发布时间】:2016-12-21 23:52:42
【问题描述】:

我需要将一个列表传递给我的 SQL Server 存储过程。我已经生成了列表,它是一个逗号分隔的数字列表,使用:

<cfquery name="House" datasource="#application.datasource#">
    SELECT cGLsubaccount
    FROM   HOUSE
    WHERE Right(cGLsubaccount,3) IN 
         (
           <cfloop index="i" list="#consulsub#">
              <cfoutput>#right(Trim(i) , 3)#,</cfoutput>
           </cfloop>
           00000000
         )
</cfquery>

<cfset GLsubacct = Valuelist(House.cGLsubaccount,",")>

现在,我使用cfprocparam 将其传递到存储过程中,并将cfsqltype 设置为varchar(1000)。 (我在谷歌上读到这是将列表传递给存储过程的方法)。

<cfprocparam type="IN" value="#GLsubacct#" DBVARNAME="@GLsubacct" cfsqltype="cf_sql_varchar">

在存储过程中,我是这样做的:

ALTER PROCEDURE [rw].[sp_EFT_NoticeXLS2] (
      @Scope varchar(50)
      , @AcctPeriod char(6)
      , @CompanyID nchar(10)
      , @GLsubacct varchar(1000) = NULL
) 
AS......

存储过程中的SQL语句如下:

CREATE  TABLE #Houses (
   iHouse_ID        int,
   cName            varchar(50),
   cNumber          varchar(10),
   cPhoneNumber1    varchar(25),
   cAddressLine1    varchar(50),
   cCity            varchar(30),
   cStateCode       varchar(2),
   cZipCode     varchar(10),
   iUnitsAvailable  int,
   iRegOpsNumber    int,
   cOpsName     varchar(50),
   iRegionID        int,
   cRegionName      varchar(50)
)

INSERT INTO #Houses (
   iHouse_ID, 
   cName, 
   cNumber,
   cPhoneNumber1,
   cAddressLine1, 
   cCity, 
   CStateCode, 
   cZipCode,
   iUnitsAvailable, 
   iRegOpsNumber, 
   cOpsName, 
   iRegionID, 
   cRegionName )
SELECT
   h2.iHouse_ID,
   h.cName, 
   h.cNumber, 
   h.cPhoneNumber1,
   h.cAddressLine1, 
   h.cCity, 
   h.CStateCode, 
   h.cZipCode, 
   h.iUnitsAvailable, 
   r.RegOpsNumber,
   r.opsname, 
   r.iRegion_ID, 
   r.regionname
FROM rw.fn_GetScopeHouses (@Scope, @dtReport, NULL, NULL, 0) h2 
        JOIN House h on h.iHouse_ID = h2.iHouse_ID
           AND h.cCompanyID = @CompanyID
           AND ( @GLsubacct IS NULL OR h.iHouse_ID IN ('+ @GLsubacct +') )
JOIN    rw.vw_Reg_Ops r on r.opsareaID = h.iOpsArea_ID

问题是我遇到了错误。错误信息是...

Error Executing Database Query.  
[Macromedia][SQLServer JDBC Driver][SQLServer]Conversion failed when converting the varchar value '+ @GLsubacct +' to data type int.  

The error occurred in E:\inetpub\wwwroot\intranet\TIPS4\Admin\EFTprocesspullfile.cfm: line 46

44 :         <cfprocparam type="IN" value="#CompanyID#" DBVARNAME="@CompanyID" cfsqltype="cf_sql_varchar"><!--- TPecku added variable to be passed to the stored Proc --->
45 :         <cfif CompanyID EQ 0000>
46 :         <cfprocparam type="IN" value="#GLsubacct#" DBVARNAME="@GLsubacct" cfsqltype="cf_sql_varchar">
47 :         </cfif>
48 :    </cfstoredproc>

错误出现在存储过程中,因为我有'+ @GLsubacct +'。 关于我做错了什么的任何想法? (抱歉,这篇文章太长了,我想尽可能详细地说明,以便您了解我在做什么。

【问题讨论】:

  • 查看表值参数 (TVP),这可能是您想要使用的,而不是传递字符串。 msdn.microsoft.com/en-us/library/bb510489.aspx
  • 感谢 rjdevereux,我查看了您的链接,但我仍然对如何前进感到困惑。
  • 好吧,你有几个选择。一种是使用字符串解析功能。这里有很多方法可以做到这一点:sqlperformance.com/2012/07/t-sql-queries/split-strings。一种是将表传递给存储过程。为此,您创建一个类型(如上面的链接中所示),然后将其用作您的过程的READONLY 参数。无论您选择什么选项,您都希望更改您的过程以加入表(拆分值表或您使用的 tvp)。
  • 谢谢ZLK。我今天将根据您的建议进行处理,明天将报告我的进度。
  • 一个正在使用字符串解析函数 是的。那肯定会。您还应该考虑用这样的方式替换您的第一个查询,因为它在当前形式下很容易受到 sql 注入的攻击。

标签: sql-server stored-procedures coldfusion coldfusion-9


【解决方案1】:

为了使这项工作与现有代码接近,您必须在存储过程中创建一个查询字符串(替换您的插入语句)并执行它。

我创建了一些虚拟/测试变量,以使用 PRINT 语句查看查询的外观。您可以从带有“DECLARE @execQuery varchar(MAX)”的代码行开始替换您的插入语句。

这样的逻辑应该用你可以使用的数据填充临时表#Houses。

-- test variables
DECLARE @Scope varchar(50), @AcctPeriod char(6), @CompanyID nchar(10), @GLsubacct varchar(1000), @dtReport VARCHAR(20);
SET @Scope = 'scope';
SET @AcctPeriod = 'fall';
SET @CompanyID = N'coID';
SET @dtReport = 'dtRpt'
SET @GLsubacct = '123,234,345,456'

-- copy code below this line
DECLARE @execQuery varchar(MAX)

SET @execQuery = 'insert into #Houses (
iHouse_ID, 
cName, 
cNumber,
cPhoneNumber1,
cAddressLine1, 
cCity, 
CStateCode, 
cZipCode,
iUnitsAvailable, 
iRegOpsNumber, 
cOpsName, 
iRegionID, 
cRegionName
)
select  
h2.iHouse_ID,
h.cName, 
h.cNumber, 
h.cPhoneNumber1,
h.cAddressLine1, 
h.cCity, 
h.CStateCode, 
h.cZipCode, 
h.iUnitsAvailable, 
r.RegOpsNumber,
r.opsname, 
r.iRegion_ID, 
r.regionname
FROM    rw.fn_GetScopeHouses ('''+ @Scope +''', '''+ @dtReport +''', NULL, NULL, 0) h2  
JOIN    House h on  h.iHouse_ID = h2.iHouse_ID
AND h.cCompanyID = N'''+ @CompanyID +''''
IF (@GLsubacct IS NOT NULL)
BEGIN
    SET @execQuery = @execQuery + ' AND ( h.iHouse_ID IN ('+ @GLsubacct +') )'
END
SET @execQuery = @execQuery + 'JOIN rw.vw_Reg_Ops r on r.opsareaID = h.iOpsArea_ID'

--PRINT @execQuery 

EXEC @execQuery

【讨论】:

  • 尽可能不要使用动态sql。除了其他问题之外,它还容易受到 SQL 注入的影响。带有 JOIN 的 udf/table 驱动方法会更好 IMO。
  • 带有 cfprocparam 标签的 cfstoredproc 调用不会像 cfqueryparam 一样工作吗?
  • 如果是普通的SQL语句,执行without EXEC yes。但是,上面构建了一个文字 sql 字符串(没有任何绑定变量)并将其传递给 EXEC。 (类似于使用 PreserveSingleQuotes 的 SQL 等价物)。所以@variables里面的任何恶意sql都会被执行。如果您绝对必须使用动态 sql,请使用 sp_executeSQL 而不是 EXEC。尽管它不适用于所有情况(例如使用 IN 子句)。在这种情况下,表驱动的方法更适合 IMO。
  • 使用 sp_executeSQL 而不是 EXEC ...当然要使用适当的参数。否则,它可能与使用 EXEC 相同。
【解决方案2】:

我终于想出了如何让它发挥作用。在我将列表传递给存储过程变量后,我无法在我的sql语句中直接使用该变量,它不起作用,所以我所做的就是遍历变量的内容并将它们存储在一个临时表中.然后我在我的 SQL 语句中使用了临时表的内容。如果你看上面,你会看到我试图做的事情没有奏效。我最终做的是......

declare @oneNumber int
declare @POS int
if @GLsubacct <> ''
begin 
--Step through the comma delimted list of numbers and plug each one into a temp table
create table #HouseList (HouseID int)
select @POS = patindex('%,%',@GLsubacct) 
while @POS > 0
begin
select @oneNumber = cast(left(@GLsubacct,@POS - 1) as int)
insert into #HouseList (HouseID) values (@oneNumber)
select @GLsubacct = right(@GLsubacct,len(@GLsubacct) - @POS)
select @POS = patindex('%,%',@GLsubacct) 
end
--do the last one which doesn't have any comma
select @oneNumber = cast(@GLsubacct as int)
insert into #HouseList (HouseID) values (@oneNumber)
end

-- Create #House table
CREATE  TABLE #Houses (
iHouse_ID       int,
cName           varchar(50),
cNumber         varchar(10),
cPhoneNumber1   varchar(25),
cAddressLine1   varchar(50),
cCity           varchar(30),
cStateCode      varchar(2),
cZipCode        varchar(10),
iUnitsAvailable int,
iRegOpsNumber   int,
cOpsName        varchar(50),
iRegionID       int,
cRegionName     varchar(50))

insert  into    #Houses (
iHouse_ID, 
cName, 
cNumber,
cPhoneNumber1,
cAddressLine1, 
cCity, 
CStateCode, 
cZipCode,
iUnitsAvailable, 
iRegOpsNumber, 
cOpsName, 
iRegionID, 
cRegionName)
select  
h2.iHouse_ID,
h.cName, 
h.cNumber, 
h.cPhoneNumber1,
h.cAddressLine1, 
h.cCity, 
h.CStateCode, 
h.cZipCode, 
h.iUnitsAvailable, 
r.RegOpsNumber,
r.opsname, 
r.iRegion_ID, 
r.regionname
from    rw.fn_GetScopeHouses (@Scope, @dtReport, NULL, NULL, 0) h2  
JOIN    House h on  h.iHouse_ID = h2.iHouse_ID
AND h.cGLsubaccount IN (select Houseid from #HouseList)
JOIN    rw.vw_Reg_Ops r on r.opsareaID = h.iOpsArea_ID

效果很好!

【讨论】:

  • 您应该将“拆分”逻辑放入一个表值 UDF 中。然后,您可以在任何程序中将它与任何列表一起使用 - 不仅仅是这个 :) 有关一些示例,请参见 the link ZLK posted in the comments
猜你喜欢
  • 2015-08-20
  • 2014-08-29
  • 1970-01-01
  • 2014-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
相关资源
最近更新 更多