我有一个SQL Server查询,其中一个INNER JOIN在一个包含一百万行(Acct)的大表和一个包含10,000行(AcctTxns)的小表之间,但是SQL Server生成的执行计划具有基数估计错误< / strong>。
我将问题归结为以下陈述:
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)。
我已经尝试了许多修复程序,包括:
但是他们不能解决问题。对嵌套循环级联的错误估计会产生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;
答案 0 :(得分:0)
似乎您在要连接的两个列上都缺少两个非聚集索引。
CREATE NONCLUSTERED INDEX NC_AcctNo on Acct(AcctNo) INCLUDE (Balance);
CREATE NONCLUSTERED INDEX NC_AcctNo on Acctxns(AcctNo) INCLUDE (TotalAmt);
您应该对查询有更好的估计,但是如果您不使用WHERE
子句过滤来自两个表的数据,则将获得索引扫描,而不是聚簇索引扫描,后者会更好如果使用上述指标,则在性能方面。
但是它们仍然会占用一些时间和资源,具体取决于您需要返回的行数。
另外,您可以查看Paul White的this answer,了解运营商估算以及该问题的其他答案。