Muito parecido com uma pergunta relacionada por swasheck , tenho uma consulta que historicamente sofreu com problemas de desempenho. Eu estava olhando o plano de consulta no SSMS e notei um Nested Loops (Inner Join)
com o aviso:
Nenhum Predicado de Junção
Com base em algumas pesquisas apressadas (confiança inspirando Scary DBA e Brent Ozar ), parece que este aviso está me dizendo que tenho um produto cartesiano oculto em minha consulta. Verifiquei minha consulta algumas vezes e não vejo a junção cruzada. Aqui está a consulta:
DECLARE @UserId INT; -- Stored procedure input
DECLARE @Now DATETIME2(7) = SYSUTCDATETIME();
;WITH AggregateStepData_CTE AS -- Considering converting this CTE into an indexed view
(
SELECT
[UA].[UserId] -- FK to the UserId
, [UA].[DeviceId] -- FK to the push device's DeviceId (int)
, SUM(ISNULL([UA].[LatestSteps], 0)) AS [Steps]
FROM [User].[UserStatus] [UA]
INNER JOIN [User].[CurrentConnections] [M] ON
[M].[Monitored] = [UA].[UserId] AND [M].[Monitor] = @UserId
WHERE
[M].[ShareSteps] = 1 -- Only use step data if we are allowed to see.
AND
CAST([UA].[ReportedLocalTime] AS DATE) =
CAST(DATEADD(MINUTE, DATEPART(TZOFFSET, [UA].[ReportedLocalTime]), @Now) AS DATE)
-- Aggregate the steps for today based on the device's time zone.
GROUP BY
[UA].[UserId]
, [UA].[DeviceId]
)
SELECT
[UA].[UserId] -- FK to the UserId
, [UA].[ReportedLocalTime]
, CASE WHEN [M].[ShareLocation] = 1 THEN [UA].[Latitude] ELSE NULL END AS [Latitude]
, CASE WHEN [M].[ShareLocation] = 1 THEN [UA].[Longitude] ELSE NULL END AS [Longitude]
, CASE WHEN [M].[ShareLocation] = 1 THEN [UA].[LocationAccuracy] ELSE NULL END
AS [LocationAccuracy]
, CASE WHEN [M].[ShareSteps] = 1 THEN ISNULL([SD].[Steps], 0) ELSE NULL END AS [Steps]
, CASE WHEN [M].[ShareBattery] = 1 THEN [UA].[BatteryPercentage] ELSE NULL END
AS [BatteryPercentage]
, CASE WHEN [M].[ShareBattery] = 1 THEN [UA].[IsDraining] ELSE NULL END
AS [IsDraining]
, [PD].[DeviceName]
FROM [User].[LatestUserStatus] [UA]
INNER JOIN [User].[CurrentConnections] [M] WITH (NOEXPAND) ON
[M].[Monitored] = [UA].[UserId] AND [M].[Monitor] = @UserId
INNER JOIN [User].[PushDevice] [PD] ON [PD].[PushDeviceId] = [UA].[DeviceId]
LEFT JOIN [AggregateStepData_CTE] [SD] ON
[M].[Monitored] = [SD].[UserId] AND [SD].[DeviceId] = [UA].[DeviceId]
ORDER BY
[UA].[UserId]
, [UA].[ReportedLocalTime] DESC
O plano de consulta pode ser encontrado em: https://gist.github.com/anonymous/d6ac970b45eb75a88b99
Ou devo simplesmente não ter medo do aviso, pois foi a conclusão da pergunta de swasheck , afinal o custo estimado da sub-árvore é bastante baixo em 0,05?
Essa resposta também parece relevante, o que implica que provavelmente é uma otimização que o SQL Server está fazendo em meu nome, porque sabe que posso descartar uma junção.
Esta postagem de blog sugere que os loops aninhados sem problema de predicado podem ser causados por um UDF em uma coluna de junção. Não estou fazendo referência a nenhum UDF nesta consulta.
Aqui está a definição da CurrentConnections
visão:
CREATE VIEW [User].[CurrentConnections]
WITH SCHEMABINDING
AS
SELECT
[M].[Monitor] -- FK to the UserId
, [M].[Monitored] -- FK to the UserId
, [M].[MonitoringId]
, [M].[ShareBattery]
, [M].[ShareLocation]
, [M].[ShareSteps]
, [M].[ShowInSocialFeed]
, [M].[Created] AS [RelationshipCreated]
, [AT].[AlertThresholdId]
, [AT].[EffectiveStartTime]
, [AT].[EndTime]
, [AT].[OverNightRedThreshold]
, [AT].[SendBatteryAlerts]
, [AT].[SendGeneralAlerts]
, [AT].[StartTime]
, [AT].[ThresholdInMinutes]
, [AT].[Threshold]
, [U_Monitored].[ProfilePhoto] AS [Monitored_ProfilePhoto]
, [U_Monitored].[DisplayName] AS [Monitored_DisplayName]
, [U_Monitored].[Fullname] AS [Monitored_FullName]
, [U_Monitored].[PhoneNumber] AS [Monitored_PhoneNumber]
FROM [User].[Monitoring] [M]
INNER JOIN [User].[AlertThreshold] [AT] ON [AT].[MonitoringId] = [M].[MonitoringId]
INNER JOIN [User].[User] [U_Monitored] ON [U_Monitored].[UserId] = [M].[Monitored]
WHERE
[M].[ArchivedOn] IS NULL
AND
[AT].[ArchivedOn] IS NULL
GO
CREATE UNIQUE CLUSTERED INDEX [IDX_User_CurrentConnections_Monitor_Monitored] ON
[User].[CurrentConnections]([Monitor], [Monitored]);
GO
CREATE NONCLUSTERED INDEX [IDX_User_CurrentConnections_Monitored] ON
[User].[CurrentConnections]([Monitored])
INCLUDE ([Monitor], [ShareBattery], [ShareLocation], [ShareSteps]);
No meu CTE estava faltando uma
WITH (NOEXPAND)
dica de consulta. Depois de adicionar essa dica de consulta, a junção sem predicado desapareceu do meu plano de consulta.Eu tive situações semelhantes e li há algum tempo que o mecanismo determinará se o tipo de algoritmo de junção é mais eficiente e, às vezes, descartará os predicados de junção em nome da eficiência do código escrito. Ao não expandir, você está dizendo ao mecanismo para não descer para a exibição e ficar com o bloco de consulta da camada superior.
Não desanime se mais sugestões forem feitas para provar que você tem seus predicados de junção. Acredite em mim também, verifiquei centenas de vezes e eles estavam corretos. Lol. Assim minha contribuição.
Tente mover o Join para cima e para baixo na pilha de referência da tabela, certificando-se de que as referências de alias estejam na ordem de dependência correta. Ou seja, se você tiver 4 junções, tive sucesso e mudei as coisas, movendo uma junção de tabela menos referenciada ou seletiva para a última posição das junções e vice-versa.
Certifique-se de que as estatísticas e os índices estejam atualizados. Como não estou confiante com a programação automática e o algoritmo "Criar/atualizar estatísticas automaticamente" que o MS deve fazer, ocasionalmente emitirei as seguintes instruções para garantir que sejam processadas recentemente.
Sp_UpdateStats Sp_CreateStats 'indexonly'
Insinuações forçadas sempre devem ser testadas e documentadas, mas eu queria alertar e informar o que me ocorreu e o que minha investigação revelou.
O link a seguir é um tópico relacionado à mesma coisa ... Verifique se há "tempo limite para encontrar um plano bom o suficiente" e outros avisos no diálogo de propriedades.
Tópico similar do SQL Central
Estou interessado se alguma ideia resolver seu problema, como aconteceu comigo.