2010-06-20 14 views
6

To jest pytanie dotyczące wydajności SQL.Zapytania SQL - czy jest lepszy sposób?

Jakiś czas temu musiałem napisać zbiór zapytań do pobierania danych z systemu ERP. Większość z nich była dość prosta, ale jedna z nich zaowocowała dość nieefektywnym zapytaniem i od tego czasu mnie dręczyła, ponieważ musi być lepszy sposób.

Problem nie jest skomplikowany. Masz wiersze danych sprzedaży. W każdym wierszu znajduje się między innymi ilość, cena sprzedaży i kod sprzedawcy.

Prowizja wypłacana jest w oparciu o stopniowaną skalę ruchomą. Im więcej sprzedają, tym lepsza prowizja. Kroki mogą wynosić 1000, 10000, 10000 $ i tak dalej. Problem prawdziwego świata jest bardziej złożony, ale to w istocie.

Jedynym sposobem znalazłem w ten sposób było zrobić coś takiego (oczywiście nie prawdziwe query)

select qty, price, salesman, 
    (select top 1 percentage from comissions 
    where comisiones.salesman = saleslines.salesman 
    and saleslines.qty > comisiones.qty 
    order by comissiones.qty desc 
) percentage 
from saleslines 

skutkuje prawidłowym komisji, ale jest straszliwie ciężkie.

Czy jest lepszy sposób to zrobić? Nie szukam kogoś, kto przerobiłby mój sql, bardziej "spójrz jak zapytania foobar" i mogę to zrobić.

Rzeczywistą strukturę prowizji można określić dla różnych sprzedawców, artykułów i klientów, a nawet dat sprzedaży. Zmienia się również od czasu do czasu, więc wszystko musi być napędzane danymi w tabelach ... tj. Nie mogę ustawić stałych zakresów w sql. Obecne zapytanie zwraca około 3-400000 wierszy i zajmuje około 20-30 sekund. Na szczęście jest to używane tylko raz w miesiącu, ale powolność trochę mnie dręczy.

To jest na mssql.

Ian

edit:

powinny dałem bardziej złożony przykład od początku. Zdaję sobie teraz sprawę, że w moim pierwszym przykładzie brakuje kilku istotnych elementów złożoności, przepraszam wszystkich.

To może lepiej uchwycić go

select client-code, product, product-family, qty, price, discount, salesman, 
    (select top 1 percentage from comissions 
     where comisiones.salesman = saleslines.salesman 
     and saleslines.qty > comisiones.qty 
     and [ 
      a collection of conditions which may or may not apply: 
      Exclude rows if the salesman has offered discounts above max discounts 
       which appear in each row in the commissions table 
      There may be a special scale for the product family 
      There may be a special scale for the product 
      There may be a special scale for the client 

      A few more cases 
      ] 
     order by [ 
      The user can control the order though a table 
      which can prioritize by client, family or product 
      It normally goes from most to least specific. 
      ] 
    ) percentage 
    from saleslines 

trzeba dodawać prawdziwy zapytanie nie jest łatwe do naśladowania. Aby uczynić życie bardziej interesującym, jego nazewnictwo jest wielojęzykowe.

Tak więc dla każdego wiersza sprzedaży prowizja może być różna.

Może wydawać się to zbyt skomplikowane, ale jeśli myślisz o tym, jak zapłacić prowizję, ma to sens. Nie chcesz płacić komuś za sprzedawanie rzeczy przy wysokich rabatach, ale chcesz też zaoferować konkretnemu klientowi zniżkę na konkretny produkt, jeśli kupisz jednostki X. Sprzedawca powinien zarabiać więcej, jeśli sprzedają więcej.

We wszystkich powyższych przypadkach wykluczam specjalne oferty z ograniczoną datą.

Myślę, że partycje mogą być rozwiązaniem, ale muszę zbadać to bardziej dogłębnie, ponieważ nie wiem nic o partycjach. Dał mi kilka pomysłów.

+0

jeśli jej tylko uruchomić miesięcznie, nie będę się o to martwić. Wygląda na to, że musi wykonać wiele obliczeń na kilku dużych stołach. Jeśli musiał być uruchamiany częściej, możesz stopniowo budować zestaw wyników przy użyciu innej strategii. –

+0

Czasami lepiej jest zmierzyć pracę i wprowadzić lepszą infrastrukturę, np. Indeksy lub szybsze dyski itp. Powinieneś profilować zapytanie i widzieć, że bity są ciężkie. –

+0

Jakiej wersji programu SQL Server używasz? –

Odpowiedz

3

Jeśli używasz wersji programu SQL Server, który obsługuje wyrażenia wspólnego stołu, takie jak SQL Server 2005 i nowszych, bardziej efektywnym rozwiązaniem może być:

With RankedCommissions As 
    (
    Select SL.qty, SL.price, SL.salesman, C.percentage 
     , Row_Number() Over (Partition By SL.salesman Order By C.Qty Desc) As CommissionRank 
    From SalesLines As SL 
     Join Commissions As C 
      On SL.salesman = C.salesman 
       And SL.qty > C.qty 
    ) 
Select qtr, price, salesman, percentage 
From RankedCommissions 
Where CommissionRank = 1 

Jeśli potrzebne, aby uwzględnić możliwość, że nie istnieją żadne wartości prowizji dla danego sprzedawcy, gdzie SalesLine.Qty> Commission.Qty, to mógłby zrobić coś takiego:

With RankedCommissions As 
    (
    Select SL.qty, SL.price, SL.salesman, C.percentage 
     , Row_Number() Over (Partition By SL.salesman Order By C.Qty Desc) As CommissionRank 
    From SalesLines As SL 
     Join Commissions As C 
      On SL.salesman = C.salesman 
       And SL.qty > C.qty 
    ) 
Select SL.qtr, SL.price, SL.salesman, RC.percentage 
From SalesLines As SL 
    Left Join RankedCommissions As RC 
     On RC.salesman = SL.salesman 
      And RC.CommissionRank = 1 
+0

Nie potrzebujesz *, aby mieć obsługę wyrażeń wspólnych tabel, aby używać tego podejścia. Równie dobrze można zapisać CTE jako widok śródliniowy. Funkcja 'ROW_NUMBER' wymaga jednak SQL Server 2005 lub nowszego. –

+0

Moje pierwotne zapytanie zostało napisane dla sql2000, ale db został niedawno poddany migracji do sql2008, co sprowokowało moje zainteresowanie skorzystaniem z nowych funkcji. Thomas, to wygląda na rozwiązanie. Jest to jednak bardzo odmienne podejście, więc będę musiał przez jakiś czas usiąść z tym i zobaczyć, jak mogę z niego korzystać. Podobnie jak rozwiązanie. Dzięki -Ian – Ian

+0

@Ian - Zrozum. CTE i funkcje rankingowe, takie jak Row_Number, są niewiarygodnie potężną nową funkcją w sezonie 2005/2008. Pomagają uprościć szereg problematycznych zapytań, takich jak te, które przedstawiłeś. Prawdziwą sztuczką w moim rozwiązaniu jest użycie klauzuli Partition By, która zamawia wartości przez sprzedawcę (sprzedawca?). – Thomas

0
select 
    qty, price, salesman, 
    max(percentage) 
from saleslines 
    inner join comissions on commisions.salesman = saleslines.salesman and 
      saleslines.qty > comissions.qty 
group by 
    qty, price, salesman 
+0

-1, twój kod daje najwyższy procent; oryginalne zapytanie podaje procent najwyższej liczby. – Gabe

+0

INNER JOIN ma warunek (saleslines.qty> comissions.qty), który odfiltrowuje wszystkie wiersze zlecenia z ilością większą niż ilość kwitu sprzedaży. Maks. W pozostałych wierszach podaje procent największej ilości (nie najwyższy procent). – potatopeelings

+0

lub tak myślę :-) – potatopeelings

Powiązane problemy