Bry*_*bok 12 performance sql-server optimization query-performance
当第一个表中的连接列定义为 NOT NULL 并且对第二个表中的相应列具有受信任的外键约束时,SQL Server 查询优化器似乎不会将 OUTER JOIN 转换为 INNER JOIN。
在这种情况下,似乎可以将 OUTER JOIN 转换为等效的 INNER JOIN,因为第一个表中的每一行是:
例如,请考虑以下表格:
CREATE TABLE dbo.tbl_fk
(
fk_val CHAR(1) NOT NULL PRIMARY KEY CLUSTERED,
junk VARCHAR(100) NOT NULL
);
CREATE TABLE dbo.tbl_main
(
id INT NOT NULL PRIMARY KEY CLUSTERED IDENTITY(1,1),
fk_val CHAR(1) NOT NULL FOREIGN KEY REFERENCES dbo.tbl_fk(fk_val)
);
Run Code Online (Sandbox Code Playgroud)
在以下查询中,为什么优化器没有将执行计划中的 LEFT OUTER JOIN 转换为 INNER JOIN?
SELECT m.fk_val, f.junk
FROM dbo.tbl_main AS m
LEFT OUTER JOIN dbo.tbl_fk AS f
ON m.fk_val = f.fk_val;
Run Code Online (Sandbox Code Playgroud)
但是,通过添加谓词以从第二个表中显式删除 NULL 值,优化器将查询计划中的 LEFT OUTER JOIN 转换为 INNER JOIN,如预期的那样:
SELECT m.fk_val, f.junk
FROM dbo.tbl_main AS m
LEFT OUTER JOIN dbo.tbl_fk AS f
ON m.fk_val = f.fk_val
WHERE f.fk_val IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)
未记录的跟踪标志显示应用规则将查询中的 OUTER JOIN 更改为 INNER JOIN,其中谓词:
SELECT m.fk_val, f.junk
FROM dbo.tbl_main AS m
LEFT OUTER JOIN dbo.tbl_fk AS f
ON m.fk_val = f.fk_val
WHERE f.fk_val IS NOT NULL
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8621);
***** Rule applied: A LOJ B -> A JN B
Run Code Online (Sandbox Code Playgroud)
这些示例使用 SQL Server 2014 和 2019 进行了测试,使用了许多不同的兼容性级别,并且还使用了旧的和“新的”基数估计器。在所有情况下,行为似乎都相同。
Forrest McDaniel 指出(截至 2010 年),Sybase 似乎在其产品中内置了这样的转换(参见示例 1):http : //dcx.sybase.com/1200/en/dbusage/queryopt-sectb-5356466。 html
我不确定查询转换没有发生的“充分理由”,除了它尚未实现。
您可以在查询中看到潜在的好处,例如
SELECT m.fk_val, count(f.junk)
FROM dbo.tbl_main AS m
LEFT OUTER JOIN dbo.tbl_fk AS f
ON m.fk_val = f.fk_val
group by m.fk_val
Run Code Online (Sandbox Code Playgroud)
如果是 INNER JOIN,则可以在加入之前推送聚合。
SELECT m.fk_val, count(f.junk)
FROM dbo.tbl_main AS m
INNER JOIN dbo.tbl_fk AS f
ON m.fk_val = f.fk_val
group by m.fk_val
Run Code Online (Sandbox Code Playgroud)
但在外列转换OUTER JOIN + Predicate的情况下,外列谓词要么完全消除,要么可以推送到join之前。
EG,与
SELECT m.fk_val, f.junk
FROM dbo.tbl_main AS m
LEFT OUTER JOIN dbo.tbl_fk AS f
ON m.fk_val = f.fk_val
WHERE f.fk_val IS NOT NULL
Run Code Online (Sandbox Code Playgroud)
此查询在逻辑上等同于更简单的查询
SELECT m.fk_val, f.junk
FROM dbo.tbl_main AS m
INNER JOIN dbo.tbl_fk AS f
ON m.fk_val = f.fk_val
Run Code Online (Sandbox Code Playgroud)
它少了一个谓词。
如果您尝试在连接之前推送外部表谓词而不将其更改为 INNER JOIN,您仍然必须在连接之后应用该谓词。
对于类似的东西
SELECT m.fk_val, m.Id
FROM dbo.tbl_main AS m
LEFT OUTER JOIN dbo.tbl_fk AS f
ON m.fk_val = f.fk_val
WHERE f.junk = 'a';
Run Code Online (Sandbox Code Playgroud)
如果f.junk = 'a'在连接之前应用谓词,则会在 JOIN 之前过滤掉一些行。但是随后与外部行对应的内部行将在外部f.junk <> 'a'联接中幸存下来,并且您必须在联接f.junk = 'a'后再次申请。
| 归档时间: |
|
| 查看次数: |
307 次 |
| 最近记录: |