Проблемы производительности из-за неправильной оценки мощности в операторе вложенных циклов SQL Server - PullRequest
0 голосов
/ 20 сентября 2018

У меня есть запрос 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

Image - Nested Loops operator with wrong Estimated Number of Rows

Full Execution Plan

The Оператор вложенных циклов показывает «Расчетное количество строк» ​​16,2588 против «Фактического количества строк» ​​10000.

Я использую MS SQL Server 2016 (13.0.1742.0).

Я пробовал несколько исправлений, в том числе:

  1. обновление статистики
  2. с использованием временных таблиц для промежуточных результатов
  3. отключение оценки кардинальности 2014 года
  4. переписывает оператор 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;

1 Ответ

0 голосов
/ 20 сентября 2018

Похоже, вам не хватает некластеризованных индексов в двух таблицах и столбцах, к которым вы присоединяетесь.

CREATE NONCLUSTERED INDEX NC_AcctNo on Acct(AcctNo) INCLUDE (Balance);
CREATE NONCLUSTERED INDEX NC_AcctNo on Acctxns(AcctNo) INCLUDE (TotalAmt);

В вашем запросе должны быть более точные оценки, но если вы не фильтруетеданные из обеих таблиц с предложением WHERE, тогда вы получите сканирование индекса вместо сканирования кластерного индекса, что несколько лучше с точки зрения производительности, если вы используете вышеупомянутые индексы.

Но они все еще могут занимать некоторое время и ресурсы, в зависимости от количества возвращаемых строк.

Дополнительно вы можете посмотреть на этот ответ от Пола.Уайт об операторных оценках и других ответах на вопрос.

...