如何清理TSQL

时间:2018-01-10 14:30:25

标签: c# sql-server tsql ado.net

我总是使用SqlParameter在从C#调用SQL Server时参数化我的查询,然而,在使用参数时,我的一些查询会生成一个丑陋的执行计划,在这些情况下,我宁愿使用硬编码的SQL而不是参数。这是一个好习惯吗? - 也许不是,但查询的表现很棒!

由于SQL Server中的Parameter Sniffing,我必须使用这种技术。

现在,有没有人有一个库来清理TSQL代码以避免SQL注入? - 如果没有,我应该检查什么来防止这种情况? (也许我会最终创建一个NuGet包。)

也许你知道其他方法可以避免这个让我继续使用SqlParameters的问题吗?

谢谢!

更新1:由于另一个问题Improve performance of query with conditional filtering出现了这个问题 在这里你可以看到为什么我必须找到这个解决方案。

更新2:为了完成问题的上下文:我有以下查询,使用ADO.NET从C#代码调用,使用参数时,查询需要7s到几分钟才能执行,具体取决于参数。

SELECT SKU, Store, ColumnA, ColumnB, ColumnC
FROM myTable
WHERE (SKU IN (select * from splitString(@skus)) OR @skus IS NULL)
AND (Store IN (select * from splitString(@stores)) OR @stores IS NULL)

@skus和@stores是带有连接ID的字符串,如'1,2,3'和splitString是一个数据库函数,它在1列的表中转换该字符串,并且每行中的每个项都允许我在SQL Server中执行IN过滤。

当执行这个没有参数的相同查询并在查询中直接连接ids时,执行时需要0到5秒,具体取决于ID。我在where子句中使用函数时知道性能问题,并且我在查询的最终形式中处理了这个问题,但它不会像使用原始sql那样影响性能。请阅读本文并采纳这个想法而不是字面意思。

由于性能问题我被迫使用第二种方法,我该如何避免SQL注入?

2 个答案:

答案 0 :(得分:1)

就像我在评论中写的那样(被移动到聊天中) - 使用表值参数而不是SQL逗号分隔的字符串拆分。由于我无法复制,我不妨在这里给出答案。

使用表值参数需要存储过程和用户​​定义的表类型,所以让我们继续创建表类型:

CREATE TYPE dbo.IntList AS TABLE
(
    val int NOT NULL PRIMARY KEY
);
GO

现在让我们创建存储过程:

CREATE PROCEDURE dbo.GetValuesBySkuOrStore
(
    @skus dbo.IntList readonly,
    @stores dbo.IntList readonly,
)
AS

    SELECT SKU, Store, ColumnA, ColumnB, ColumnC
    FROM myTable
    LEFT JOIN @skus skus ON SKU = skus.val
    LEFT JOIN @stores stores ON Store = stores.val
    WHERE skus.val IS NOT NULL 
    OR stores.val IS NOT NULL 

GO

现在,要从c#执行此操作,您需要首先为每个表值参数创建一个DataTable,然后将它们添加到类型为SqlDbType.Structured的参数集合中。像这样:

DataTable CreateIntList(IEnumerable<int> values)
{
    var dt = new DataTable();
    // Note: Columns order matters! 
    // if you have a udt with multiple columns, 
    // your data table must reflect the columns exactly in the same order.
    dt.Columns.Add("val", typeof(int));
    foreach(var val in values)
    {
        dt.Rows.Add(val);
    }
}

void GetValuesBySkuOrStore(Ienumerable<int> skus, Ienumerable<int> stores)
{
    var skusDT = CreateIntList(skus);
    var storesDT = CreateIntList(stores); 

    using(var con = new SqlConnection(connectionString))
    {
        using(var cmd = new SqlCommand("GetValuesBySkuOrStore", con))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add("@skus", SqlDbType.Structured).Value = skus;
            cmd.Parameters.Add("@stores", SqlDbType.Structured).Value = stores;
            con.Open();
            using(var reader = cmd.ExecuteReader())
            {
                while(reader.Read())
                {
                    // do your stuff here...
                }
            }
        }
    }
}

这可能与硬编码值的性能不同,但它受SQL注入保护,并且在SQL Server中拆分逗号分隔值方面具有更好的性能。

答案 1 :(得分:0)

有人可能证明我错了,但如果sku和store是整数,那么就没有SQL注入

where sku in (1, 2, 3)是安全的

我将字符串解析为你很好的整数 如果它不解析为整数,则不要让它通过

如果你真的想使用一个函数,那么调用一次并将输出存储在表值参数(TVP)中