Sql/Dapper: 如何在值数组的 WHERE 子句中执行 LIKE?
Sql/Dapper: How do perform LIKE in WHERE clause for array of values?
我有一个 table 里面有很多员工,每个人都有一个姓名栏,里面有他们的全名。
然后我想在搜索人的时候做一个类似这样的查询:
SELECT * FROM Employees WHERE Name LIKE '%' + @value1 + '%' AND Name LIKE '%' + @value2 +'%' AND so forth...
对于任意值数组。
我的 Dapper 代码看起来像这样:
public IEnumerable<Employee> Search(string[] words)
{
using var connection = CreateConnection();
connection.Query<Employee>("SELECT * etc.", words);
}
有没有什么方法可以在不求助于字符串连接的情况下使用 SQL 做到这一点,以及随之而来的 SQL 注入攻击的风险?
警告:我不知道 Dapper 实际上是如何将数组传递给查询的,这限制了我解决这个问题的创意:-D
而且:不幸的是,更改 Table 结构是不可能的。而且我宁愿避免将每个人都提取到 .Net 内存中并在那里进行过滤。
Is there ANY way to do this with SQL without resorting to string concatenation, and the risk of SQL Injection attacks that follows?
由于 where 条件的集合不固定,您需要动态构建查询。但这并不意味着您不能对查询进行参数化,您只需在构建查询的同时构建参数列表。每次将列表中的一个词添加到条件并添加一个参数。
由于 Dapper 不直接包含任何需要 DbParameter
集合的内容,请考虑使用 ADO.NET 获取 IDataReader
,然后是 Dappter 的
IEnumerable<T> Parse<T>(this IDataReader reader)
用于映射。
这样的builder会很粗略
var n = 0;
for (criterion in cirteria) {
var cond = $"{crition.column} like @p{n}";
var p = new SqlPatameter($"@p{n}", $"%{crition.value}%";
conditions.Add(cond);
cmd.Parameters.Add(p);
}
var sql = "select whetever from table where " + String.Join(" and ", conditions);
cmd.CommandText = sql;
var reader = await cmd.ExecuteReaderAsync();
var res = reader.Parse<TResult>();
出于性能原因,最好将其作为基于集合的操作执行。
您可以将数据table 作为Table 值参数传递,然后以LIKE
作为条件加入该数据。在这种情况下,您希望 所有 值匹配,因此您需要一点关系除法。
首先创建您的 table 类型:
CREATE TYPE dbo.StringList AS TABLE (str varchar(100) NOT NULL);
你的SQL如下:
SELECT *
FROM Employees e
WHERE NOT EXISTS (SELECT 1
FROM @words w
WHERE e.Name NOT LIKE '%' + w.str + '%' ESCAPE '/' -- if you want to escape wildcards you need to add ESCAPE
);
然后你通过列表如下:
public IEnumerable<Employee> Search(string[] words)
{
var table = new DataTable{ Columns = {
{"str", typeof(string)},
} };
foreach (var word in words)
table.Rows.Add(SqlLikeEscape(word)); // make a function that escapes wildcards
using var connection = CreateConnection();
return connection.Query<Employee>(yourQueryHere, new
{
words = table.AsTableValuedParameter("dbo.StringList"),
});
}
我有一个 table 里面有很多员工,每个人都有一个姓名栏,里面有他们的全名。
然后我想在搜索人的时候做一个类似这样的查询:
SELECT * FROM Employees WHERE Name LIKE '%' + @value1 + '%' AND Name LIKE '%' + @value2 +'%' AND so forth...
对于任意值数组。
我的 Dapper 代码看起来像这样:
public IEnumerable<Employee> Search(string[] words)
{
using var connection = CreateConnection();
connection.Query<Employee>("SELECT * etc.", words);
}
有没有什么方法可以在不求助于字符串连接的情况下使用 SQL 做到这一点,以及随之而来的 SQL 注入攻击的风险?
警告:我不知道 Dapper 实际上是如何将数组传递给查询的,这限制了我解决这个问题的创意:-D
而且:不幸的是,更改 Table 结构是不可能的。而且我宁愿避免将每个人都提取到 .Net 内存中并在那里进行过滤。
Is there ANY way to do this with SQL without resorting to string concatenation, and the risk of SQL Injection attacks that follows?
由于 where 条件的集合不固定,您需要动态构建查询。但这并不意味着您不能对查询进行参数化,您只需在构建查询的同时构建参数列表。每次将列表中的一个词添加到条件并添加一个参数。
由于 Dapper 不直接包含任何需要 DbParameter
集合的内容,请考虑使用 ADO.NET 获取 IDataReader
,然后是 Dappter 的
IEnumerable<T> Parse<T>(this IDataReader reader)
用于映射。
这样的builder会很粗略
var n = 0;
for (criterion in cirteria) {
var cond = $"{crition.column} like @p{n}";
var p = new SqlPatameter($"@p{n}", $"%{crition.value}%";
conditions.Add(cond);
cmd.Parameters.Add(p);
}
var sql = "select whetever from table where " + String.Join(" and ", conditions);
cmd.CommandText = sql;
var reader = await cmd.ExecuteReaderAsync();
var res = reader.Parse<TResult>();
出于性能原因,最好将其作为基于集合的操作执行。
您可以将数据table 作为Table 值参数传递,然后以LIKE
作为条件加入该数据。在这种情况下,您希望 所有 值匹配,因此您需要一点关系除法。
首先创建您的 table 类型:
CREATE TYPE dbo.StringList AS TABLE (str varchar(100) NOT NULL);
你的SQL如下:
SELECT *
FROM Employees e
WHERE NOT EXISTS (SELECT 1
FROM @words w
WHERE e.Name NOT LIKE '%' + w.str + '%' ESCAPE '/' -- if you want to escape wildcards you need to add ESCAPE
);
然后你通过列表如下:
public IEnumerable<Employee> Search(string[] words)
{
var table = new DataTable{ Columns = {
{"str", typeof(string)},
} };
foreach (var word in words)
table.Rows.Add(SqlLikeEscape(word)); // make a function that escapes wildcards
using var connection = CreateConnection();
return connection.Query<Employee>(yourQueryHere, new
{
words = table.AsTableValuedParameter("dbo.StringList"),
});
}