Verwenden von Summen für Aggregate zur Verbesserung der Leistung

7

Ich habe zwei Tabellen: Details und Summen dieser Details.

Details ( langsame Lösung ):

select 
    OrderId             =   r.OrderId                   
  , TotalQty            =   SUM(r.Quantity)
  , TotalGrossConsid    =   SUM(r.Price * r.Quantity)
from dbo.Order r
group by r.OrderId

Summen ( schnelle Lösung ):

select 
    t.OrderId                           
  , t.TotalQty          
  , t.TotalGrossConsid  
  , t.IsValid
from dbo.OrderTotal t

Manchmal werden Summen ungültig (einige Jobs müssen geänderte Summen neu berechnen, aber es verzögert sich). Wie Sie verstehen, ist die zweite Abfrage schneller und die Anzahl der gültigen Summen ist höher als die der ungültigen. Daher suche ich nach einer kombinierten Abfrage, die gültige Summen aus der zweiten Tabelle (Summen) zurückgibt und mithilfe der ersten langsamen Abfrage dynamisch neu berechnete Summen zurückgibt. Damit ist mein Ziel erreicht: Alle Summen sind gültig und die Reaktionszeit ist schneller als die vollständige Neuberechnung.

Hier ist mein Versuch ( Hybridlösung ):

with fast_static(OrderId, TotalQty, TotalGrossConsid, IsValid)
as
(
    select 
        t.OrderId                           
      , t.TotalQty          
      , t.TotalGrossConsid  
      , t.IsValid
    from dbo.OrderTotal t
)

, slow_dynamic(OrderId, TotalQty, TotalGrossConsid)
(
   select 
        OrderId             =   r.OrderId                   
      , TotalQty            =   SUM(r.Quantity)
      , TotalGrossConsid    =   SUM(r.Price * r.Quantity)
    from dbo.Order r
)
select
    OrderId, TotalQty, TotalGrossConsid
from fast_static 
where IsValid = 1  
union all
select
  OrderId, TotalQty, TotalGrossConsid 
from slow_dynamic s 
    --inner join fast_static ff
    --on ff.OrderId = s.OrderId 
where   --ff.Valid = 0 -- too slow!!!
    s.OrderId in (select OrderId from fast_static f where f.Valid = 0)

Ich habe die Fast-Lösung mit der Hybrid-Lösung verglichen und 32% bis 68% (relative Abfragekosten) erhalten. Wenn Sie eine kommentierte Variante sehen können, entspricht dies 1% bis 99% (schade). Ist es möglich, diese Abfrage zu verbessern?

HINZUGEFÜGT

@gbn:

Valid  =  case when i.OrderId is null then 1 else 0 end
...
dbo.OrderTotal t  left join dbo.InvalidOrders i

Ja, ich habe einen Job zum Neuberechnen von Summen und dieser Prozess ist nicht mit Abfrageanforderungen synchronisiert. InvalidOrders-Tabellen sind kleine Tabellen, in denen Datensätze gespeichert werden, um zu wissen, dass Summen ungültig sind (neu zu berechnen).

LÖSUNG

Indizierte Ansichten sind die beste Wahl. Beachten Sie die SQL Server-Edition ( Noexpand-Hinweis für Nicht-Enterprise-Editionen) und erstellen Sie einige Datenbankobjekte ( SET ANSI_NULLS ON, SET QUOTED_IDENTIFIER ON) neu, um indizierte Ansichten auf der Clientseite zu verwenden.

garik
quelle
Sie haben also einen Job, der Summen vorberechnet? Was sagt das IsValid-Flag? Wie viele Zeilen haben Sie, haben Sie Indizes, wie lauten die Tabellenschemata?
Gbn
Ist OrderTotal also eine Ansicht mit einem LEFT JOIN? Oder ist dies der Code, mit dem Sie INSERT / UPDATE für OrderTotal generieren? Welche Abozut-Schemata + Indizes gibt es auch? Verstecke keine Informationen ...
gbn
@gbn Ich habe meiner Frage einige Kommentare hinzugefügt. Bestellungen, OrderTotals, InvalidOrders: Einige Codes ändern die Bestelldetails und legen den Datensatz in der InvalidOrder ab. Einige Jobs berechnen Summen basierend auf InvalidTotals neu und entfernen Datensätze aus dem InvalidOrder. Einige Code-Abfragen summieren sich. Hier sind Prozesse wie sie sind.
Garik
@garik: Sie sollten InvalidOrder nicht benötigen: Dies ist ein Hack für schlechte Indizierung oder schlechtes Design.
Gbn
@gbn - Bestellungen können vermutlich eine Reihe von haben details. Dies
scheint

Antworten:

5

Ich kann nicht verstehen, warum man nicht im laufenden Betrieb aggregieren kann und warum es so langsam ist. Ist die "gültige" Idee eine Problemumgehung, um die Verzögerung von OrderTotals oder eines Geschäftsprozesses zu bewältigen?

Beide Ideen verwerfen die InvalidOrder-Tabelle, die eine Problemumgehung für eine schlechte Indizierung darstellt.

  • Vorschlag 1:

Erstellen Sie eine berechnete Spalte

ALTER TABLE dbo.Order ADD PriceXQuantity AS Price * Quantity PERSISTED

Fügen Sie einen Index hinzu

CREATE INDEX IX_Totals ON dbo.Order (OrderID) INCLUDE Quantity, PriceXQuantity)

Schau was passiert

  • Vorschlag 2:

Verwenden Sie eine indizierte Ansicht

CREATE VIEW OrderTotals
WITH SCHEMABINDING
AS
select 
    OrderId             =   r.OrderId                   
  , TotalQty            =   SUM(r.Quantity)
  , TotalGrossConsid    =   SUM(r.Price * r.Quantity)
  , COUNT_BIG(*) AS Dummy
from dbo.Order r
group by r.OrderId
GO
CREATE UNIQUE CLUSTERED INDEX IXCU_OrderTotals ON OrderTotals (OrderId9
GO

Sie können die berechnete Spalte auch hier verwenden

gbn
quelle
1) Aufgrund einiger clientseitiger Code-Anforderungen werden ALLE Summen angefordert. 2) Wegen zu vieler Datensätze pro Sekunde werden Einfügungen in die Detailtabelle vorgenommen. Denken Sie immer noch, dass die indizierte Ansicht (oder das neu berechnete Feld) nicht zu langsam ist (Neuindizierung, Neuerstellung der Ansicht oder etwas anderes)? Ich denke, das ist eine große Frage. Überzeuge mich vom Gegenteil.
Garik
@garik: Ich habe 40.000 Zeilen pro Sekunde INSERTs. Ich habe Aggregate auch in Echtzeit auf derselben Tabelle.
Gbn
Sie haben Recht! Indizierte Ansicht ist etwas tolles. Vielen Dank für das Beispiel aus dem wirklichen Leben (... 40.000). Erledigt.
Garik
2
@gbn - das ist eine ziemlich coole Funktion. Ist die indizierte Ansicht immer mit den Basistabellen synchron oder wird sie beim Festschreiben aktualisiert?
Jack sagt, versuchen Sie es mit topanswers.xyz
2
@garik: Ich neige dazu, es immer zu verwenden (sogar auf Enterprise): Ich habe festgestellt, dass SQL Server Frankyl manchmal ziemlich verrückt ist, wenn indizierte Ansichten verwendet werden. Sie können die indizierte Ansicht auch in eine andere Ansicht einbinden, die keine Erweiterung hat, da Hinweise "nach innen" propagieren: In Ihrer äußeren Ansicht ist dies nicht erforderlich
gbn
2

Wenn die Kosten nicht zu hoch wären, könnten Sie die IsValidFlagge in die DetailTabelle verschieben und indizieren? Dies würde Ihre Transaktionen verlangsamen, aber die Abfrageleistung verbessern, da auf die (vermutlich) große DetailTabelle in einer großen zugegriffen werden würderange scan

Jack sagt, versuchen Sie es mit topanswers.xyz
quelle