Ähnlich wie bei einer verwandten Frage von Swasheck habe ich eine Abfrage, die in der Vergangenheit unter Leistungsproblemen gelitten hat. Ich habe den Abfrageplan auf SSMS durchgesehen und eine Nested Loops (Inner Join)
Warnung festgestellt :
Kein Join-Prädikat
Basierend auf einigen hastigen Nachforschungen (vertrauensbildendes Scary DBA und Brent Ozar ) sieht es so aus, als würde diese Warnung mir sagen, dass ich ein verstecktes kartesisches Produkt in meiner Anfrage habe. Ich habe meine Abfrage einige Male überprüft und sehe keine Kreuzverbindung. Hier ist die Abfrage:
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
Der Abfrageplan finden Sie unter: https://gist.github.com/anonymous/d6ac970b45eb75a88b99
Oder sollte ich mich einfach nicht vor der Warnung fürchten, wie es die Schlussfolgerung in Swashecks Frage war , nachdem die geschätzten Kosten für den Teilbaum mit 0,05 recht niedrig sind?
Diese Antwort scheint ebenfalls relevant zu sein, was impliziert, dass dies wahrscheinlich eine Optimierung ist, die SQL Server in meinem Namen vornimmt, da er weiß, dass ich einen Join löschen kann.
Dieser Blog-Beitrag schlägt vor, dass die verschachtelten Schleifen kein Prädikatproblem durch eine UDF in einer Join-Spalte verursachen könnten. Ich verweise in dieser Abfrage nicht auf UDFs.
Hier ist die Definition der CurrentConnections
Ansicht:
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]);