У меня есть запрос SQL Server с INNER JOIN между большой таблицей, содержащей 1 миллион строк (Acct), и меньшей таблицей, содержащей 10000 строк (AcctTxns), но SQL Server создает план выполнения с неверными оценками количества элементов .
Я свел проблему к следующему утверждению:
SELECT p.AcctNo, p.Balance + t.TotalAmt as New Balance
FROM Acct p JOIN AcctTxns t
ON p.AcctNo = t.AcctNo
The Оператор вложенных циклов показывает «Расчетное количество строк» 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;