【问题标题】:Delete with params in SqlCommand在 SqlCommand 中使用参数删除
【发布时间】:2011-05-12 22:32:16
【问题描述】:

我使用 ADO.NET 从数据库中删除一些数据,如下所示:

    using (SqlConnection conn = new SqlConnection(_connectionString))
    {
        try
        {
            conn.Open();

            using (SqlCommand cmd = new SqlCommand("Delete from Table where ID in (@idList);", conn))
            {
                cmd.Parameters.Add("@idList", System.Data.SqlDbType.VarChar, 100);
                cmd.Parameters["@idList"].Value = stratIds;

                cmd.CommandTimeout = 0;
                cmd.ExecuteNonQuery();
            }
        }
        catch (Exception e)
        {
            //_logger.LogMessage(eLogLevel.ERROR, DateTime.Now, e.ToString());
        }
        finally
        {
            conn.Close();
        }
    }

该代码在没有异常的情况下执行,但没有从数据库中删除数据。 当我使用相同的算法插入或更新数据库时,一切正常。 有人知道是什么问题吗?

【问题讨论】:

    标签: c# tsql ado.net


    【解决方案1】:

    您不能在常规 TSQL 中这样做,因为服务器将 @idList 视为恰好包含逗号的 single 值。但是,如果您使用List<int>,则可以使用 dapper-dot-net,与

    connection.Execute("delete from Table where ID in @ids", new { ids=listOfIds });
    

    dapper 弄清楚你的意思,并生成适当的参数化。

    另一种选择是发送一个字符串并编写一个 UDF 来执行“拆分”操作,然后在查询中使用该 UDF:

    delete from Table where ID in (select Item from dbo.Split(@ids))
    

    【讨论】:

    • 你说 - “你不能在常规 TSQL 中这样做,因为服务器将 @idList 视为恰好包含逗号的单个值” - 事实上我可以使用代码 SqlCommand cmd = new SqlCommand("Delete from Table where ID in ("+stratIds+");", conn) 和一切工作正常。所以我想我可以用param替换字符串stratIds。
    • @Kate - 我不明白评论
    • @Kate,SQL 参数不能这样工作,您使用字符串连接的示例可以工作,但参数不会被替换掉,也不会像字符串连接一样处理。
    • @Kate - 你的字符串连接策略是等待发生的 SQL 注入攻击。
    • 这就是为什么我要使用SqlParametr
    【解决方案2】:

    根据 Marc 的 Split-UDF,这是一个可行的实现:

    CREATE FUNCTION [dbo].[Split]
    (
        @ItemList NVARCHAR(MAX), 
        @delimiter CHAR(1)
    )
    RETURNS @IDTable TABLE (Item VARCHAR(50))  
    AS      
    
    BEGIN    
        DECLARE @tempItemList NVARCHAR(MAX)
        SET @tempItemList = @ItemList
    
        DECLARE @i INT    
        DECLARE @Item NVARCHAR(4000)
    
        SET @tempItemList = REPLACE (@tempItemList, ' ', '')
        SET @i = CHARINDEX(@delimiter, @tempItemList)
    
        WHILE (LEN(@tempItemList) > 0)
        BEGIN
            IF @i = 0
                SET @Item = @tempItemList
            ELSE
                SET @Item = LEFT(@tempItemList, @i - 1)
            INSERT INTO @IDTable(Item) VALUES(@Item)
            IF @i = 0
                SET @tempItemList = ''
            ELSE
                SET @tempItemList = RIGHT(@tempItemList, LEN(@tempItemList) - @i)
            SET @i = CHARINDEX(@delimiter, @tempItemList)
        END 
        RETURN
    END  
    

    你可以这样称呼它:

    DELETE FROM Table WHERE (ID IN (SELECT Item FROM dbo.Split(@idList, ',')));
    

    【讨论】:

    【解决方案3】:

    我想为这个讨论提供更多背景信息。这似乎属于“如何将多行数据写入 sql”的主题。在@Kate 的案例中,她正在尝试 DELETE-WHERE-IN,但此用户案例的有用策略与 UPDATE-FROM-WHERE-IN 或 INSERT INTO-SELECT FROM 的策略非常相似。在我看来,有一些基本策略。

    字符串连接

    这是最古老也是最基本的方式。你做一个简单的“SELECT * FROM MyTable WHERE ID IN (" + someCSVString + ");”

    • 超级简单
    • 让自己面临 SQL 注入攻击的最简单方法。
    • 您为清理字符串所付出的努力最好花在其他解决方案之一上

    对象映射器

    正如@MarcGravell 建议的那样,您可以使用像 dapper-dot-net 这样的东西,就像 Linq-to-sql 或实体框架一样。 Dapper 可以让你做connection.Execute("delete from MyTable where ID in @ids", new { ids=listOfIds }); 类似地,Linq 会让你做类似from t in MyTable where myIntArray.Contains( t.ID ) 的事情

    • 对象映射器很棒。
    • 但是,如果您的项目是直接 ADO,那么完成一项简单的任务是一个相当严重的更改。

    CSV 拆分

    在此策略中,您将 CSV 字符串传递给 SQL,无论是 ad-hoc 还是作为存储过程参数。该字符串由一个表值 UDF 处理,该 UDF 将值作为单列表返回。

    • 这是自 SQL-2000 以来的成功策略
    • @TimSchmelter 给出了一个很好的 csv 拆分函数示例。
    • 如果您在 google 上搜索,这里有数百篇文章研究了从基础知识到各种字符串长度的性能分析的各个方面。

    表值参数

    在 SQL 2008 中可以定义自定义“表类型”。一旦定义了表类型,就可以在 ADO 中构造它并作为参数向下传递。

    • 这里的好处是它适用于更多的场景,而不仅仅是一个整数列表——它可以支持多列
    • 强类型
    • 将字符串处理拉回到非常擅长的层/语言。
    • 这是一个相当大的话题,但SQL Server 2008 (ADO.NET) 中的表值参数是一个很好的起点。

    【讨论】:

      猜你喜欢
      • 2021-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 2023-03-24
      • 1970-01-01
      相关资源
      最近更新 更多