【发布时间】:2021-02-16 15:36:27
【问题描述】:
也许这对我来说是一个不可靠的设计决定,但我创建了一个表,其中有一列包含 1 到 N 个逗号分隔值,我需要查询这些值中是否包含多个值中的任何一个。
为了更清楚,我允许用户选择一系列电影类型(喜剧、戏剧等),并有一个“电影”表,这些类型都包含在一个列中。例如,电影“公主新娘”的流派列包含复合 csv 值“冒险、家庭、奇幻”
因此,如果用户至少选择了其中一种类型,则该电影应包含在根据他们选择的搜索条件返回的结果集中。但电影应该只返回一次,即使用户选择了不止一种类型来搜索。
数据库的前一个化身包含查找表和多对多表,它们避免了这个问题,但为了通过将其设为一个大(宽)表来使其“更容易”和更直接,我运行了进入这个难题(关于如何制作查询)。
查询目前是以这种方式动态构建的:
string baseQuery = "SELECT * FROM MOVIES_SINGLETABLE ";
string imdbRatingFilter = "WHERE IMDBRating >= @IMDBMinRating ";
string yearRangeFilter = "AND YearReleased BETWEEN @EarliestYear AND @LatestYear ";
string genreFilterBase = "AND GENRE IN ({0}) ";
string mpaaRatingFilterBase = "AND MPAARating IN ({0}) ";
string orderByPortion = "ORDER BY IMDBRating DESC, YearReleased DESC ";
...以及包含所选标准的字符串,如下所示:
if (filterGenres)
{
if (ckbxAction.Checked) genresSelected = "\'Action\',"; // changed to "@" hereafter:
if (ckbxAdventure.Checked) genresSelected = genresSelected + @"'Adventure',";
. . .
if (ckbxWar.Checked) genresSelected = genresSelected + @"'War',";
if (ckbxWestern.Checked) genresSelected = genresSelected + @"'Western',";
LastCommaIndex = genresSelected.LastIndexOf(',');
genresSelected = genresSelected.Remove(LastCommaIndex, 1)
}
// the same situation holds for mpaaRatings as for Genres:
if (filterMPAARatings)
{
if (ckbxG.Checked) mpaaRatingsSelected = @"'G',";
if (ckbxPG.Checked) mpaaRatingsSelected = mpaaRatingsSelected + @"'PG',";
if (ckbxPG13.Checked) mpaaRatingsSelected = mpaaRatingsSelected + @"'PG13',";
if (ckbxNR.Checked) mpaaRatingsSelected = mpaaRatingsSelected + @"'NR',";
LastCommaIndex = mpaaRatingsSelected.LastIndexOf(',');
mpaaRatingsSelected = mpaaRatingsSelected.Remove(LastCommaIndex, 1);
}
. . .
//string genreFilterBase = "AND GENRES IN ({0}) ";
if (filterGenres)
{
genreFilter = string.Format(genreFilterBase, genresSelected);
completeQuery = completeQuery + genreFilter;
}
//string mpaaRatingFilterBase = "AND MPAARating IN ({0}) ";
if (filterMPAARatings)
{
mpaaRatingFilter = string.Format(mpaaRatingFilterBase, mpaaRatingsSelected);
completeQuery = completeQuery + mpaaRatingFilter;
}
我的设计可以挽救吗? IOW,鉴于这些公认的有问题的表设计决策,我可以检索适当的数据吗?
更新
我通过将 GMB 的 SQL 合并到我的 SQL 中来测试它,但我可能做错了什么,因为它无法编译:
我不知道为什么会有这些逗号,但我认为 GMB 比我更擅长 SQL...
尽管如此,这确实工作(没有逗号和管道):
更新 2
我也尝试过使用 CONTAINS:
从 MOVIES_SINGLETABLE 中选择 * WHERE IMDBRating >= 7.5 和年份在 '1980' 和 '2020' 之间发布 并包含(类型,“冒险”) 或包含(流派、“家庭”) 或包含(类型,“奇幻”) 并包含(MPAARating,'G') 或包含(MPAARating,'PG') 或包含(MPAARating,'PG-13') ORDER BY IMDBRating DESC, YearReleased DESC
...但是得到“不能在表或索引视图 'MOVIES_SINGLETABLE' 上使用 CONTAINS 或 FREETEXT 谓词,因为它不是全文索引。”
Alex Aza 的答案 [https://stackoverflow.com/questions/6003240/cannot-use-a-contains-or-freetext-predicate-on-table-or-indexed-view-because-it]提供了一个解决方案,但显然它不适用于 SQL Server Express:
...此外,无论如何,这最终(很快)将被迁移到 SQLite 表,我怀疑如果在 SQL Server(尽管 Express)中执行它需要,即使可能,SQLite 是否支持 CONTAINS通过篮球。
更新 3
我结合了 Lukasz 对 SQLite 的想法(这就是我现在正在查询的内容),查询字符串最终是:
选择电影标题、MPAARating、IMDBRating、DurationInMinutes、 上映年份、流派、演员、导演、编剧 FROM MOVIES_SINGLETABLE WHERE IMDBRating >= @IMDBMinRating AND (在@EarliestYear 和@LatestYear 之间发布的年份)和 (INSTR(genres, @genre1) > 0 或 INSTR(genres, @genre2) > 0) AND (MPAARating = @mpaaRating1) 按 IMDBRating DESC 排序,发布年份 DESC 限制 1000
...但仍然没有结果。
【问题讨论】:
-
这里有什么不使用LINQ的理由吗?
-
没有“也许”。 :) 您成为classic blunders 之一的受害者——其中最著名的是“永远不要卷入亚洲的陆战”——但鲜为人知的是:“永远不要将 csv 值放在表格列中!”
-
我猜这已经回答了?设计不好,如果数据库很小或不需要性能,您可以摆脱类似的条件,因为所有此类查询都会执行全表扫描。如果那是例如 Postgres 数据库 - 您可以将其存储在数组列(可索引)中,但不是在这里。另外,为什么 year 是 nchar(4) 而不是数字类型?
-
在该屏幕截图上,您正在执行年份之间的查询,您假设进行数值比较。
-
@B.ClayShannon 您能否为某些参数值添加 3 或 4 行示例数据和您的预期结果集。这将帮助我验证您的查询并提出建议。
标签: c# sql sqlite csv where-clause