在此过程中,用户传递一个项目 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
我能够将多个值传递给存储过程的唯一方法是将它们连接到一个字符串并将该字符串传递给 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
查看本文中选定的答案:如何在存储过程 SQL Server 2008 中使用 `IN` 运算符传递字符串参数
用户所做的是创建一个存储过程,它将拆分逗号分隔的字符串,然后返回一个表。获得 ID 表后,您可以将其插入查询中。由于您的查询包含更多查询,可能需要进行一些修改才能使其正常工作,但理论仍然相同。
如果无法使用表参数,则创建一个分隔字符串参数来保存多个 Id 值,并将该分隔字符串转换为过程内的表变量。 (网上有很多 T-SQL UDF 可以做到这一点。这篇是一篇好文章。)
那么你的 In 子句将如下所示:
Where [Somecolumn] In (Select funcColName From dbo.ConvertFunction(@stringParameter))
虽然可以在 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 表时生成唯一标识符,然后在执行存储过程时过滤相同的标识符。
您可以查看使用表值参数...
按照我的理解,提问者想要调用一个 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 的评论)