2017-12-04 2 views
1

Ich habe diese Abfrage, die in einem SSRS-Bericht verwendet wird, den jemand anders erstellt hat. Der linke Join ist die Ursache des Problems. Wenn ich es zu einem inneren Join ändere, erhalte ich Ergebnisse (nicht die korrekten Ergebnisse) in ungefähr 15 Sekunden. Mit dem Linksbündnis beende ich die Abfrage nach 20 Minuten. Ich habe einen Index zu Budgets.Professionals und Transactions.Professionals ohne Leistungsänderung hinzugefügt. Gibt es eine Möglichkeit, die Abfrage neu zu schreiben und nicht die linke Verknüpfung zu verwenden?Welche Alternative habe ich zu einer Left Join in dieser Abfrage?

SELECT 
profs.ProfName as orig 
,profs.Initials 
,DATEPART(year, TransDate) as [Year] 
,SUM(CASE WHEN IsFlatFee = 'Y' OR COALESCE(MT.Admin, 'N') = 'Y' 
    THEN 0.0 
    ELSE Units * (aph.assignedpercent/100) * isnull(B.rate, 0.0) 
    END) AS ctp 
,SUM(CASE WHEN IsFlatFee = 'Y' OR COALESCE(MT.Admin, 'N') = 'Y' 
    THEN 0 
    ELSE Units 
    END * (aph.assignedpercent/100)) AS worked_hours 
,SUM(Value * (aph.assignedpercent/100)) AS worked_value 
, 0 AS billed_hours 
,0 AS billed_value 
,0 AS billed_netamt 
, 0.0 as paid 
, 0.0 as wo 
FROM Transactions Trans 
INNER JOIN Matters Matts ON Trans.matters = Matts.matters 
INNER JOIN MatterTypes MT ON Matts.mattertype = MT.mattertypesdesc 
and MT.Admin <> 'Y' 
INNER JOIN Components Comps ON Comps.components = Trans.components 
and Comps.CompType = 'F' 
INNER JOIN AssignedProfsHistory APH on APH.Matters = Trans.Matters 
and APH.AssignedType = 'Originating' 
and Trans.TransDate between APH.EffectiveDate and 
ISNULL(EndDate,'12/31/2099') 
INNER JOIN Professionals profs on profs.Professionals = APH.Professionals 
    and profs.ProfType = 'Member' 
    and profs.IsActive = 'Y' 
    and profs.IsBillable = 'Y' 
**LEFT join** (SELECT Budgets.Professionals as timekeeper, Budgets.Amount as 
rate, Budgets.PeriodDate 
FROM Matters Matts 
INNER JOIN Budgets ON Matts.matters = Budgets.matters 
    and cast(Budgets.PeriodDate as Date) <= '2017-12-31' 
    AND MONTH('2017-12-31') = MONTH(Budgets.PeriodDate) 
WHERE Matts.MatterID = '99999-99.003') as B 
    *on B.timekeeper = Trans.Professionals* 
    and YEAR(B.PeriodDate) = DATEPART(year, TransDate) 
WHERE cast(transdate as DATE) between dateadd(day, 1, DATEADD(year, -3, 
'2017-12-31')) and '2017-12-31' 
GROUP BY profs.ProfName, profs.Initials, DATEPART(year, TransDate) 
+2

Es gibt kaum eine Chance, dass jemand mit so wenig Informationen helfen kann. Wir müssten Tabellendefinitionen einschließlich Indizes zusammen mit ungefähren Zeilenzahlen für alle beteiligten Tabellen sehen. Außerdem würde ein Ausführungsplan helfen, aber da Sie die Abfrage nicht abschließen können, ist das möglicherweise nicht möglich. –

+1

Es gibt einige sichtbare Kuriositäten, wie zum Beispiel, warum Sie als date 'cast (Budgets.PeriodDate als Date) 'casten, und dann eine Datumsfunktion auf demselben Feld ohne einen' MONTH (Budgets.PeriodDate) '- Cast ausführen. Sie sollten die Funktionen in where/join immer sorgfältig ausführen, da sie die Verwendung von Indizes ungültig machen können. Sonst, was Sean gesagt hat. –

+1

Meine Vermutung wäre, dass es keine Indizes für die LEFT JOINed-Tabelle verwendet.Vermutlich haben Sie einen Index auf Budgets.PeriodDate, aber bevor Sie versuchen, die Spalte (und den zugehörigen Index) zu verwenden, machen Sie eine CAST darauf. An diesem Punkt hat der Optimierer keine Ahnung mehr, dass der neue Wert der gleiche wie der Index ist, weil es nicht sein könnte. Wir müssen auf jeden Fall Ihre Tabellenstruktur, Indizes und die geschätzten Ausführungspläne sehen (da, wie Sean sagte, Sie nicht die tatsächlichen haben werden). – indiri

Antwort

1

Als Sean und Aaron sagte. Es gibt zu viele Dinge, die potentiell ein Problem darstellen.

Sie scheinen (ich rate von Spaltennamen), dass Sie beitreten Text Spalten mattertypesdesc für eins. In der Tat ist die meiste Arbeit gegen Textspalten getan. Sogar Matts.MatterID ist textuell. Dies ist in Ihrem Szenario möglicherweise nicht möglich, würde aber besser funktionieren, wenn die Tabellen ganzzahlige Primärschlüssel haben und Sie diesen beitreten.

Wie auch immer, Raten beiseite .... Sie können einen schnellen Gewinn erhalten, wenn Sie Ihre Unterabfrage im linken Join durch eine temporäre Tabelle ersetzen.

so, bevor Sie Abfrage nur tun bestehende ...

SELECT Budgets.Professionals as timekeeper, Budgets.Amount as rate, Budgets.PeriodDate 
INTO #t 
FROM Matters Matts 
INNER JOIN Budgets ON Matts.matters = Budgets.matters 
    and cast(Budgets.PeriodDate as Date) <= '2017-12-31' 
    AND MONTH('2017-12-31') = MONTH(Budgets.PeriodDate) 
WHERE Matts.MatterID = '99999-99.003' 

dann in Ihrer exisintg Abfrage, ersetzen Sie die Unterabfrage mit

SELECT ... 
... 
... 
LEFT JOIN #t as B 
    ON B.timekeeper = Trans.Professionals 
    .... 
1

Sie auch mit dem APPLY-Operator versuchen können ... Entfernen Links Join & ist es unter der Bedingung, verwenden Sie äußere gelten und schließen Sie auf Bedingungen in der äußeren Anwendung Skript wie

Probe

OUTER APPLY 
     ( 
        SELECT  budgets.professionals AS timekeeper, 
          budgets.amount  AS rate, 
          budgets.perioddate 
        FROM  matters matts 
        INNER JOIN budgets 
        ON   matts.matters = budgets.matters 
        AND  cast(budgets.perioddate AS date) <= '2017-12-31' 
        AND  month('2017-12-31') = month(budgets.perioddate) 
        AND budgets.timekeeper = trans.professionals 
        AND year(budgets.perioddate) = datepart(year, transdate) 
        WHERE  matts.matterid = '99999-99.003' 

       ) AS b 
0

Vielen Dank allen, die geantwortet haben. Ich nahm Ihre Vorschläge und ich war in der Lage, eine Lösung zu finden. Die Abfrage, die ich nach einer Laufzeit von 2 Stunden beenden musste, endet nun in etwa 14 Sekunden.

Ich habe am Anfang des Skripts eine CTE erstellt.

;with cte as 
(SELECT Transactions FROM Transactions t 
WHERE cast(t.TransDate as DATE) between dateadd(day, 1, DATEADD(year, -3, 
@EndDate)) and @EndDate) 

Dann habe ich den CTE mit Transaktionen verbunden.

FROM Transactions Trans 
INNER JOIN cte ON cte.Transactions = Trans.Transactions 

Ich war dann in der Lage, die Where-Klausel zu entfernen, die das Problem verursacht hat.

WHERE cast(transdate as DATE) between dateadd(day, 1, DATEADD(year, -3, 
@EndDate)) and @EndDate