英文:
LINQ Query for searching with Input Array to a database column where it has multiple item with comma seperate value
问题
在我的数据库中,我有一个包含国家列表和区域ID的表格。一个国家可以属于多个区域ID。在这种情况下,区域ID以逗号分隔的字符串形式呈现。这是一个例子。
现在我需要编写LINQ查询来根据输入筛选数据。例如,输入可以是3或'3'或'6','7','3'。对于输入3或'3',我应该得到如上所示的所有14行。为了获取具有搜索字符串的行,我编写了下面的LINQ查询。
int result;
var arrRegionId = input.Split(',');
var countries = countries.WhereIf(arrRegionId.Length > 0,
x => arrRegionId.Select(item => int.TryParse(item.Trim('''), out result) ? result.ToString() : "0")
.Contains(x.RegionId) || arrRegionId.Contains(x.RegionId));
但是使用这个查询,我只能获取除第一条记录(阿富汗)之外的13条记录。因为阿富汗有多个区域ID。
现在我想要一些建议,如何改进LINQ查询,以便我可以获取具有多个区域ID的记录。
英文:
In my database, I have a table containing a country list with region ids. One country can belong to multiple region Ids. In that case, Region IDs are put as a comma-separate value as a string. Here is an example:
Now I need to write a LINQ query to filter data based on input. For example, the input can be like 3 or ‘3’ or '6', '7','3'. For input 3 or ‘3’, I should get all 14 rows as shown above. To get the rows with the search string I wrote the below LINQ Query:
int result;
var arrRegionId = input.Split(',');
var countries = countries.WhereIf(arrRegionId.Length > 0,
x => arrRegionId.Select(item => int.TryParse(item.Trim('\''), out result) ? result.ToString() : "0")
.Contains(x.RegionId) || arrRegionId.Contains(x.RegionId));
But with this Query, I can only get the 13 records apart from the first record (Afghanistan). Because Afghanistan has multiple Region IDs.
Now I would like to have some suggestions, on how can I improve the LINQ Query so that I can get the records which have multiple Region IDs.
答案1
得分: 1
如果您想通过一个或多个地区搜索记录,那么最好的方法是引入一个具有CountryId和RegionId的CountryRegion表,建立多对多的关系。原因是这些外键可以建立索引,您可以在需要查找单个或多个地区的情况下正确构建查询。
如果您可以控制要写入的内容,且不希望进行模式更改,我建议选择一个用于多个值的标准括号格式。例如,使用方括号括住值,无论是单个值还是多个值,如:
[3]
或
[3][11][7]
这样,如果您传递一个“3”的条件,您可以编写:
var region = $"[{input}]";
.Where(x => x.Regions.Contains(region));
但是,这仅适用于搜索单个地区。(input) 看起来您正在传递可能的地区的逗号分隔列表,所以问题是基于多个输入地区ID意味着构建查询的问题。如果您传递“3,7”,这是表示您要查找具有3或7的所有国家,还是您要查找具有3和7的所有国家?
无论您使用标准化的多对多方法还是将多个地区放入单个字段中,都需要解决查询中的“或”或“全部”方面的问题,这意味着需要动态过滤表达式来将条件进行OR或AND连接。这会变得有点复杂,但有一些表达式构建帮助程序可以使其变得更容易。假设您要查找3或7:
var regionIds = input.Split(',').Select(x => $"[{x}]").ToList();
Expression<Func<Country, bool>> expr = null;
foreach(var regionId in regionIds)
{
if (expr == null)
expr = (country) => country.RegionIds.Contains(regionId);
else
expr = Expression<Func<Country, bool>>
.OrElse(expr, (country) => country.RegionIds.Contains(regionId));
}
var countries = context.Countries.Where(expr).ToList();
将分隔列表括在括号中,并强制使其一致,可以简化搜索表达式,但请注意,这种方法仅适用于非常小的数据集。像国家和地区这样的数据,由于行数和关系有限,可能是可以接受的,但在更大的表/数据集中,这将对性能产生非常负面的影响,因为这种搜索无法有效地利用索引,导致更昂贵的表扫描。如果您绝对无法甚至更改RegionId字符串的内容以使值一致地放在括号中,那么可以使用相同的方法,但您的搜索表达式将变得更加复杂。类似于:
expr = (country) => country.RegionIds == regionId || country.RegionIds.Contains("'" + regionId + "'");
...也许可以实现。
英文:
If you want to search across records by one or more regions, then the best thing you can do is introduce a many-to-many relationship using a CountryRegion table with CountryId and RegionId. The reason for this is that these FKs can be indexed where you can build queries properly in situations where you want to find countries for a single or for multiple regions.
If you have control over what gets written and don't want to make schema changes, I would recommend opting for a standard bracketed format for multiple values. For instance using square brackets around values, whether single or multiple, I.e:
[3]
or
[3][11][7]
This way if you pass a criteria of "3" you can write:
var region = $"[{input}]";
.Where(x => x.Regions.Contains(region));
However that only works for searching for a singular region. (input) It looks like you are passing a comma delimited list of possible regions, so then the question is building a query based on what multiple input region IDs means. If you pass "3,7" does that mean you want all countries that have either 3 or 7, or do you want all countries that have both 3 and 7?
Regardless of your approach whether using a normalized many-to-many or hacking the multiple regions into a single field, you will need to address the "either" or "all" aspect of the query, and that means a dynamic filter expression to either OR the conditions or AND them together. This gets a bit messy, but there are expression builder helpers out there that might make it a bit easier. Assuming you want either 3 or 7:
var regionIds = input.Split(',').Select(x => $"[{x}]").ToList();
Expression<Func<Country, bool>> expr = null;
foreach(var regionId in regionIds)
{
if (expr == null)
expr = (country) => country.RegionIds.Contains(regionId);
else
expr = Expression<Func<Country, bool>>
.OrElse(expr, (country) => country.RegionIds.Contains(regionId));
}
var countries = context.Countries.Where(expr).ToList();
Bracketing the delimited list, and enforcing it to be consistent can make searching expressions simpler, but keep in mind that this approach should only be used for very small sets. Something like countries and regions is likely Ok given the limited number of rows and relationships, but in much larger tables/sets this will have a very negative performance impact as searches like this cannot leverage indexes properly, resulting in more expensive table scans. If you absolutely cannot even change the contents of this RegionId string to bracket the values consistently, then the same approach can be used, but your search expressions will be a lot more complicated. Something like:
expr = (country) => country.RegionIds == regionId || country.RegionIds.Contains("'" + regionId + "'");
... might do it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论