【问题标题】:Big parameter list for SQL querySQL查询的大参数列表
【发布时间】:2011-04-30 07:31:25
【问题描述】:

我有一个 SQL 查询的 int 参数的大列表:

update mytable set col='xyz'
where id in (/* thousands of ints */)

我的问题是在 SQL Server 2000 中对参数有限制。 我也可以在 SQL Server 2008 上运行此查询。

有什么更好的方法来做到这一点。

编辑:

ID 列表来自 C# 程序。不是来自另一个表。

【问题讨论】:

    标签: c# sql-server sql-server-2008 sql-server-2000


    【解决方案1】:

    您可以将整数插入临时表,然后像这样查询:

    update mytable m set col='xyz' 
    where exists (select * from #MyTempTable where id = m.id)
    

    【讨论】:

    • 这是个好主意吗? 5000x Insert into #MyTempTable 的执行时间可能过高。
    • IN不是如果列表不止几个的好选择。 EXISTS 是要走的路。
    • @Floyd:您可以通过UNION ALL 一次性插入。
    • 我的反对票已被删除。 这个地方就像在讲笑话:如果你是一名喜剧演员,人们会认为你很有趣,几乎什么都笑。我可以讲同样的笑话,没有人会笑。在这里,即使页面上有更好的解决方案,人们也会投票给代表最多的人。
    【解决方案2】:

    另一种适用于 SQL 2000 的方法是使用 XML。

    让程序/应用程序像这样格式化整数:

    '<root><TMP J="111"/><TMP J="222"/><TMP J="333"/></root>'
    

    .
    然后创建如下存储过程:

    CREATE PROCEDURE UpDateIntsFromXML (
        @sXML TEXT
    )
    AS
        DECLARE @iDoc INT
        EXEC    sp_xml_preparedocument @iDoc OUTPUT, @sXML
    
        UPDATE  YourTable
        SET     YourColumn = 'fixed value'
        FROM    OPENXML (@iDoc, '/root/TMP', 1) WITH (J INT) AS X
        WHERE   X.J = YourTable.IntColumn
    
        EXEC    sp_xml_removedocument @iDoc
    RETURN
    

    .
    然后您的应用程序可以调用该 SP,并传递一个可能很大的文本/XML 块。

    注意rootTMPJ 都区分大小写。

    【讨论】:

    • 这种方式可行,我们试试。我们还找到了另外两种方法:首先Table-Valued Parameters SqlBulkCopy .. 我们也会尝试这个。
    • 这可行,但表值参数更快。但此功能不适用于 SQL-2000。
    • 您的意思是 Table-Valued-Parameters 不适用于 SQL-2000。 XML 适用于 2000 及更高版本。
    【解决方案3】:

    对我来说最好的工作解决方案是SQL Server 2008: Table Valued Parameters

    100000 个 ID 需要 14-20 秒,1000 个 ID 需要 ~140 毫秒。

    sql = @"
      update MyTable
        set Col1 = 1
        where ID in (select * from @ids)
      ";
    sqlCmd = new SqlCommand {Connection = _sqlConn, CommandText = sql};
    
    //Create a DataTable with one Column("id") and all ids as DataRows
    DataTable listOfLeadIDs = new DataTable();
    listOfIDs.Columns.Add("id", typeof(int));
    Ids.ToList<string>().ForEach(x => listOfIDs.Rows.Add(new object[] { int.Parse(x) }));
    
    //Bind this DataTable to the Command-object
    // Node: "IntTable" is an User-Defined-Table-Typ (new feature with SQL-2008)
    sqlCmd.Parameters.Add(
      new System.Data.SqlClient.SqlParameter("@ids", listOfIDs) { 
        TypeName = "IntTable" 
      });
    
    //Execute the Query
    sqlCmd.ExecuteNonQuery();
    

    用户定义的表类型:

    CREATE TYPE [dbo].[IntTable] AS TABLE(
        [id] [int] NULL
    )
    GO
    

    【讨论】:

      【解决方案4】:

      不惜一切代价,避免 IN;特别是如果您是 2000 年后的人。 my backup


      改为使用EXISTS

      UPDATE myTable
      SET col = 'newValue'
      FROM myTable 
      WHERE EXISTS (
            SELECT * 
            FROM @myTempTable temp
            WHERE myTable.ID = temp.ID)
      

      【讨论】:

      • 与 RedFilters 帖子相同:5000x Insert into #MyTempTable 的执行时间可能过高。
      • 我认为 5000 次插入单个 INT 不会那么糟糕。还有其他方法可以通过查询获得INTs 的列表吗?
      • 不,我不能。那是我的问题。由程序生成的 ID 列表。
      • 试一试并进行基准测试怎么样。创建临时表时,将其聚集在 ID 上,并在进入数据库之前对列表进行预排序。
      • 我强烈反对第一句话(但还没有被否决)。 IN 准确地表达了意图。 EXISTS 表单将其转换为相关子查询,随着数据大小的增加,它的性能可能会很糟糕。
      【解决方案5】:

      将数据分成更小的组,并执行多个更新查询。

      没有理由使用临时表,因为您从数据库外部检索数据,因此无法避免将其传输到数据库。

      【讨论】:

      • 这适用于INEXISTS,但不适用于NOT INNOT EXISTS
      • 但是查询确实使用了“IN”,而不是“NOT IN”。您在较小的数据集上多次执行相同的查询...
      • 但我的问题是“用于 sql 查询的位参数列表”而不是“用于“in”查询的位参数列表。。查询只是一个示例。
      • 那么创建一个临时表并用它做任何你想做的事情。
      • 这不是我的问题.. 我的问题是程序到数据库的传输形式。使用Insert into tempTable 我得到一个错误。也请阅读 cmets 以进行发布。
      【解决方案6】:

      如果整数以任何方式是连续的(一次超过两个),您可以将它们组成BETWEEN 对。

      但在这种情况下,只需将这些整数组成一个字符串并将其作为单个 varchar(max) 参数传递。

      【讨论】:

        【解决方案7】:

        我认为您可能想创建一个基于内存的带有索引的临时表。假设您要查询的表很大,您不希望进行表扫描,将每一行与 5000 个匹配项中的每一个进行比较。您想在两个索引的帮助下进行连接。

        CREATE TEMPORARY TABLE IF NOT EXISTS inputlist
        (i INT PRIMARY KEY) ENGINE = MEMORY;
        
        INSERT INTO inputlist (i) VALUES (1),(2),(3),(1000),(2000),(5000);
        
        SELECT * FROM your_table JOIN inputlist ON your_table.intvalues = inputlist.i;
        
        DROP TEMPORARY TABLE inputlist;
        

        基于 MySQL 的 SQL,参见:
        http://dev.mysql.com/doc/refman/5.1/en/memory-storage-engine.html
        http://dev.mysql.com/doc/refman/5.1/en/insert.html
        http://dev.mysql.com/doc/refman/5.1/en/create-table.html

        【讨论】:

        • 这不适用于 sql-2000 或 sql-2008。如果我也尝试使用单插入(sql-2000 中支持多插入),我会收到一个错误:Msg 8621, Level 17, State 88, Line 5 Internal Query Processor Error: The query processor ran out of stack space during query optimization. 我会以块的形式发送插入,但这需要很多时间进行传输和语句编译。
        • 我的错,没注意MS Sql的注释。我对那里不是很熟悉,但我确实注意到一个页面建议解决此问题,也许值得快速测试:planet-source-code.com/vb/scripts/…
        • 这个解决方案(来自快速测试的来源)有效,但只有很少的 id。没有数百也没有千。
        • 伤心,好吧。我很想看看什么是最好的解决方案。如果它不是建议的选项之一,请确保将其张贴在这里。有趣的问题。
        • 对于这个博客说你可以用 SQL 2008 执行多个值插入(对不起,我的主要经验是使用 MySQL)是值得的:blogs.microsoft.co.il/blogs/bursteg/archive/2007/12/05/… 另外,这里有一个以逗号分隔的选项列表到一个存储过程。您可以尝试使用此示例填充内存中的临时表,如上所述:daniweb.com/forums/thread34491.html 同样,您必须尝试并分析它以查看它是否是一个可用的解决方案,但它不应该花费太多精力对于测试用例
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-01
        • 2019-11-18
        • 2019-10-17
        • 2010-09-21
        • 2020-05-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多