Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Mnożenie w limicie
Forum PHP.pl > Forum > Bazy danych > MySQL
Destrudo
Hej,

Mam takie rekordy w bazie:

xxxx
xxxx
yyyy

czyli 2 są takie same i 1 inny, w sumie 2 różne.

Potrzebuję zrobić zapytanie w stylu (poglądowo): select x,y from tabela order by data desc limit (ilość różnych rekordów czyli 2*30) i nie bardzo mam pomysł jak takie coś zrobic. Ma ktoś może pomysł?
trueblue
A mógłbyś wyjaśnić czemu potrzebny jest ten limit?

Akurat w Twoim przypadku przy trzech rekordach limit będzie na 6 rekordów, więc jest kompletnie niepotrzebny.
Oczywiście w innych przypadkach będzie mieć wpływ.

I dlaczego jest tam mnożnik 30?
Destrudo
Przykładowo podałem bazę, rekordów ja tam mam około 100 i teraz jeśli dam limit 30, to pokaże mi w sumie 15 * xxxxx i 15 * yyyyy, a chciałbym, żeby pokazało 30 * xxxxx i 30 * yyyyy, a więc musiałbym dać limit 60, a nie wiem ile będzie różnych rekordów dublujących się, więc jeśli bym je zliczył, wystarczyłoby pomnożyć limit 30 razy ilość różnych rekordów
nospor
Wystarczy ze bedziesz mial pod rzad 59 xxxx i 1 yyyy i caly twoj zmyslny algorytm szlag trafi wink.gif
Destrudo
w sumie masz rację. to jakby to rozwiązać?

chociaż czekaj, 59x i 1y, to 2 różne, a wiec 30*2 rozne = 60, a wiec tyle ile byłoby rekordow, czyli by zadziałało?
javafxdev
jaki to problem ma rozwiązywać? bo wygląda że trzeba do tego zupełnie inaczej podejść, ale musisz powiedzieć jaki problem to ma rozwiązywać, a nie sugerować gotowe (raczej złe) pomysły.
Destrudo
powinno mi pokazać ostatnie 30 rekordow z bazy danych dla każdego rodzaju rekordu, czyli 30 dla xxxx i 30 dla yyyy. Jeśli po prostu wpiszę limit 30, to mi pokaże po 15 (w sumie będzie 30), a chciałbym po 30 dla każdego
nospor
Cytat
chociaż czekaj, 59x i 1y, to 2 różne, a wiec 30*2 rozne = 60, a wiec tyle ile byłoby rekordow, czyli by zadziałało?
No jak zadziala? Wyswietli ci 59xxx i 1 yyyy a chciales miec po 30 wink.gif
Destrudo
fakt biggrin.gif chodzi mi generalnie o to, żeby nie tyle po 30, co maksymalnie 30 z każdego, bo nieraz będzie 50 z jednego i 2 z innego czy tego typu
nospor
Cytat
żeby nie tyle po 30, co maksymalnie 30 z każdego, bo nieraz będzie 50 z jednego i 2 z innego czy tego typu
No tego to ja sie domyslilem wink.gif
Destrudo
Widzę właśnie biggrin.gif To jakby to rozwiąząć?
nospor
Wpierw pobierasz wszystkie rozne xxxx yyyyy.
SELECT DISTINCT....

potem dla kazdego piszesz zapytanie ktore pobierze ci max 30 rekordow dla kazdego z nich
...where costam=xxxxx limit 30
...where costam=yyyyy limit 30
itd

Mozesz to upchnoc w UNION i miec jedno zapytanie zamiast x malych
Destrudo
a jeśli nie znam xxxxx i yyyy? tzn bedzie tyle roznych, ze caly czas musialbym modyfikowac zapytanie zeby mi pokazywalo dobrze i dodawac kolejne warunki z nowymi rekordami? czy źle zrozumiałem?
javafxdev
zrób correlated subquery
nospor
Dlatego napisalem
Cytat
Wpierw pobierasz wszystkie rozne xxxx yyyyy.
SELECT DISTINCT....

To zapytanie zwroci ci wszyskie xxxx yyyyy zzzz czy co ty tam masz.
Destrudo
Jednak nie rozumiem :/ mógłbyś podać poglądowy przykład? może niekoniecznie gotowca, ale schemat, bo zbyt ogolnie napisales i nie rozumiem. dajmy na to, że mam już zrobiony select distinct na limit 30 tak? co dalej?
javafxdev
nie słuchaj nospora - zrób correlated subquery tak jak Ci wujek mówi:
  1. CREATE TABLE test (id int(11) UNSIGNED PRIMARY KEY AUTO_INCREMENT , x varchar(255) NOT NULL);


potem:

  1. INSERT INTO test (x) VALUES ('xxx'), ('yyy');
  2. INSERT INTO test (x) VALUES ('xxx'), ('yyy');
  3. INSERT INTO test (x) VALUES ('xxx'), ('yyy');
  4. INSERT INTO test (x) VALUES ('xxx'), ('yyy');
  5. INSERT INTO test (x) VALUES ('xxx'), ('yyy');
  6. INSERT INTO test (x) VALUES ('xxx'), ('xxx');
  7. INSERT INTO test (x) VALUES ('zzz'), ('zzz');


potem:

  1. SELECT * FROM test t WHERE (SELECT COUNT(*) FROM test t2 WHERE t.id <= t2.id AND t.x = t2.x) <= 4 ORDER BY t.x;


działa? ano działa
trueblue
javafxdev,
nie do końca, bo miały to być rekordy sortowane według daty, a nie po prostu top/last z grupy.

  1. CREATE TABLE `xy` (
  2. `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `x` VARCHAR(255) NOT NULL,
  4. `data` DATE NULL DEFAULT NULL,
  5. PRIMARY KEY (`id`)
  6. )
  7.  
  8. INSERT INTO `xy` VALUES (1, 'xxx', '2016-01-01');
  9. INSERT INTO `xy` VALUES (2, 'yyy', '2016-01-02');
  10. INSERT INTO `xy` VALUES (3, 'xxx', '2016-01-03');
  11. INSERT INTO `xy` VALUES (4, 'yyy', '2016-01-04');
  12. INSERT INTO `xy` VALUES (5, 'xxx', '2016-01-05');
  13. INSERT INTO `xy` VALUES (6, 'yyy', '2016-01-06');
  14. INSERT INTO `xy` VALUES (7, 'xxx', '2016-01-07');
  15. INSERT INTO `xy` VALUES (8, 'yyy', '2016-01-08');
  16. INSERT INTO `xy` VALUES (9, 'xxx', '2016-01-09');
  17. INSERT INTO `xy` VALUES (10, 'yyy', '2016-01-10');
  18. INSERT INTO `xy` VALUES (11, 'xxx', '2016-01-11');
  19. INSERT INTO `xy` VALUES (12, 'xxx', '2016-01-12');
  20. INSERT INTO `xy` VALUES (13, 'zzz', '2016-01-13');
  21. INSERT INTO `xy` VALUES (14, 'zzz', '2016-01-14');


5 rekordów z każdej grupy sortowane według daty desc:
  1. SELECT x3.x,x3.DATA FROM (SELECT x1.*, (SELECT COUNT(*) FROM xy AS x2 WHERE x1.data<=x2.DATA AND x1.x=x2.x) AS wiersz FROM xy AS x1) x3
  2. WHERE wiersz <=5 ORDER BY x, DATA DESC;
javafxdev
ja bym się pokusił jeszcze w Twoim przykładzie zeby zamiast x1.x=x2.x dać x1.id=x2.id wtedy trochę bardziej zoptymalizowane będzie. ale ogólnie masz rację - w moim przykładzie brakowało sortowania po datach w grupach. Chodziło mi bardziej o pokazanie żeby nie robić jakiś niepotrzebnych UNIONÓW i DISTINCTÓW tylko ładnie machnąć w SQL.
Destrudo
Dziękuje, działa fajnie, tylko jak np pokazuje mi:

xxxx - 30 rekordów
yyyy - 30 rekordów

i teraz dajmy na to dodam 2 kolejne rekordy xxxx i yyyyy, tylko że yyyy z nowszą datą, to yyyy - 30 rekordów, pokaże się najpierw, a chciałbym tego nie zmieniać, czyli jak kolejność jest już:

xxxx - 30 rekordów
yyyy - 30 rekordów

to żeby tylko rekordy się sortowały, a nie xxxx i yyy i kolejność nie zmieniła się na:

yyyy - 30 rekordów
xxxx - 30 rekordów
trueblue
Jak wygląda ORDER BY w zapytaniu?
nospor
Cytat
nie słuchaj nospora - zrób correlated subquery tak jak Ci wujek mówi:

@wujek wszystko fajnie pieknie, ale jak wyglada sprawa z wydajnoscia takiego zapytania dla wiekszej ilosci danych? Bo tak na pierwszy rzut oka to nie wyglada to za dobrze
javafxdev
Załóżmy, że różnych (x,y,z.....) mamy 50 po 10 w każdym przypadku - Twoja metoda wykona 1 zapytanie DISTINCT, i 50 zapytań do pobrania rekordów czyli 51 zapytań żeby pobrać 500 rekordów - troche dużo sam roudtrip do bazy będzie mało wydajny i wydajność będzie spadać wraz z większą ilością różnych elementów - słabo skalowalne, natomiast wykonanie subquery skaluje się lepiej, bo za kazdym razem masz 1 zapytanie - niezależnie od ilości różnorodnych elementów.
nospor
Dlatego powiedzialem by uzyc union. Wowczas masz dwa zapytania.

A teraz zalozmy ze masz milion rekordow i 50 roznych danych. Te podzapytania ktore tam masz i liczenie dla kazdego wiersza pokolei danych nie wyglada dla mnie zbyt obiecujaco
javafxdev
jak napiszesz kawałek kodu, który robi to Twoją metodą (skleja te wielkie UNIONY itd) to ja załaduje do bazy danych 1mln rekordów z różnymi x,y,z i porównamy, a jak nie zrobisz to załóżmy że moje działa wydajniej wink.gif
nospor
OK, specjalnie dla ciebie odpalilem te wasze cudo.

Liczba rekordow: 200 000
Liczba roznych danych:100

Czas pobrania 30 rekordow dla danego jednego: 0.03s
Union ze 100 takimi zapytaniami: 3 sekundy
DISTINCT by pobrac te 100 roznych danych: 0.2s.
Laczny czas: nie wiecej niz 5 sekund.


A teraz wasze cudo:
czas:1 minuta 52 sekundy

Tyle w temacie tongue.gif

edit: a skad taki dlugi czas u Was? Bo tak jak mowilem wczesniej, wy tworzycie tyle zapytan ile macie rekordow. Jest 200 000 rekordow to to zapytanie
SELECT COUNT(*) FROM xy AS x2 WHERE x1.data<=x2.DATA AND x1.x=x2.x
wykona sie 200 000 razy. I ty mi mowisz ze moje 50 zapytan jest obciazeniem.... tongue.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.