我总是使用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注入?
答案 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)中