Indeksy przy pobieraniu danych w MSSQL

Indeksy przy pobieraniu danych w MSSQL
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około 5 godzin
0

Załóżmy, że mam tabelę:

Kopiuj
create table Samochody
(
    Zrodlo1 varchar(20) not null,
    Zrodlo2 varchar(20) not null,
    Marka varchar(100) not null,
    Kolor varchar(100) not null,
    MocSilnika varchar(100) not null,
    KosztZakupu numeric not null,
    WartoscSprzedazy numeric not null
)

Do tabeli są wprowadzane dane z różnych źródeł (źródło identyfikowane za pomocą Zrodlo1 i Zrodlo2) i przewiduje się ewentualną aktualizację danych w razie potrzeby. Aktualizacja polega na usunięciu danych mających konkretną wartość pól Zrodlo1 i Zrodlo2, a potem wstawienie na nowo danych. Oczywiście możliwe jest dopisanie danych z innego źródła, jak też rezygnacja z danych z określonego źródła. Jednakże, zestawy wartości pól Marka, Kolor, MocSilnika nie powtarzają się, ale wartość jednego z tych pól może się powtarzać.

Aby zapewnić wydajność przy kontroli, czy są dane z danego źródła i usuwaniu danych z konkrentego źródła, w tabeli jest założony indeks klastrowany na polach Zrodlo1 i Zrodlo2.

Natomiast korzystanie z danych polega na wywołaniu zapytania np.:

Kopiuj
select sum(KosztZakupu), sum(WartoscSprzedazy), Marka, Kolor, MocSilnika from Samochody group by Marka, Kolor, MocSilnika
select sum(KosztZakupu), sum(WartoscSprzedazy), Marka, Kolor from Samochody group by Marka, Kolor
select sum(KosztZakupu), sum(WartoscSprzedazy), Kolor, MocSilnika from Samochody group by Kolor, MocSilnika
select sum(KosztZakupu), sum(WartoscSprzedazy) from Samochody

W tym przypadku nie bierze się pod uwagę pól Zrodlo1 i Zrodlo2, a dopuszczalne są wszystkie możliwe grupowania pól Marka, Kolor, MocSilnika (czyli jest 8 możliwości), każde grupowanie z podobną częstotliwością zapotrzebowania. W jaki sposób najlepiej usprawnić pobieranie danych?
Mi do głowy przychodzą następujące działania:

  1. Założyć indeks nieklastrowany na każde z pól grupowania (Marka, Kolor, MocSilnika), w tym przypadku będa trzy indeksy.
  2. Założyć jeden indeks nieklastrowany na wszystkie pola grupowania (Marka, Kolor, MocSilnika). Czy kolejnosć pól w indeksie ma znaczenie?
  3. Założyć indeks nieklastrowany na każde możliwe grupowanie, w tym przypadku będzie to 7 indeksów (bo nie da się założyć indeksu bez podawania pól).
  4. Rozszerzyć indeks klastrowany na wszystkie pola, czyli indeks klastrowany obejmowałby pola (Zrodlo1, Zrodlo2, Marka, Kolor, MocSilnika). Będzie to mieć istotny wpływ na edycję danych w opisany wyżej sposób?

Pobór danych ma miejsce istotnie częściej niż ewentualna kontrola i aktualizacja danych.

edytowany 4x, ostatnio: andrzejlisek
woolfik
  • Rejestracja:ponad 17 lat
  • Ostatnio:około 4 godziny
  • Postów:1595
2

Ja bym zaczął od 1 unikalnego indeksu na Marka, Kolor i MocSilnika bo jak napisałeś one się nie powtarzają i sprawdził wydajność zapytań. Indeksy czasami można dodawać na zasadzie prób i błędów więc sprawdź sam, które rozwiązanie będzie dla Ciebie najkorzystniejsze

YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 6 godzin
  • Postów:2368
1
  1. Ile jest tych danych?
  2. Jaki % unikalności masz po Marka, Kolor, Moc silnika?
  3. Czy te agregacje to jedyne zapytania na tej tabeli? (Pomijając aktualizację po zrodlo1/zrodlo2)
UglyMan
  • Rejestracja:około 6 lat
  • Ostatnio:około 3 lata
  • Postów:2206
1

Zacznij od palny zapytania. Możesz też rzucić okiem na to: https://www.brentozar.com/archive/2015/06/indexing-for-group-by/ - pamiętaj, że kolejne indeksy będą psuły wydajność przy dodawaniu rekordów, Nie wiem, czy to jest ważne w twoim przypadku, ale należy zwrócić na to uwagę.

PA
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 10 godzin
  • Postów:3866
2

Aby zapewnić wydajność przy kontroli, czy są dane z danego źródła i usuwaniu danych z konkrentego źródła, w tabeli jest założony indeks klastrowany na polach Zrodlo1 i Zrodlo2.

Dla mnie nie ma to sensu, zakladanie pogrupowanego indeksu tylko w celu przyspieszenia usuwania (zakładam, że to operacja wykonywana raz na jakiś czas i sama wydajność nie wydaje się kluczowa)
gdzie i tak odpytujesz wszystkie dane bez względu na źródło.

Samo konstrukcja trzymania tego w osobnych 2 polach jakaś pokraczna, czemu nie w jednym? Jak dojdzie 3 źródło to dodasz kolumne zrodlo3

AK
Zgadzam się z "pokracznością", a i z pierwszym zdaniem też.
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około 5 godzin
0
yarel napisał(a):
  1. Ile jest tych danych?
  1. Jaki % unikalności masz po Marka, Kolor, Moc silnika?
  2. Czy te agregacje to jedyne zapytania na tej tabeli? (Pomijając aktualizację po zrodlo1/zrodlo2)
  1. Od kilku milionów do ok. 20 milionów pozycji
  2. W docelowym rozwiązaniu takich kolumn do grupowania ma być ok. 10, nie wiem, jak rozumieć "% unikalności", dla jednej konkrentnej kombinacji wartości cech od kilku do kilkudziesięciu pozycji.
  3. Zapytania pobierające dane to wymienione agregacje (obejmujące wszystkie możliwe grupowania pól cech) oraz select Zrodlo1, Zrodlo2 from Samochody group by Zrodlo1, Zrodlo2 w celu uzyskania informacji, z jakich źródeł są wprowadzone dane.
Panczo napisał(a):

Aby zapewnić wydajność przy kontroli, czy są dane z danego źródła i usuwaniu danych z konkrentego źródła, w tabeli jest założony indeks klastrowany na polach Zrodlo1 i Zrodlo2.

Dla mnie nie ma to sensu, zakladanie pogrupowanego indeksu tylko w celu przyspieszenia usuwania (zakładam, że to operacja wykonywana raz na jakiś czas i sama wydajność nie wydaje się kluczowa)
gdzie i tak odpytujesz wszystkie dane bez względu na źródło.

Samo konstrukcja trzymania tego w osobnych 2 polach jakaś pokraczna, czemu nie w jednym? Jak dojdzie 3 źródło to dodasz kolumne zrodlo3

Jedno źródło danych to nie jedna wartość, tylko zestaw wartości. W tej koncepcji sa to dwie wartości, jak zajdzie potrzeba, że mają być trzy, to dojdzie pole Zrodlo3. Jednakże nie wykluczam możliwości przerobienia na jedno pole, w którym wartość będzie jednoznacznie identyfikować źródło danych, np. wartość jest tekstem Wartosc1 + '|' + Wartosc2, gdzie w samej wartości znak | nie ma prawa wystąpić, a i tak klient bazy danych generuje listę wartości. Najpierw następuje select Zrodlo1, Zrodlo2 from Samochody group by Zrodlo1, Zrodlo2, odpowiedź jest porównywana z listą z innego miejsca (to porównanie robi klient bazy danych) i jeżeli jest jakaś wartość, która jest w odpowiedzi, a nie ma na liście, to jest robiony delete from Samochody where Zrodlo1 = 'Wartosc1' and Zrodlo2 = 'Wartosc2'. A jak jest odwrotnie, czyli jakaś para wartości jest na liście kontrolnej, a nie ma w tabeli, to jest robiony insert into Samochody (Zrodlo1, Zrodlo2, Marka, Model, PojemnoscSilnika, KwotaZakupu, KwotaSprzedazy) select 'Wartosc1', 'Wartosc2', BardzoSkomplikowaneZapytanieSelect, gdzie wprowadzane dane są tak naprawdę odpowiedzią na bardziej skomplikowany select. Kontrola źródeł z listą następuje równie często, co pobór agregacji, a ewentualne zmiany na liście kontrolnej, które pociągają odpowiednie modyfikacje tabeli następują rzadko.

AK
brniesz w "pokraczność" jeszcze dalej.
AK
za komuny był taki dowcip, opowiadało się specjalnym głosem, niby że przemówienie Gomółki "20 lat temu staliśmy na skraju przepaści. Dzięki bratniej pomocy Związku Radzieckiego zrobiliśmy znaczący krok do przodu".
PA
  • Rejestracja:ponad 22 lata
  • Ostatnio:około 10 godzin
  • Postów:3866
2

Jedno źródło danych to nie jedna wartość, tylko zestaw wartości.

To bardziej by pasowała konstrukcja: tabela ze źródłami, z dowolnymi atrybutami, bezproblemowa do rozbudowy/zmiany, ID źródła w tabeli z danymi.

AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około 5 godzin
1
Panczo napisał(a):

Jedno źródło danych to nie jedna wartość, tylko zestaw wartości.

To bardziej by pasowała konstrukcja: tabela ze źródłami, z dowolnymi atrybutami, bezproblemowa do rozbudowy/zmiany, ID źródła w tabeli z danymi.

Według pierwszej lub drugiej postaci normalnej tak nawet być powinno, testujemy system i nie wykluczone, że coś takiego będzie.

Marcin Badtke
Abstrahując od postaci normalnych utrzymanie jednej kolumny 'zrodla' + tabela ze źródłami jest znacznie łatwiejsze. Podobnie jak rozbudowa np. o kolejne źródła. Oraz SQLe dotyczących jedynie jednego źródła.
AK
  • Rejestracja:ponad 6 lat
  • Ostatnio:10 dni
  • Postów:3561
0
andrzejlisek napisał(a):
Panczo napisał(a):

Jedno źródło danych to nie jedna wartość, tylko zestaw wartości.

To bardziej by pasowała konstrukcja: tabela ze źródłami, z dowolnymi atrybutami, bezproblemowa do rozbudowy/zmiany, ID źródła w tabeli z danymi.

Według pierwszej lub drugiej postaci normalnej tak nawet być powinno, testujemy system i nie wykluczone, że coś takiego będzie.

W inżynierii pewne rzeczy się WIE a nie "testuje". Inżynieria jest teorią i praktyką osiągania celów, a nie miotaniem z kąta w kąt. Myśl uprzedza realizację, a nie an odwrót.

nie to że jestem religijnym zwolennikiem 3ciej postaci, lubię 2 1/2, ale pierwsza postać normalna nie jest i nigdy nie była żeby ja implementować, tylko jako etap analizy


Bo C to najlepszy język, każdy uczeń ci to powie
edytowany 1x, ostatnio: AnyKtokolwiek
Marcin Badtke
  • Rejestracja:ponad 4 lata
  • Ostatnio:prawie 4 lata
  • Postów:21
0

Jak komentowano wcześniej - utrzymanie 2 kolumn 'zrodlo' zamiast jednej + tabela ze zrodlami nie jest najlepszym pomysłem. A już w ogóle "big no no" sklejanie wartości w jednej kolumnie 'zrodlo'. To proszenie się o kłopoty w utrzymaniu. Niemniej jest jak jest :-) Masz na nich indeks klastrowy co oznacza, że dane w tabeli posortowane są wg kolumn zrodlo1, zrodlo2. Dla wymienionych zapytań agregujących ten indeks klastrowy jest nieprzydatny. W zaprezentowanych zapytaniach nie ma klauzuli 'where', więc zawsze czytana jest cała tabela. Grupowanie nigdy nie jest po kolumnach 'zrodlo1/2', więc posortowanie danych w tabeli wg zrodel nie jest przydatne dla zapytań. Zakładanie kolejnych indeksów nie ma sensu ponieważ nie zmienią sortu na tabeli, a zawsze trzeba odczytać wszystkie dane bo nie ma 'where'.

Moje sugestie do rozważenia/stestowania:
#1 zmienić indeks klastrowy na zbudowany na kolumnach 'KosztZakupu', 'WartoscSprzedazy'. Indeks klastrowy wartości kolumn przechowuje w liściach, więc zapytanie:
select sum(KosztZakupu), sum(WartoscSprzedazy) from Samochody
powinno działać szybciej - wykona skan indeksu zamiast skanu tabeli. Z dysku odczyta tylko bloki z indeksem.
Co do usuwania danych wg źródła to trzeba policzyć liczność krotek z każdego źródła. Jeśli jest 50/50 to spodziewam się, że optymalizator i tak przeskanuje całą tabelę. Jeśli jest znaczna dysproporcja np. 20/80 to wtedy indeks tylko na kolumnie źródła, które ma 20% krotek. Dla 80% i tak będzie skan tabeli.

Innymi słowy dla motoru bazy danych odczytanie 50% tabeli po indeksie jest mniej opłacalne niż skanowanie 100% tabeli. Korzystanie z indeksu to dla motoru 3 IO aby dostać dane. 2IO jeśli są w liściach indeksu. Skan tabeli to 1IO, który może być robiony w duuużych blokach dyskowych po wiele stron bazodanowych - bardziej wydajnie. Na dodatek skan tabeli nie zaśmieca cache bazy danych.

#2 zawsze sumujesz KosztZakupu i WartoscSprzedazy - przemyślałbym trzymanie danych wstępnie zagregowanych pod tym kątem aby za każdym razem nie sumować wszystkich krotek. Czyli zsumować dla każdej kombinacji marki, koloru i mocy silnika - bo jak piszesz to jest unikalne. A wynik końcowy obliczać sumując te wcześniejsze podsumy. Innymi słowy - nie musisz przechowywać każdego rekordu zakupu/sprzedaży audi. Nigdy nie oglądasz jakimi egzemplarzami audi handlowałeś. Wystarczy Ci suma zakupu/sprzedaży dla marki audi. Podobnie koloru i mocy silnika. Później te podsumy tylko dodajesz na różne sposoby.

Tytułem komentarza - schemat tabeli bardziej odpowiada systemowi OLTP, natomiast użycie to hurtownia zwracająca zagregowane dane finansowe. Wynika to również z Twojego opisu - wyciąganie danych znacznie częstsze niż modyfikacje. Doczytałbym na Twoim miejscu o kostkach OLAP i ETL - może się nada.


edytowany 4x, ostatnio: Marcin Badtke
AN
  • Rejestracja:prawie 19 lat
  • Ostatnio:około 5 godzin
1

Właśnie dokonałem następujących zmian:

  1. Jedno pole na źródło danych.
  2. Po polu "zrodlo" indeks nieklastrowany.
  3. Po pozostałych polach z danymi nieliczbowymi indeks klastrowany.

W międzyczasie stwierdziłem, że w indeksie nieklastrowanym maksymalna wielkość pozycji danych może wynieść 1700 bajtów. Przy zakładaniu indeksu pokazuje sie takie ostrzeżenie.

Co do OLAP, to doczytam, a przechowywanie podsumowanych danych jest i takt przewidziane.

Marcin Badtke
Gratuluję odwagi :-) Oby w tym jednym polu były 2 wartości ;-) Daj znać czy zmiany wyszły na +. I tak jak pisałem wcześniej - jeśli ilość danych ze źródeł jest 50/50 (póki są tylko 2 źródła) to indeks nie ma sensu. Optymalizator i tak wybierze skan tabeli. Możesz potwierdzić generując plany wykonania czy mam rację.
AN
Źródeł może być więcej (od ok. 10 do 100), identyfikowane grupą wartości, ale posłużenie się identyfikatorem grupy lub sklejonym tekstem na jedno wychodzi. Chodzi tylko o "group by zrodlo" i porównanie z zewnętrzną listą.
Marcin Badtke
W takim układzie zdecydowanie sugeruję osobną tabelę źródła z primary key na id źródła + foreign key na kolumnie źródła w tabeli samochody. Zabezpieczy Cię to, również przed 'nieautoryzowanymi' źródłami. Jak robić wydajne 'group by' opisał Marcus Winand gdzieś tutaj: https://use-the-index-luke.com/sql/sorting-grouping
AN
Póki co, problem jest rozwiązany, przyjęty sposób zakładania indeksu skrócił czas pobierania danych do akceptowalnego.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)