Dlaczego otrzymuję niejawną konwersję Int / Smallint na Varchar i czy to naprawdę wpływa na oszacowania liczności?

11

Próbuję rozwiązać problem z użyciem wolno działającego zapytania przy użyciu narzędzia Show Plan Analysis (SSMS) w rzeczywistym planie wykonania. Narzędzie analizy wskazuje, że szacunki dotyczące liczby wierszy są wyłączone z wyników zwróconych w kilku miejscach w planie, a ponadto daje mi pewne niejawne ostrzeżenia dotyczące konwersji.

Nie rozumiem tych niejawnych konwersji int na Varchar. Odwołane pola nie są częścią żadnego parametru / filtra w zapytaniu i we wszystkich zaangażowanych tabelach typy danych kolumn są takie same:

Otrzymuję poniższe ostrzeżenia dotyczące kardynalności:

Konwersja typu w wyrażeniu (CONVERT_IMPLICIT (varchar (12), [ccd]. [Profileid], 0)) może wpływać na „CardinalityEstimate” w wyborze planu zapytań - To pole jest liczbą całkowitą w całym DB

Konwersja typu w wyrażeniu (CONVERT_IMPLICIT (varchar (6), [ccd]. [Nodeid], 0)) może wpływać na „CardinalityEstimate” w wyborze planu zapytań - To pole jest małą literą wszędzie w mojej bazie danych

Konwersja typu w wyrażeniu (CONVERT_IMPLICIT (varchar (6), [ccd]. [Sessionseqnum], 0)) może wpływać na „CardinalityEstimate” w wyborze planu zapytań - To pole jest małe w każdym miejscu w mojej bazie danych

Konwersja typu w wyrażeniu (CONVERT_IMPLICIT (varchar (41), [ccd]. [Sessionid], 0)) może wpływać na „CardinalityEstimate” w wyborze planu zapytań - To pole jest dziesiętne wszędzie w mojej DB

[EDYCJA] Oto zapytanie i aktualny plan wykonania w celach informacyjnych https://www.brentozar.com/pastetheplan/?id=SysYt0NzN

I definicje tabel ..

/****** 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
źródło

Odpowiedzi:

11

Niejawne konwersje są powodowane przez kolumnę obliczeniową 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,
);

Biorąc pod uwagę powyższą uproszczoną tabelę, poniższe proste oświadczenie generuje takie same niejawne ostrzeżenia o konwersji podane w pytaniu:

SELECT T.*
FROM dbo.Test AS T;

Planuj z ostrzeżeniami

Z dokumentacji (wyróżnienie dodane):

CONCAT niejawnie konwertuje wszystkie argumenty na typy łańcuchowe przed konkatenacją.

Ostrzeżenie jest dodawane, gdy SQL Server rozważa alternatywny plan, który nie korzysta z utrwalonej wartości, ale oblicza wartość jawnie. Ostrzeżenie nie jest usuwane, jeśli ostateczny plan wykorzystuje utrwaloną wartość.

W takim przypadku ostrzeżenia mogą być bezpiecznie zignorowane. Odnosi się to również do twojego planu wykonania, o ile mogę powiedzieć - ukryte konwersje zaangażowane w CONCATnie wpływają negatywnie na wybór planu.

Użycie nieudokumentowanej i nieobsługiwanej flagi śledzenia 176 zapobiega utrwaleniu rozwiniętej kolumny obliczeniowej i usuwa ostrzeżenia:

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

z tf 176

Aby uzyskać więcej informacji, zobacz mój artykuł Właściwie utrwalone kolumny obliczeniowe .

Paul White 9
źródło
5

Są to pola, o których otrzymujesz niejawne ostrzeżenia o konwersji:

  • [ccd].[profileid] (int do varchar (12))
  • [ccd].[nodeid] (smallint do varchar (6))
  • [ccd].[sessionseqnum] (smallint do varchar (6))
  • [ccd].[sessionid] (dziesiętnie na varchar (41))

Powiązane pola nie są częścią żadnego parametru / filtra w zapytaniu

Pewnie, że są w waszych warunkach przyłączenia. Oto, gdzie ccd.profileid jest używany jako filtr (a także w sprzężeniu z 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

i we wszystkich zaangażowanych tabelach typy danych kolumn są takie same

Możesz dwukrotnie sprawdzić definicje tabel

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

Wygląda na to, że nie używają typów danych, o których myślisz, że używają.

i czy to naprawdę wpływa na Szacunki Kardynalności?

W mojej odpowiedzi trwają domysły oparte na podanych przez ciebie informacjach. Zachęcam do dodania rzeczywistego planu wykonania i definicji tabel do pytania, abyśmy mieli wszystkie szczegóły związane z tymi ukrytymi problemami z konwersją.

Zasadniczo niejawna konwersja warunków łączenia może powodować poważne problemy z oszacowaniami. Trudno powiedzieć, czy dzieje się tak w twoim przypadku, nie widząc faktycznego planu wykonania.

Josh Darnell
źródło