填充“IN”子句的参数

问题描述 投票:0回答:6

在此过程中,用户传递一个项目 ID。我希望能够传递不确定数量的项目 ID,以便我可以将 where 子句从“ProjectID=@projectID”(例如)更改为“ProjectID IN (@ProjectID)”。

这样,我可以通过一次调用数据库来删除十几个项目,而不是重复调用数据库。

有什么好的策略可以做到这一点吗?我正在从 Access 呼叫 SP....

create procedure dbo.cpas_DeleteProject
@ProjectID INt = 0,
@errorFlag int OUTPUT
AS
set @errorFlag=0
BEGIN TRY
    BEGIN TRANSACTION
        DELETE FROM tblWOTasks WHERE tblWOTasks.WorkOrderID IN (SELECT ID FROM tblWorkOrders WHERE ProjectID=@ProjectID)
        DELETE FROM tblELaborSpread WHERE tblELaborSpread.WorkOrderID IN (SELECT ID FROM tblWorkOrders WHERE ProjectID=@ProjectID)
        DELETE FROM tblWorkOrders WHERE tblWorkOrders.ProjectID IN (SELECT ID FROM tblWorkOrders WHERE ProjectID=@ProjectID)
        DELETE FROM tblCPTransactiON WHERE tblCPTransactiON.CPProjectID=@ProjectID
        DELETE FROM tblCPJE WHERE tblcpje.jeid IN 
            (SELECT tblcpje.JEID FROM tblCPJE left joIN tblCPTransactiON as CR ON CR.CPTransID = tblCPJE.JECreditID 
                            left joIN tblCPTransactiON as DR ON DR.CPTransID = tblCPJE.JEDebitID 
                    WHERE DR.CPTransID is null AND cr.CPTransID is null)        
        DELETE FROM tblProjectTasks WHERE tblProjectTasks.ProjectID=@ProjectID
        DELETE FROM xrefProjectMICAP WHERE xrefProjectMICAP.ProjectID=@ProjectID
        DELETE FROM tblworkorders WHERE tblWorkOrders.ProjectID=@ProjectID
        DELETE FROM tblprojects WHERE tblProjects.ID=@ProjectID
        --Project Comments cascade delete....
    COMMIT TRANSACTION
END TRY

BEGIN CATCH
    ROLLBACK TRANSACTION
    set @errorFlag=1
END CATCH
sql t-sql stored-procedures ms-access-2007
6个回答
1
投票

我能够将多个值传递给存储过程的唯一方法是将它们连接到一个字符串并将该字符串传递给 SP,然后解析 SP 开头的字符串,如下所示(projetc_id 被分隔)与 ,

CREATE PROCEDURE [dbo].[cpas_DeleteProject] 

@ProjectID varchar(3000)
@errorFlag int OUTPUT
AS

--Parse function
DECLARE @tblstring Table(ProjectID int) --table variable to store all parsed ProjectID's
DECLARE @project varchar(10)

DECLARE @StartPos int, 
@Length int,--streng lengd
@Delimeter varchar(1)=','--delimeter
WHILE LEN(@ProjectID) > 0
  BEGIN
   SET @StartPos = CHARINDEX(@Delimeter, @ProjectID)
IF @StartPos < 0 SET @StartPos = 0
SET @Length = LEN(@ProjectID) - @StartPos - 1
IF @Length < 0 SET @Length = 0
IF @StartPos > 0
  BEGIN
    SET @Project = SUBSTRING(@ProjectID, 1, @StartPos - 1)
    SET @ProjectID = SUBSTRING(@ProjectID, @StartPos + 1, LEN(@ProjectID) - @StartPos)
  END
ELSE
  BEGIN
    SET @Project = @ProjectID
    SET @ProjectID = ''
  END
INSERT @tblstring (ProjectID) VALUES(@Project)
END


     set @errorFlag=0
BEGIN TRY
BEGIN TRANSACTION
    DELETE FROM tblWOTasks WHERE tblWOTasks.WorkOrderID IN (SELECT ID FROM tblWorkOrders WHERE ProjectID in (SELECT ProjectID  from @tblstring))
    DELETE FROM tblELaborSpread WHERE tblELaborSpread.WorkOrderID IN (SELECT ID FROM tblWorkOrders WHERE ProjectID in (SELECT ProjectID  from @tblstring))
    DELETE FROM tblWorkOrders WHERE tblWorkOrders.ProjectID IN (SELECT ID FROM tblWorkOrders WHERE ProjectID in (SELECT ProjectID  from @tblstring))
    DELETE FROM tblCPTransactiON WHERE tblCPTransactiON.CPProjectID in (SELECT ProjectID  from @tblstring)
    DELETE FROM tblCPJE WHERE tblcpje.jeid IN 
        (SELECT tblcpje.JEID FROM tblCPJE left joIN tblCPTransactiON as CR ON CR.CPTransID = tblCPJE.JECreditID 
                        left joIN tblCPTransactiON as DR ON DR.CPTransID = tblCPJE.JEDebitID 
                WHERE DR.CPTransID is null AND cr.CPTransID is null)        
    DELETE FROM tblProjectTasks WHERE tblProjectTasks.ProjectID in (SELECT ProjectID  from @tblstring)
    DELETE FROM xrefProjectMICAP WHERE xrefProjectMICAP.ProjectID in (SELECT ProjectID  from @tblstring)
    DELETE FROM tblworkorders WHERE tblWorkOrders.ProjectID in (SELECT ProjectID  from @tblstring)
    DELETE FROM tblprojects WHERE tblProjects.ID in (SELECT ProjectID  from @tblstring)
    --Project Comments cascade delete....
COMMIT TRANSACTION
END TRY

BEGIN CATCH
ROLLBACK TRANSACTION
set @errorFlag=1
END CATCH

1
投票

查看本文中选定的答案:如何在存储过程 SQL Server 2008 中使用 `IN` 运算符传递字符串参数

用户所做的是创建一个存储过程,它将拆分逗号分隔的字符串,然后返回一个表。获得 ID 表后,您可以将其插入查询中。由于您的查询包含更多查询,可能需要进行一些修改才能使其正常工作,但理论仍然相同。


1
投票

如果无法使用表参数,则创建一个分隔字符串参数来保存多个 Id 值,并将该分隔字符串转换为过程内的表变量。 (网上有很多 T-SQL UDF 可以做到这一点。这篇是一篇好文章。)

那么你的 In 子句将如下所示:

  Where [Somecolumn] In (Select funcColName From dbo.ConvertFunction(@stringParameter))

1
投票

虽然可以在 SQL Server 中使用表值参数,但据我所知,没有简单的方法可以从 Access 填充这些参数。

解决方案可能是在存储过程中使用动态 SQL,然后将项目 ID 放在 Access 应用程序中创建的 varchar(max) 中,例如:

@projectIdList = '200,300,400'

然后在动态 SQL 中,您只需将其包含为

'WHERE tblProjects.ID IN (' + @projectIdList + ')'

这显然容易受到 SQL 注入攻击,但根据您的 Access 应用程序的使用方式,这可能是可以接受的。

另一个更安全的解决方案是允许 Access 在调用存储过程之前将 projectId 作为行写入小表中,然后在存储过程中执行删除操作,如下所示:

DELETE FROM tblprojects 
WHERE tblProjects.ID IN (SELECT ProjectId FROM ProjectIdsToBeDeleted)

这更加安全和干净,因为您避免了动态 SQL。但是,您需要某种方法来处理并发性 - 也许通过在填充 ProjectIdsToBeDeleted 表时生成唯一标识符,然后在执行存储过程时过滤相同的标识符。


0
投票

您可以查看使用表值参数...


0
投票

按照我的理解,提问者想要调用一个 SQL Server 存储过程,该过程在一个参数中接受多个值,并从 MSAccess 调用该存储过程。

问题 #1: 将多个 ID 连接成标量字符串值。这篇文章接受的答案甚至基于 VB 的答案应该可以正常工作

问题#2:如何使用标量字符串列表: 下面是参数中 VARCHAR(MAX) 列表的安全模式。与这里的其他一些答案相同的核心思想,除了它可以免受常见的 SQL 注入攻击,并且在 IMO 方面具有一些灵活性优势。这确实需要 STRING_SPLIT 或等效函数(SQL Server 2016 及更高版本。不确定其他 SQL 风格)

CREATE PROCEDURE dbo.SelectSomeListOfIDs (
    @AnyDelimitedIDList VARCHAR(MAX)
)
BEGIN

    --The idea here is to make the list creation flexible and non-brittle for non-technical users and external calls.  Overkill for most scenarios.  Depending on the expected data, some delimiters should definitely be taken out.
    REPLACE @AnyDelimitedIDList = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        @AnyDelimitedIDList
        ,CHAR(09),'|')  --Tab
        ,CHAR(10),'|')  --LF
        ,CHAR(13),'|')  --CR
        ,CHAR(44),'|')  --Comma
        ,CHAR(59),'|')  --SemiColon
        ,CHAR(58),'|')  --Colon
        ,CHAR(46),'|')  --dot
        ,CHAR(34),'|')  --DoubleQuote
        ,CHAR(39),'|')  --SingleQuote
        ,CHAR(32),'|')  --Space

    --this enables the input to never become executable code
    DROP TABLE IF EXISTS #IDList
    SELECT ID = x.value
    INTO #IDList
    FROM STRING_SPLIT(@AnyDelimitedIDList,'|') x
    WHERE ISNULL(x.value,'') <> ''

    --Obviates the need for Dynamic SQL 
    SELECT ID
    FROM dbo.MyTable x
    JOIN #IDList  list
        ON x.ID = List.ID --safe from sql injection 

    --Dynamic SQL is no longer needed for the sake of the ID List, but if Dyn is still needed/desired, the list can still be simplified and made safe:
    DECLARE @dummyScalar DATETIME = GETDATE()
    EXEC ('
        SELECT ID
        FROM dbo.MyTable x
        JOIN #IDList  list --safe from sql injection, and a bit simpler
            ON x.ID = List.ID
        WHERE x.date > '''+@dummyScalar+'''
    ')

END

(已编辑以解决 Abra 对 MSAccess 的评论)

© www.soinside.com 2019 - 2024. All rights reserved.