Почему я получаю неявное преобразование Int / Smallint в Varchar, и это действительно влияет на оценки кардинальности?

11

Я пытаюсь решить проблему с медленным выполнением запроса, используя Show Plan Analysis (SSMS) для фактического плана выполнения. Инструмент «Анализ» указывает, что оценки количества строк не совпадают с возвращенными результатами в нескольких местах плана, а также дает некоторые неявные предупреждения о преобразовании.

Я не понимаю этих неявных преобразований типа int в Varchar. Указанные поля не являются частью какого-либо параметра / фильтра в запросе, и во всех задействованных таблицах типы данных столбцов одинаковы:

Я получаю следующие предупреждения CardinalityEstimate:

Преобразование типов в выражении (CONVERT_IMPLICIT (varchar (12), [ccd]. [Profileid], 0)) может повлиять на «CardinalityEstimate» при выборе плана запроса. Это поле является целым числом везде в моей БД

Преобразование типов в выражении (CONVERT_IMPLICIT (varchar (6), [ccd]. [Nodeid], 0)) может повлиять на «CardinalityEstimate» при выборе плана запроса. Это поле является маленьким шрифтом везде в моей БД

Преобразование типов в выражении (CONVERT_IMPLICIT (varchar (6), [ccd]. [Sessionseqnum], 0)) может повлиять на «CardinalityEstimate» при выборе плана запроса. Это поле является маленьким шрифтом везде в моей БД

Преобразование типов в выражении (CONVERT_IMPLICIT (varchar (41), [ccd]. [Sessionid], 0)) может повлиять на «CardinalityEstimate» при выборе плана запроса. Это поле является десятичным везде в моей БД

[РЕДАКТИРОВАТЬ] Вот запрос и фактический план выполнения для ссылки https://www.brentozar.com/pastetheplan/?id=SysYt0NzN

И определения таблиц ..

/****** Object:  Table [dbo].[agentconnectiondetail]    Script Date: 1/10/2019 9:10:04 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[agentconnectiondetail](
    [sessionid] [decimal](18, 0) NOT NULL,
    [sessionseqnum] [smallint] NOT NULL,
    [nodeid] [smallint] NOT NULL,
    [profileid] [int] NOT NULL,
    [resourceid] [int] NOT NULL,
    [startdatetime] [datetime2](7) NOT NULL,
    [enddatetime] [datetime2](7) NOT NULL,
    [qindex] [smallint] NOT NULL,
    [gmtoffset] [smallint] NOT NULL,
    [ringtime] [smallint] NULL,
    [talktime] [smallint] NULL,
    [holdtime] [smallint] NULL,
    [worktime] [smallint] NULL,
    [callwrapupdata] [varchar](40) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [callresult] [smallint] NULL,
    [dialinglistid] [int] NULL,
    [convertedStartDatetimelocal] [datetime2](7) NULL,
    [convertedEndDatetimelocal] [datetime2](7) NULL,
 CONSTRAINT [PK_agentconnectiondetail] PRIMARY KEY CLUSTERED 
(
    [sessionid] ASC,
    [sessionseqnum] ASC,
    [nodeid] ASC,
    [profileid] ASC,
    [resourceid] ASC,
    [startdatetime] ASC,
    [qindex] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[contactcalldetail]    Script Date: 1/10/2019 9:10:04 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[contactcalldetail](
    [sessionid] [decimal](18, 0) NOT NULL,
    [sessionseqnum] [smallint] NOT NULL,
    [nodeid] [smallint] NOT NULL,
    [profileid] [int] NOT NULL,
    [contacttype] [smallint] NOT NULL,
    [contactTypeDescription] [varchar](20) COLLATE Latin1_General_CI_AS NULL,
    [contactdisposition] [smallint] NOT NULL,
    [contactdispositionDescription] [varchar](20) COLLATE Latin1_General_CI_AS NULL,
    [dispositionreason] [varchar](100) COLLATE Latin1_General_CI_AS NULL,
    [originatortype] [smallint] NOT NULL,
    [originatorTypeDescription] [varchar](20) COLLATE Latin1_General_CI_AS NULL,
    [originatorid] [int] NULL,
    [originatordn] [varchar](30) COLLATE Latin1_General_CI_AS NULL,
    [destinationtype] [smallint] NULL,
    [destinationTypeDescription] [varchar](20) COLLATE Latin1_General_CI_AS NULL,
    [destinationid] [int] NULL,
    [destinationdn] [varchar](30) COLLATE Latin1_General_CI_AS NULL,
    [startdatetimeUTC] [datetime2](7) NOT NULL,
    [enddatetimeUTC] [datetime2](7) NOT NULL,
    [gmtoffset] [smallint] NOT NULL,
    [callednumber] [varchar](30) COLLATE Latin1_General_CI_AS NULL,
    [origcallednumber] [varchar](30) COLLATE Latin1_General_CI_AS NULL,
    [applicationtaskid] [decimal](18, 0) NULL,
    [applicationid] [int] NULL,
    [applicationname] [varchar](30) COLLATE Latin1_General_CI_AS NULL,
    [connecttime] [smallint] NULL,
    [customvariable1] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable2] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable3] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable4] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable5] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable6] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable7] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable8] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable9] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [customvariable10] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [accountnumber] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [callerentereddigits] [varchar](40) COLLATE Latin1_General_CI_AS NULL,
    [badcalltag] [char](1) COLLATE Latin1_General_CI_AS NULL,
    [transfer] [bit] NULL,
    [NextSeqNum] [smallint] NULL,
    [redirect] [bit] NULL,
    [conference] [bit] NULL,
    [flowout] [bit] NULL,
    [metservicelevel] [bit] NULL,
    [campaignid] [int] NULL,
    [origprotocolcallref] [varchar](32) COLLATE Latin1_General_CI_AS NULL,
    [destprotocolcallref] [varchar](32) COLLATE Latin1_General_CI_AS NULL,
    [convertedStartDatetimelocal] [datetime2](7) NULL,
    [convertedEndDatetimelocal] [datetime2](7) NULL,
    [AltKey]  AS (concat([sessionid],[sessionseqnum],[nodeid],[profileid]) collate database_default) PERSISTED NOT NULL,
    [PrvSeqNum] [smallint] NULL,
 CONSTRAINT [PK_contactcalldetail] PRIMARY KEY CLUSTERED 
(
    [sessionid] ASC,
    [sessionseqnum] ASC,
    [nodeid] ASC,
    [profileid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[contactqueuedetail]    Script Date: 1/10/2019 9:10:04 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[contactqueuedetail](
    [sessionid] [decimal](18, 0) NOT NULL,
    [sessionseqnum] [smallint] NOT NULL,
    [profileid] [int] NOT NULL,
    [nodeid] [smallint] NOT NULL,
    [targetid] [int] NOT NULL,
    [targettype] [smallint] NOT NULL,
    [targetTypeDescription] [varchar](10) COLLATE Latin1_General_CI_AS NULL,
    [qindex] [smallint] NOT NULL,
    [queueorder] [smallint] NOT NULL,
    [disposition] [smallint] NULL,
    [dispositionDescription] [varchar](50) COLLATE Latin1_General_CI_AS NULL,
    [metservicelevel] [bit] NULL,
    [queuetime] [smallint] NULL,
 CONSTRAINT [PK_contactqueuedetail] PRIMARY KEY CLUSTERED 
(
    [sessionid] ASC,
    [sessionseqnum] ASC,
    [profileid] ASC,
    [nodeid] ASC,
    [targetid] ASC,
    [targettype] ASC,
    [qindex] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Index [<Name of Missing Index, sysname,>]    Script Date: 1/10/2019 9:10:04 AM ******/
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>] ON [dbo].[contactcalldetail]
(
    [convertedStartDatetimelocal] ASC
)
INCLUDE (   [sessionid],
    [sessionseqnum],
    [nodeid],
    [profileid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
/****** Object:  Index [idx_CCD_ContactType_DestType_StDtLocal]    Script Date: 1/10/2019 9:10:04 AM ******/
CREATE NONCLUSTERED INDEX [idx_CCD_ContactType_DestType_StDtLocal] ON [dbo].[contactcalldetail]
(
    [destinationtype] ASC,
    [contacttype] ASC,
    [convertedStartDatetimelocal] ASC
)
INCLUDE (   [sessionid],
    [sessionseqnum],
    [nodeid],
    [profileid],
    [convertedEndDatetimelocal]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [idx_CQD_Profile_Traget_TargetType]    Script Date: 1/10/2019 9:10:04 AM ******/
CREATE NONCLUSTERED INDEX [idx_CQD_Profile_Traget_TargetType] ON [dbo].[contactqueuedetail]
(
    [profileid] ASC,
    [targetid] ASC,
    [targettype] ASC
)
INCLUDE (   [targetTypeDescription],
    [queueorder],
    [disposition],
    [dispositionDescription],
    [queuetime]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Voysinmyhead
источник

Ответы:

11

Неявные преобразования вызваны вычисляемым столбцом AltKey:

CREATE TABLE dbo.Test
(
    [sessionid] [decimal](18, 0) NOT NULL,
    [sessionseqnum] [smallint] NOT NULL,
    [nodeid] [smallint] NOT NULL,
    [profileid] [int] NOT NULL,
    [AltKey] AS 
        CONCAT
        (
            [sessionid],
            [sessionseqnum],
            [nodeid],
            [profileid]
        ) PERSISTED NOT NULL,
);

Учитывая приведенную выше упрощенную таблицу, простое утверждение ниже генерирует те же неявные предупреждения преобразования, которые приведены в вопросе:

SELECT T.*
FROM dbo.Test AS T;

План с предупреждениями

Из документации (выделение добавлено):

CONCAT неявно преобразует все аргументы в строковые типы перед объединением.

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

В этом случае предупреждения могут быть безопасно проигнорированы. Насколько я могу судить, это также относится к вашему плану выполнения - неявные преобразования, которые участвуют в проекте CONCAT, не оказывают негативного влияния на выбор плана.

Использование недокументированного и неподдерживаемого флага трассировки 176 предотвращает расширение сохраняемого вычисляемого столбца и удаляет предупреждения:

SELECT * 
FROM dbo.Test AS T
OPTION (QUERYTRACEON 176);

с тф 176

См. Мою статью « Правильно сохраненные вычисляемые столбцы» для более подробной информации.

Пол Уайт 9
источник
5

Вот поля, о которых вы получаете неявные предупреждения о преобразовании:

  • [ccd].[profileid] (int varchar (12))
  • [ccd].[nodeid] (smallint to varchar (6))
  • [ccd].[sessionseqnum] (smallint to varchar (6))
  • [ccd].[sessionid] (десятичное в varchar (41))

Указанные поля не являются частью какого-либо параметра / фильтра в запросе.

Конечно, в ваших условиях присоединения. Вот где ccd.profileid используется в качестве фильтра (а также в соединении с agentconnectiondetail):

FROM contactcalldetail ccd 
    INNER JOIN contactqueuedetail csqd 
        ON ccd.sessionID=csqd.sessionid 
            AND ccd.sessionSeqNum=csqd.sessionSeqNum 
            AND ccd.nodeID=csqd.nodeID 
            AND ccd.profileid=csqd.profileid -- Right here

и во всех участвующих таблицах типы данных столбца одинаковы

Вы можете дважды проверить определения таблиц для

  • contactcalldetail.profileid
  • contactqueuedetail.profileid
  • agentconnectiondetail .profileid

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

и действительно ли это влияет на оценки кардинальности?

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

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

Джош Дарнелл
источник