T.G*_*ang 3 sql-server query-performance
我有一个很大的Product表:
| 产品编号 | 产品名称 |
|---|---|
| 1 | 产品一 |
| 2 | 产品二 |
| ... | ... |
| n | 产品编号 |
而 n 大约为 2,000,000。
为了限制某个国家/地区的产品可用性,我有第二个ProductCountryRestriction表:
| 产品编号 | 国家代码 | 包含排除 |
|---|---|---|
| 1 | 美国 | 0 |
| 1 | 英国 | 0 |
| 2 | 澳大利亚 | 1 |
| 2 | 新西兰 | 1 |
如果产品仅限于一个或多个国家/地区,我会将包含产品的国家/地区添加到此表中,其中 IncludeExclude = 0。例如,产品 1 仅适用于美国和英国。
如果产品对除某些国家/地区以外的所有国家/地区都有效,那么我将使用 IncludeExclude = 1 将产品 - 排除的国家/地区添加到此表中。例如,产品 2 适用于除 AUS 和 NZ 之外的所有国家/地区。
与产品对应的所有国家/地区代码必须设置为 IncludeExclude = 0 或 IncludeExclude = 1。不允许混合使用 0 和 1。例如,对于产品 1,有 IncludeExclude = 0 的记录,我无法将 ProductID = 1 和 IncludeExclude = 1 的记录添加到此表中。
该系统的用户可以选择多个国家进行工作。用户首选项存储在UserCountry表中:
| 用户身份 | 国家代码 |
|---|---|
| 1 | 美国 |
| 1 | 新西兰 |
所以用户 1 可以看到产品 1,因为它在美国可用。他还可以看到产品 2,因为产品 2 也可用于美国(尽管它被排除在 NZ 之外)。这是我为用户获取所有可用产品的查询:
DECLARE @UserID int = 1;
SELECT P.*
FROM Product P
WHERE
EXISTS
(
SELECT * FROM ProductCountryRestriction PCR
WHERE
PCR.ProductId = P.ProductId
AND PCR.IncludeExclude = 0
AND PCR.CountryCode IN (SELECT CountryCode FROM UserCountry WHERE UserID = @UserID)
)
OR EXISTS
(
SELECT * FROM UserCountry UC
WHERE UserID = @UserID
AND UC.CountryCode NOT IN (SELECT CountryCode FROM ProductCountryRestriction PCR WHERE PCR.ProductId = P.ProductId AND PCR.IncludeExclude = 1)
)
Run Code Online (Sandbox Code Playgroud)
此查询按预期工作,但性能不佳。我能做些什么来改善它?我不介意改变数据库设计。感谢您阅读我的问题!如果您能提供任何帮助,我将不胜感激。
2020 年 12 月 31 日编辑 - 添加了@JD 建议的执行计划 请查看此计划的链接:https : //www.brentozar.com/pastetheplan/?id=BywbFaqaw
既然写了你不会介意改变设计...改变设计。
而不是带有标志和隐式包含/排除的复杂逻辑,只需有一个将产品映射到国家/地区的表格。让我们简单地称之为productcountry。当且仅当存在产品的某个国家/地区的记录且productcountry该产品在该国家/地区可用时。
然后查询只使用一些JOINs 和 a WHERE。
SELECT DISTINCT
p.*
FROM product p
INNER JOIN productcountry pc
ON pc.productid = p.productid
INNER JOIN usercountry uc
ON uc.countrycode = pc.countrycode
WHERE uc.userid = @userid;
Run Code Online (Sandbox Code Playgroud)
为此,您应该在usercountry (userid, countrycode),productcountry (countrycode, productid)和上尝试索引product (id)。
它需要一个DISTINCT虽然,因为一种产品可以在用户使用的多个国家/地区提供。(我在这里默默地假设产品本身是不同的,即它们有一个键。)如果您产生更好的计划,如果您改为使用EXISTS相关子查询,您可以进行试验。
SELECT p.*
FROM product p
WHERE EXISTS (SELECT *
FROM productcountry pc
INNER JOIN usercountry uc
ON uc.countrycode = pc.countrycode
WHERE uc.userid = @userid
AND pc.productid = p.productid);
Run Code Online (Sandbox Code Playgroud)
在这里,您可以在usercountry (userid, countrycode)和上尝试索引productcountry (productid, countrycode)。
| 归档时间: |
|
| 查看次数: |
256 次 |
| 最近记录: |