我有一个SQL Server查询,其中包含一个连接1百万行(Acct)的大表和一个包含1万行(AcctTxns)的小表的内部连接,但是SQL Server生成了一个执行计划,基数估算错误。
我已经将问题简化为以下语句:
SELECT p.AcctNo, p.Balance + t.TotalAmt as New Balance
FROM Acct p JOIN AcctTxns t
ON p.AcctNo = t.AcctNo
嵌套循环运算符显示的“估计行数”为16.2588,而“实际行数”为10000。
我正在使用MS SQL Server 2016(13.0.1742.0)。
我尝试了许多修复方法,包括:
- 更新统计信息
- 使用临时表进行中间结果
- 关闭2014基数估计器
- 以多种不同的方式重写SQL语句(这导致了上述问题的核心)
但它们都无法解决这个问题。 嵌套循环的错误估计会级联产生tempDB溢出,从而影响性能。
有没有人遇到过类似的问题? 感谢任何帮助解决这个问题。 谢谢。
以下代码设置了此问题:
--- [a] 1 million row Numbers table
DROP TABLE IF EXISTS #Numbers;
CREATE TABLE #Numbers (Number int PRIMARY KEY);
INSERT INTO #Numbers (Number)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY (SELECT 1)) - 1
FROM sys.objects A CROSS JOIN sys.objects B
--- [b] Create Acct table and populate with 1 million accounts
DROP TABLE IF EXISTS dbo.Acct;
CREATE TABLE dbo.Acct (
PkID int not null IDENTITY(1,1),
AcctNo varchar(48) not null PRIMARY KEY,
Balance decimal(20,10) not null constraint DF_Balance default(0)
)
INSERT INTO dbo.Acct (AcctNo)
SELECT RIGHT( (REPLICATE('0',6) + CAST(number as varchar(6))), 6)
FROM #Numbers
ORDER BY Number
--- [c] Insert 10K transactions. Each Acct gets 2 txns
DROP TABLE IF EXISTS dbo.AcctTxns;
CREATE TABLE dbo.AcctTxns
(
PkID int not null IDENTITY(1,1),
AcctNo varchar(48) not null,
TxnID nvarchar(50) not null,
Amt decimal(20,10) not null,
TxnStatus nvarchar(10) not null,
LastBalance decimal(20,10) null
PRIMARY KEY (AcctNo, TxnID, TxnStatus)
)
DROP TABLE IF EXISTS #Acct_Inserted_3XB9F;
CREATE TABLE #Acct_Inserted_3XB9F
(
AcctNo varchar(48) not null PRIMARY KEY,
Balance decimal(20,10) null
)
declare @TxnCount int = 10000
; WITH Txns (RowNo, TxnID) AS (
SELECT Number, '#T9-' + RIGHT(REPLICATE('0',8) + CAST(Number as varchar(8)), 8)
FROM #Numbers WHERE Number BETWEEN 1 AND @TxnCount/2
UNION
SELECT Number, '#T9-' + RIGHT(REPLICATE('0',8) + CAST(Number as varchar(8)), 8)
FROM #Numbers WHERE Number BETWEEN @TxnCount/2+1 AND @TxnCount
)
INSERT INTO dbo.AcctTxns (AcctNo, TxnID, Amt, TxnStatus)
SELECT A.AcctNo, T.TxnID, 100, 'COMM'
FROM dbo.Acct A JOIN Txns T ON A.PkID = T.RowNo
--- [d] Update statistics
UPDATE STATISTICS dbo.Acct;
UPDATE STATISTICS dbo.AcctTxns;
--- [e] PROBLEM HERE ...
SET STATISTICS IO, XML ON;
SELECT TxnCount=COUNT(1)
FROM dbo.Acct A INNER JOIN dbo.AcctTxns T
ON A.AcctNo = T.AcctNo
SET STATISTICS IO, XML OFF;