SQL IN i uniq

To chyba bardziej pytanie z baz danych ale mam taki przykład

User.where(:id => events.map(&:user_id)) SELECT SQL_NO_CACHE id, name FROM `users` WHERE `users`.`id` IN (1, 1, 1, 1)
I pytanie czy warto robić uniq na tablicy przed przekazaniem jej do where

User.where(:id => events.map(&:user_id).uniq) SELECT * FROM `users` WHERE `users`.`id` IN (1)
Zakładam że events będzie dużą kolekcją

Seriously. Numbers. Use them
:slight_smile:

EDIT:

W zasadzie to nie lubię takich lakonicznych postów, więc dopiszę :wink: Najlepiej sprawdzić samemu - wypełnij tą tabelę przykładowymi danymi i zrób 2 benchmarki. Fajna zabawa (przynajmniej dla mnie), a nie będziesz się opierał na teorii kogos z forum.

a czy przypadkiem to, co chcesz uzyskać to nie przypadkiem

events.map(&:user)#ew .uniq

jeżeli nie, to zrób

events.uniq.pluck("user_id")

@drogus Moje lenistwo obawiało się takiego postu :slight_smile:
Miałem nadzieję, że ktoś już takie testy zrobił i zna odpowiedź (Tak|Nie|Bez znaczenia)
Zasiduje i zbenczmarkuje

@krzyzak
Chodzio mi tylko o to czy bazy nie denerwują zduplikowane wartości dla IN

Moim zdaniem warto, gdyż korzyść będzie tym większa im większe będzie events.size. Jeśli chcesz zmierzyć to samemu to musisz wziąć pod uwagę to, że testujesz to na konkretnym silniku bazodanowym oraz konkretnej jego wersji. Dla przykładu postgres:

[code]=# explain analyze verbose select id, first_name from users where id in(1,1,1,1,1,1,1,1,1,1,1,1,2);
QUERY PLAN

Bitmap Heap Scan on users (cost=55.37…101.65 rows=13 width=11) (actual time=0.033…0.033 rows=0 loops=1)
Output: id, first_name
Recheck Cond: (id = ANY (’{1,1,1,1,1,1,1,1,1,1,1,1,2}’::integer[]))
-> Bitmap Index Scan on users_pkey (cost=0.00…55.37 rows=13 width=0) (actual time=0.032…0.032 rows=0 loops=1)
Index Cond: (id = ANY (’{1,1,1,1,1,1,1,1,1,1,1,1,2}’::integer[]))
Total runtime: 0.061 ms
(6 rows)

=# explain analyze verbose select id, first_name from users where id in(1,2);
QUERY PLAN

Bitmap Heap Scan on users (cost=8.52…16.18 rows=2 width=11) (actual time=0.021…0.021 rows=0 loops=1)
Output: id, first_name
Recheck Cond: (id = ANY (’{1,2}’::integer[]))
-> Bitmap Index Scan on users_pkey (cost=0.00…8.52 rows=2 width=0) (actual time=0.019…0.019 rows=0 loops=1)
Index Cond: (id = ANY (’{1,2}’::integer[]))
Total runtime: 0.045 ms
(6 rows)[/code]
Możliwe, że optymalizotor skróci to zapytanie, ale imho lepiej zrobić to samemu. Z tego co widzimy będziemy robić “Bitmap Index scan” a warunkiem będzie tutaj:

Index Cond: (id = ANY ('{1,1,1,1,1,1,1,1,1,1,1,1,2}'::integer[]))

lub

Index Cond: (id = ANY ('{1,2}'::integer[]))

Teraz pytanie jak działa operator ANY() oraz operator IN(). Dla postgresa będzie to tak: http://www.postgresql.org/docs/9.1/static/functions-comparisons.html
Nie ma nic o optymalizacji, więc pewnie leci od lewej do prawej, aż do znalezienia pasującej wartości.

Podsumowując: Uważam, że lepiej skorzystać z uniq() u siebie w kodzie i posiadać 100% gwarancję optymalizacji. Kolejna rzecz, to zrzucamy z bazy danych optymalizacje zapytania, przez co kiepsko skalująca się baza danych staje się mniejszym wąskim gardłem oraz trzeba wysłać mniej pakietów TCP z zapytaniem, ponieważ zapytanie będzie krótsze. Tak jak pisałem na początku zysk z wydajności będzie tym bardziej widoczny im większe będzie events.size.

Dzięki za full wypas odpowiedź