Linq 2 Sql 使用 'Like' 将本地序列与查询进行比较 - 最有效的方法
Linq 2 Sql Compare a local sequence to a query using 'Like' - most efficient method
我知道这里有几个类似的问题,关于使用 Sql 'Like' 操作作为比较器将列表与查询进行比较。
我所知道的是,没有使用 Linq2Sql 的直接翻译来进行此类比较。提供的方法 Contains() 转换为 Sql 中的 'IN' 并且是一个精确的比较器。我也知道非精确比较方法(SqlMethods.Like()、String.Contains()、StartsWith() 和 EndsWith())都适用于单个项目,而不是列表。
目前,我在 foreach 循环中进行查询以提供所需的功能,但是,我担心我的网络带宽以及大型循环对数据库服务器的压力。
我的问题是:执行此操作最有效的方法是什么,同时最大限度地减少网络/服务器负载?
我当前实现的代码片段:
using (var context = new GeoDataContext())
{
var originalSuggestions = new List<SuggestItem>();
foreach (var suggestItem in suggestionList)
{
var item = suggestItem;
var placeNameList = context.tl_2014_39_places.Where(placeName =>
placeName.NAME.Contains(item.Term)).Select(
place => place.NAME).ToList();
}
}
试试这个,
如果你想检查Term
有placeName.NAME
var placeNameList = context.tl_2014_39_places
.Where(placeName => suggestionList
.Select(x => x.Term)
.Contains(placeName.NAME))
.Select(place => place.NAME).ToList();
如果你想检查placeName.NAME
有Term
var placeNameList = context.tl_2014_39_places.AsEnumerable()
.Where(placeName => suggestionList.Any(x => placeName.Conaints(x.Term))
.Select(place => place.NAME).ToList();
为了避免区分大小写的搜索,您可以对两个比较字符串使用ToLower()
方法,例如。 placeName.ToLower().Conaints(x.Term.ToLower())
如果您确实 需要性能,在这种情况下您应该使用原始 sql 查询。即便如此,它也不会超级容易(同样,如果您需要性能)。您必须像这样创建自定义类型:
CREATE TYPE dbo.StringList AS TABLE (value NVARCHAR(MAX));
然后在 C# 代码中将这种类型的变量(即@terms)声明为 DataTable,然后执行类似
的操作
select NAME from tl_2014_39_places pl where exists (select 1 from @terms where pl.NAME like '%' + value + '%')
或使用@terms 变量加入您的 table。然后执行 SqlCommand,您将在一个查询中获得结果。如果您决定遵循原始 sql 查询的路线,我可以帮助您找到正确有效的查询(上面的查询只是它的外观示例)。
更新:这里是完整的工作示例。它假设有 table tl_2014_39_places 列 NAME 并且您使用上面的查询创建了自定义类型 StringList:
private static void Main(string[] args) {
var termsTable = new DataTable();
var suggestionList = new List<string>() {"one", "two", "three"};
termsTable.Columns.Add(new DataColumn("value", typeof (string)));
foreach (var term in suggestionList) {
termsTable.Rows.Add(term);
}
using (var conn = new SqlConnection(@"data source=(LocalDb)\v11.0;initial catalog=TestDB;integrated security=True;")) {
conn.Open();
using (var cmd = new SqlCommand("select NAME from tl_2014_39_places pl where exists (select 1 from @terms where pl.NAME like '%' + value + '%') ", conn)) {
cmd.Parameters.Add(new SqlParameter("terms", SqlDbType.Structured) {
Value = termsTable,
TypeName = "dbo.StringList"
});
using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) {
Console.WriteLine(reader[0]);
}
}
}
}
Console.ReadKey();
}
请注意,通常带有第一个 '%' 的 LIKE(即 "ends with" 查询)效率低下,因为需要扫描完整的 table 才能获得结果。但这与问题本身无关,当然它仍然比循环和多个数据库查询更有效。您还可以使用以下等效查询代替 where exists:
select NAME from tl_2014_39_places pl inner join @terms t on pl.NAME like '%' + t.value + '%'
我知道这里有几个类似的问题,关于使用 Sql 'Like' 操作作为比较器将列表与查询进行比较。
我所知道的是,没有使用 Linq2Sql 的直接翻译来进行此类比较。提供的方法 Contains() 转换为 Sql 中的 'IN' 并且是一个精确的比较器。我也知道非精确比较方法(SqlMethods.Like()、String.Contains()、StartsWith() 和 EndsWith())都适用于单个项目,而不是列表。
目前,我在 foreach 循环中进行查询以提供所需的功能,但是,我担心我的网络带宽以及大型循环对数据库服务器的压力。
我的问题是:执行此操作最有效的方法是什么,同时最大限度地减少网络/服务器负载?
我当前实现的代码片段:
using (var context = new GeoDataContext())
{
var originalSuggestions = new List<SuggestItem>();
foreach (var suggestItem in suggestionList)
{
var item = suggestItem;
var placeNameList = context.tl_2014_39_places.Where(placeName =>
placeName.NAME.Contains(item.Term)).Select(
place => place.NAME).ToList();
}
}
试试这个,
如果你想检查Term
有placeName.NAME
var placeNameList = context.tl_2014_39_places
.Where(placeName => suggestionList
.Select(x => x.Term)
.Contains(placeName.NAME))
.Select(place => place.NAME).ToList();
如果你想检查placeName.NAME
有Term
var placeNameList = context.tl_2014_39_places.AsEnumerable()
.Where(placeName => suggestionList.Any(x => placeName.Conaints(x.Term))
.Select(place => place.NAME).ToList();
为了避免区分大小写的搜索,您可以对两个比较字符串使用ToLower()
方法,例如。 placeName.ToLower().Conaints(x.Term.ToLower())
如果您确实 需要性能,在这种情况下您应该使用原始 sql 查询。即便如此,它也不会超级容易(同样,如果您需要性能)。您必须像这样创建自定义类型:
CREATE TYPE dbo.StringList AS TABLE (value NVARCHAR(MAX));
然后在 C# 代码中将这种类型的变量(即@terms)声明为 DataTable,然后执行类似
的操作select NAME from tl_2014_39_places pl where exists (select 1 from @terms where pl.NAME like '%' + value + '%')
或使用@terms 变量加入您的 table。然后执行 SqlCommand,您将在一个查询中获得结果。如果您决定遵循原始 sql 查询的路线,我可以帮助您找到正确有效的查询(上面的查询只是它的外观示例)。
更新:这里是完整的工作示例。它假设有 table tl_2014_39_places 列 NAME 并且您使用上面的查询创建了自定义类型 StringList:
private static void Main(string[] args) {
var termsTable = new DataTable();
var suggestionList = new List<string>() {"one", "two", "three"};
termsTable.Columns.Add(new DataColumn("value", typeof (string)));
foreach (var term in suggestionList) {
termsTable.Rows.Add(term);
}
using (var conn = new SqlConnection(@"data source=(LocalDb)\v11.0;initial catalog=TestDB;integrated security=True;")) {
conn.Open();
using (var cmd = new SqlCommand("select NAME from tl_2014_39_places pl where exists (select 1 from @terms where pl.NAME like '%' + value + '%') ", conn)) {
cmd.Parameters.Add(new SqlParameter("terms", SqlDbType.Structured) {
Value = termsTable,
TypeName = "dbo.StringList"
});
using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) {
Console.WriteLine(reader[0]);
}
}
}
}
Console.ReadKey();
}
请注意,通常带有第一个 '%' 的 LIKE(即 "ends with" 查询)效率低下,因为需要扫描完整的 table 才能获得结果。但这与问题本身无关,当然它仍然比循环和多个数据库查询更有效。您还可以使用以下等效查询代替 where exists:
select NAME from tl_2014_39_places pl inner join @terms t on pl.NAME like '%' + t.value + '%'