Szybkość SQL SELECT int vs varchar

110

Jestem w trakcie tworzenia stołu i zastanawiałem się.

Jeśli przechowuję, powiedzmy, samochody, które mają markę (np. BMW, Audi itp.), Czy zapiszę markę jako int lub varchar, wpłynie to na szybkość zapytania.

Więc jest

SELECT * FROM table WHERE make = 5 AND ...;

Szybciej / wolniej niż

SELECT * FROM table WHERE make = 'audi' AND ...;

czy prędkość będzie mniej więcej taka sama?

googletorp
źródło

Odpowiedzi:

99

Porównania int są szybsze niż porównania varchar z prostego faktu, że ints zajmują znacznie mniej miejsca niż varchar.

Dotyczy to zarówno dostępu niezindeksowanego, jak i indeksowanego. Najszybszym sposobem jest indeksowana kolumna typu int.


Jak widzę, oznaczyłeś pytanie postgreql, możesz być zainteresowany wykorzystaniem przestrzeni dla różnych typów dat:

Robert Munteanu
źródło
13
Masz na myśli str. 7.4. W nowoczesnych wersjach zajmują 1 bajt + długość, jeśli masz <126 bajtów. Należy również zauważyć, że powodem, dla którego ciągi znaków są znacznie wolniejsze, jest często to, że porównanie wrażliwe na sortowanie jest niezwykle kosztowne - nie dlatego, że ciąg zajmuje więcej miejsca. Ale efekt końcowy jest oczywiście taki sam.
Magnus Hagander
@Magnus - dzięki za ostrzeżenie. Zapraszam do edycji mojej odpowiedzi, ponieważ widzę, że masz wystarczająco dużo punktów rep.
Robert Munteanu
„nie żeby łańcuch zajmował więcej miejsca”… ciągi znaków powyżej minimalnych rozmiarów zajmują dużo więcej miejsca niż nawet bardzo precyzyjne liczby, ponieważ liczba (pojedyncza) ma stałą jednostkę, łańcuchy są zawsze typami zagregowanymi . 8 bajtów dla 64-bitowej liczby 4 bajty na znak w ciągu, w tym bajt długości lub struktura; lub inny znak terminatora dla niewiarygodnie naiwnych implementacji ...
MrMesees
@RobertMunteanu Hej Robert, przepraszam Wiem, że to stary post, ale czy mogę uprzejmie sprawdzić ... w następujący sposób: aby zapytać o liczby całkowite, muszę połączyć każdą kolumnę ciągu z inną tabelą (relacją). oznacza to jednak, że dla każdego zapytania potrzeba więcej operacji łączenia. Jak ustalić, czy ten kompromis jest tego wart? Dziękuję Ci!
AiRiFiEd
2
„Porównania int są szybsze niż porównania varchar, z prostego faktu, że ints zajmują znacznie mniej miejsca niż varchar” - generalnie NIE jest to prawdą . W zależności od używanego DBMS i dokładnych typów danych i ciągów znaków, które chcesz wstawić, może się okazać, że twoje (powiedzmy) 8-bajtowe wartości int są dłuższe niż varchary ascii, które zawierają niektóre identyfikatory tekstowe o średniej długości 3-4 znaków. Tak więc ta odpowiedź - nieprecyzyjna i pozbawiona określonego kontekstu lub wyników eksperymentów - tak naprawdę nie odpowiada na pytanie. Wszyscy wiedzą, że varchary mogą zajmować znacznie więcej miejsca niż int, ale NIE muszą.
Marcin Wojnarski
36

Niektóre przybliżone testy porównawcze:

4 miliony rekordów w Postgres 9.x

Table A = base table with some columns
Table B = Table A + extra column id of type bigint with random numbers
Table C = Table A + extra column id of type text with random 16-char ASCII strings

Wyniki na laptopie 8 GB RAM, i7, SSD:

Size on disk:                A=261MB        B=292MB        C=322MB
Non-indexed by id: select count(*), select by id: 450ms same on all tables
Insert* one row per TX:       B=9ms/record        C=9ms/record
Bulk insert* in single TX:    B=140usec/record    C=180usec/record
Indexed by id, select by id:  B=about 200us       C=about 200us

* inserts to the table already containing 4M records

więc wygląda na to, że w tej konfiguracji, o ile twoje indeksy mieszczą się w pamięci RAM, tekst bigint vs 16-znakowy nie ma znaczenia w szybkości.

Grzegorz Luczywo
źródło
6
Bardzo interesujące. Dlaczego różnica jest znikoma?
Chibueze Opata
18

Będzie trochę szybciej używając int zamiast varchar. Ważniejsze dla szybkości jest posiadanie indeksu w polu, którego kwerenda może użyć do znalezienia rekordów.

Jest jeszcze jeden powód, dla którego warto używać int, a jest nim normalizacja bazy danych. Zamiast przechowywać tekst „Mercedes-Benz” tysiące razy w tabeli, należy przechowywać jego identyfikator i raz przechowywać nazwę marki w osobnej tabeli.

Guffa
źródło
Czy mógłbyś wyjaśnić więcej? Czy masz na myśli zamiast Mercedes-Benzprzechowywać tysiące razy identyfikator 1. Na przykład tabela car_brands, kolumny Brandsi Id. Rząd Mercedes-Benzi 1. A w głównej kolumnie tabeli Brandsi wartości 1. A kiedy SELECT, to najpierw wstań Idod stołu, car_brandsa potem SELECT Something FROM main_table WHERE Brands = (SELECT Id FROM car_brands WHERE Brands = Mercedes-Benz). Albo inne podejście?
Andris,
3
@ user2118559: Tak, tak byś to przechowywał. Aby uzyskać dane byś zazwyczaj używają dołączyć zamiast podkwerendzie: select something from main_table c inner join car_brands b on b.Id = c.Brands where b.Brands = 'Mercedes-Benz'.
Guffa
Dlaczego głos przeciw? Jeśli nie wyjaśnisz, co Twoim zdaniem jest niewłaściwe, nie poprawi to odpowiedzi.
Guffa
8

Rozbijając się na rzeczywistą wydajność porównywania ciągów w porównaniu z wartościami zmiennoprzecinkowymi, w tym przypadku dowolny rozmiar bez znaku i ze znakiem nie ma znaczenia. Rozmiar jest właściwie prawdziwą różnicą w wydajności. Czy to 1 bajt + (do 126 bajtów), czy porównanie 1, 2, 4 lub 8 bajtów ... oczywiście nie-zmiennoprzecinkowe są mniejsze niż łańcuchy i zmiennoprzecinkowe, a zatem są bardziej przyjazne dla procesora w montażu.

Porównanie ciągów znaków we wszystkich językach jest wolniejsze niż coś, co może zostać porównane w jednej instrukcji przez procesor. Nawet porównanie 8 bajtów (64 bity) na 32-bitowym procesorze jest nadal szybsze niż VARCHAR (2) lub większe. * Ponownie, spójrz na utworzony zestaw (nawet ręcznie), aby porównać znaki po znaku, potrzeba więcej instrukcji niż numeryczny procesor CPU o wielkości od 1 do 8 bajtów.

O ile szybciej? zależy również od ilości danych. Jeśli po prostu porównujesz 5 do „audi” - i to wszystko, co ma twój DB, wynikowa różnica jest tak minimalna, że ​​nigdy byś jej nie zauważył. W zależności od procesora, implementacji (klient / serwer, sieć / skrypt itp.) Prawdopodobnie nie zobaczysz tego, dopóki nie trafisz kilkuset porównań na serwerze DB (może nawet kilka tysięcy porównań, zanim będzie to zauważalne).

  • Aby unieważnić nieprawidłowy spór dotyczący porównań skrótów. Większość samych algorytmów haszujących jest powolna, więc nie korzystasz z rzeczy takich jak CRC64 i mniejsze. Od ponad 12 lat opracowuję algorytmy wyszukiwania dla wyszukiwarek wielopowiatowych i 7 lat dla biur informacji kredytowej. Wszystko, co możesz zapisać w liczbach, tym szybciej ... na przykład numery telefonów, kody pocztowe, a nawet waluta * 1000 (przechowywanie) waluta div 1000 (pobieranie) jest szybsze niż DECIMAL dla porównań.

Ozz

Ozz Nixon
źródło
6

Indeksuj czy nie, int jest dużo szybsze (im dłuższy varchar, tym wolniej).

Kolejny powód: indeks na polu varchar będzie znacznie większy niż na int. W przypadku większych tabel może to oznaczać setki megabajtów (i tysiące stron). To znacznie pogarsza wydajność, ponieważ odczyt samego indeksu wymaga wielu odczytów dysku.

Konrad Garus
źródło
3
Na przykład 5 milionów rekordów „audi”, czy indeks nie zawierałby tylko jednej kopii ciągu „audi” i 5 milionów liczb całkowitych podstawowego_klucza? Czy różnica wielkości byłaby naprawdę tak duża, czy to vchar, czy liczba całkowita?
lulalala
Masz rację lulalala, ale dla kolumny, która ma zawierać losowe ciągi, odpowiedź jest wystarczająca.
Awais fiaz
4

Ogólnie int będzie szybszy. Im dłuższy jest varchar, tym wolniej się robi

anthares
źródło
3

Wskazówka: Jeśli możliwe wartości dla pola marki będzie nigdy (lub rzadko) zmiany, można użyć ENUM jako kompromis. Łączy dobrą szybkość z dobrą czytelnością.

Thomas Schaub
źródło
1
Ciekawe, jaka będzie różnica prędkości między ENUM i int?
googletorp
Czy PostgresSQL ma enumtyp danych? Myślałem, że jest to specyficzne dla MySQL.
Robert Munteanu
Postgres ma ENUM, ale nie sądzę, że jest zaimplementowany w taki sam sposób jak MySQL. postgresql.org/docs/current/static/datatype-enum.html
googletorp
2
Pod względem wydajności ENUM powinien działać mniej więcej tak samo jak int w polu wyszukiwania, ale tak samo jak varchar na liście docelowej (ponieważ musi przesłać cały ciąg do klienta dla dopasowanych wierszy, a nie tylko int)
Magnus Hagander
1
Oto ciekawa lektura o tym, dlaczego NIE używać enum w MySQL (tylko po to, aby dodać trochę paliwa do ognia: D)
Wilt
1

Jeśli włączysz indeksowanie któregokolwiek z pól, będzie to szybsze. Jeśli chodzi o twoje pytanie, myślę, że intjest szybsze niż varchar.

Sarfraz
źródło
0

Nieco względne. Tak, INT będą szybsze, ale pytanie brzmi, czy jest to zauważalne w Twojej sytuacji. Czy VARCHAR to tylko małe słowa czy dłuższe teksty? a ile wierszy jest w tabeli? Jeśli jest tylko kilka wierszy, najprawdopodobniej będzie on całkowicie buforowany w pamięci (gdy jest często żądany), w takim przypadku nie zauważysz dużej różnicy. Oczywiście jest też indeksowanie, które staje się ważniejsze, gdy tabela rośnie. Korzystanie z dysków SSD może być szybsze niż dysków HD ze zoptymalizowanymi zapytaniami. Dobre kontrolery dysków czasami przyspieszają zapytania> 10x. Może to zostawić miejsce na zwykłe używanie VARCHAR, co ułatwia czytanie i pisanie zapytań (nie ma potrzeby pisania złożonych złączeń) i przyspiesza rozwój. Puryści jednak się nie zgodzą i zawsze wszystko znormalizują.

Alex
źródło