我有一个查询,我想返回与值列表关联的所有行。你可以这么简单地写成:
select * from TableA where ColumnB in (1, 2, 3, 5)
我可以在C#中生成此查询并执行它。然而,这显然不太理想,因为它不使用参数,在尝试缓存查询计划时会受到影响,并且显然容易受到SQL注入攻击。
另一种方法是将其写为:
select * from TableA where ColumnB = @value
这可以通过C#执行多次,但这会导致N DB命中。
我能看到的唯一其他选择是创建一个临时表并以这种方式加入它,但是我没有看到这一点,因为它会更复杂并且受到与第一个选项相同的限制。 / p>
我正在使用SQL Server和OLDB,创建查询不是问题。我正在努力创造最有效的流程。
这三种方法中哪一种更有效?我错过了替代方案吗?
答案 0 :(得分:4)
假设SQL Server 2008或更高版本,在SQL Server中创建一次表类型:
CREATE TYPE dbo.ColumnBValues AS TABLE
(
ColumnB INT
);
然后是一个采用输入类型的存储过程:
CREATE PROCEDURE dbo.whatever
@ColumnBValues dbo.ColumnBValues READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT A.* FROM dbo.TableA AS A
INNER JOIN @ColumnBValues AS c
ON A.ColumnB = c.ColumnB;
END
GO
现在在C#中,创建一个DataTable并将其作为参数传递给存储过程:
DataTable cbv = new DataTable();
cbv.Columns.Add(new DataColumn("ColumnB"));
// in a loop from a collection, presumably:
cbv.Rows.Add(someThing.someValue);
using (connectionObject)
{
SqlCommand cmd = new SqlCommand("dbo.whatever", connectionObject);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter cbvParam = cmd.Parameters.AddWithValue("@ColumnBValues", cbv);
cbvParam.SqlDbType = SqlDbType.Structured;
//cmd.Execute...;
}
(您可能希望使该类型更通用,我专门命名它以明确它正在做什么。)
答案 1 :(得分:2)
你也可以使用multiple resultsets并发送一个像这样的查询:
select * from TableA where ColumnB = @value0
select * from TableA where ColumnB = @value1
select * from TableA where ColumnB = @value2
...
select * from TableA where ColumnB = @valuen
在单一电话中。 即使显然反直觉,它也会利用执行计划,并且在参数化方面是安全的。
答案 2 :(得分:0)
您可以轻松地写下这个:
String csvString = "1, 2, 3, 5"; // Built the list somehow, don't forget escaping
String query = "select * from TableA where ColumnB in (" + csvString + ")";
通过这种方式,性能不会降低,并且您可以在创建csvString
时阻止 Sql Injection 简单地转义输入值。
顺便说一句,如果你使用 MS SQL 而不是标准SQL,你可以find alternative ways。