Pass an array as value in an ado.net DBParameter

試著忘記壹切 提交于 2019-12-23 09:41:56

问题


The project I'm working on has a lot of IN-queries like:

SELECT something, anotherthing
FROM atable
WHERE something IN (value1, value2, value3)

This is an example of a query with 3 parameters in the IN-part but the same query could be executed with 1 or 2 or 5 or 10 or ... parameters. The problem is that each query has an other execution plan in the database with makes it slow.

I'd like to hava a query like this:

SELECT something, anotherthing
FROM atable
WHERE something IN (@value1, @value2, @value3)

or this:

SELECT something, anotherthing
FROM atable
WHERE something IN (@values)

I have accomplished the first query with some helper function, but I still have different execution plan per number of parameters. This could be solved with the second one.

What is the best way to pass an array as database parameter? I'm using Oracle and SQL Server, solutions for both of them are welcome.


回答1:


For SQL-Server, there are two common approaches for this. The third option to be avoided is to pass in a varchar and concatenate it into a dynamic SQL statement with IN - this is a clear injection attack surface.

Reasonable options:

  • pass in a varchar and use a UDF to split the data on a delimiter (like in this question), perhaps comma, pipe, tab, etc. Join to the result:

    SELECT something, anotherthing
    FROM atable a
    INNER JOIN dbo.SplitUDF(@values) udf
            ON udf.Value = a.something
    
  • use a table-valued-parameter (SQL2008) and join directly (avoid the UDF)



回答2:


Have a look at these articles

  • MSSQL Limitations and Arrays
  • Passing Arrays in SQL Parameters using XML Data Type in SQL Server 2005
  • Since there is no Sqlserver array parameter, what’s the best way to proceed?
  • Arrays and Lists in SQL Server 2005

This is an example of using the XML type to create a list

--Split
DECLARE @textXML XML
DECLARE @data NVARCHAR(MAX), 
        @delimiter NVARCHAR(5)

SELECT  @data = 'A,B,C',
        @delimiter = ','

SELECT    @textXML = CAST('<d>' + REPLACE(@data, @delimiter, '</d><d>') + '</d>' AS XML)
SELECT  T.split.value('.', 'nvarchar(max)') AS data
FROM    @textXML.nodes('/d') T(split)



回答3:


This code does the trick. You can create your own BuildQuery(???) function.

    public void RemoveDependencies(int versionID, int[] deps)
    {
        if (versionID <= 0)
            throw new ArgumentException();
        if (deps == null)
            throw new ArgumentNullException();
        if (deps.Length <= 0)
            throw new ArgumentException();

        SqlCommand cmd = new SqlCommand();
        string query = "DELETE FROM Dependencies WHERE version_id = @VersionId AND dep_version_id IN (";
        int n = deps.Length;
        string key;
        for (int i = 0; i < n; i++)
        {
            if (deps[i] <= 0)
                throw new ArgumentException();
            key = String.Format("@dep{0}", i);
            query += key;
            cmd.Parameters.AddWithValue(key, deps[i]);
            if (i < n - 1)
            {
                 query += ", ";
            }
        }
        query += ")";
        cmd.Parameters.AddWithValue("@VersionId", versionID);
        cmd.CommandText = query;
        using (SqlConnection con = GetSqlConnection())
        {
            con.Open();
            cmd.Connection = con;
            if (cmd.ExecuteNonQuery() <= 0)
            {
                throw new ArgumentException("No rows affected! Illegal id.");
            }
        }
    }


来源:https://stackoverflow.com/questions/1968763/pass-an-array-as-value-in-an-ado-net-dbparameter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!