Hilfe bei der Suche nach Join ohne Prädikat


7

Ä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 CurrentConnectionsAnsicht:

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]);

Antworten:


4

In meinem CTE fehlte mir ein WITH (NOEXPAND)Abfragehinweis. Nachdem ich diesen Abfragehinweis hinzugefügt habe, ist der Join ohne Prädikat aus meinem Abfrageplan verschwunden.

;WITH AggregateStepData_CTE AS
(
    SELECT
        [UA].[UserId]
        , [UA].[DeviceId]
        , SUM(ISNULL([UA].[LatestSteps], 0)) AS [Steps]
    FROM [User].[UserStatus] [UA]
        INNER JOIN [User].[CurrentConnections] [M] WITH (NOEXPAND) -- Added query hint here
          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]
)

1

Ich hatte ähnliche Situationen und habe vor einiger Zeit gelesen, dass die Engine bestimmt, ob der Typ des Join-Algorithmus am effizientesten ist, und manchmal die Join-Prädikate im Namen der Effizienz über den geschriebenen Code fallen lässt. Wenn Sie nicht erweitern, weisen Sie die Engine an, nicht in die Ansicht abzusteigen und bei Ihrem Abfrageblock der obersten Ebene zu bleiben.

Lassen Sie sich nicht davon abbringen, wenn weitere Vorschläge gemacht werden, um zu beweisen, dass Sie Ihre Join-Prädikate haben. Glauben Sie mir auch, ich habe 100 Mal nachgesehen und ich hatte Recht. Lol. Also mein Beitrag.

Versuchen Sie, den Join im Tabellenreferenzstapel nach oben und unten zu verschieben, und stellen Sie sicher, dass die Aliasreferenzen in der richtigen Abhängigkeitsreihenfolge vorliegen. Das heißt, wenn Sie 4 Joins haben, habe ich Erfolg gehabt und die Dinge geändert, indem ich einen weniger referenzierten oder wenig selektiven Tabellen-Join an die letzte Position der Joins verschoben habe und umgekehrt.

Stellen Sie sicher, dass Statistiken und Indizes auf dem neuesten Stand sind. Da ich mit dem automatischen Zeitplan und Algorithmus "Statistiken automatisch erstellen / aktualisieren", den MS ausführen soll, nicht vertraut bin, werde ich gelegentlich die folgenden Anweisungen ausgeben, um sicherzustellen, dass sie in letzter Zeit verarbeitet werden.

Sp_UpdateStats Sp_CreateStats 'nur Index'

Forcing-Hinweise sollten immer getestet und dokumentiert werden, aber ich wollte warnen und informieren, was mir eingefallen ist und was meine Untersuchung ergeben hat.

Der folgende Link ist ein Thread, der sich auf dasselbe bezieht ... Überprüfen Sie im Eigenschaftendialog auf "Zeitüberschreitung bei ausreichendem Plan" und andere Warnungen.

SQL Central Ähnlicher Thread

Ich bin interessiert, ob irgendwelche Ideen Ihr Problem lösen, wie es für mich getan hat.


Die Reihenfolge der Verknüpfungen war das Juwel im Meer der Ratschläge, die Sie gegeben haben. Nichts davon hilft wirklich dabei, mein Problem zu beleuchten.
Erik
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.