Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Zliczanie wystąpień rekordu w tabelach
zoltodziob
post
Post #1





Grupa: Zarejestrowani
Postów: 8
Pomógł: 0
Dołączył: 9.05.2015

Ostrzeżenie: (0%)
-----


Witajcie.
Postanowiłem założyć konto na Forum. Od kilku miesięcy korzystałem z jego pomocy, głównie przeszukując archiwalne tematy, jednak dzisiaj potrzebuję Waszej pomocy.
Mam do wykonania takie zapytanie MYSQL: muszę sprawdzić czy podany rekord id_klienta o wartości dajmy na to 10 w tabeli KLIENCI został użyty w innych tabelach. Zapytanie miało by zwraca łączną liczbę wystąpień we wszystkich tabelach (np. w 6 podanych tabelach). Próbowałem za pomocą JOIN ale strasznie skomplikowane przy wielu tabelach. Czytałem też o HAVING, ale trochę tego nie ogarniam. Dodam też pracuję przy pomocy Codeigniter. Macie jakiś pomysł? (IMG:style_emoticons/default/wink.gif)
Go to the top of the page
+Quote Post
mmmmmmm
post
Post #2





Grupa: Zarejestrowani
Postów: 1 421
Pomógł: 310
Dołączył: 18.04.2012

Ostrzeżenie: (0%)
-----


Funkcje agregujące (Count) przyjacielem twym. Łącz kolejno z każdą z tabel (tych 6 podanych) i daj WHERE + Count. Sprawdzić ilość możesz sobie na kliencie.
Go to the top of the page
+Quote Post
zoltodziob
post
Post #3





Grupa: Zarejestrowani
Postów: 8
Pomógł: 0
Dołączył: 9.05.2015

Ostrzeżenie: (0%)
-----


Nie wiem czy dobrze rozumiem, ale poniższy kod na pewno nie działa:
  1. SELECT COUNT(k.klient_id) AS ilość
  2. FROM klienci k
  3. JOIN tabela1 t1 ON t1.klient_id = k.klient_id
  4. JOIN tabela2 t2 ON t2.klient_id = k.klient_id
  5. WHERE k.klient_id = 10


Ten post edytował zoltodziob 10.05.2015, 10:22:30
Go to the top of the page
+Quote Post
lukasz1985
post
Post #4





Grupa: Zarejestrowani
Postów: 205
Pomógł: 43
Dołączył: 5.03.2012

Ostrzeżenie: (0%)
-----


Zlicz z każdej tabeli osobno i zsumuj

  1.  
  2. $a = $this->db->where("id_klienta", $id_klienta)->get("tabela1")->result_array();
  3. $count_a = count($a);
  4.  
  5. $b = $this->db->where("id_klienta", $id_klienta)->get("tabela2")->result_array();
  6. $count_b = count($b);
  7.  
  8. $c = $this->db->where("id_klienta", $id_klienta)->get("tabela3")->result_array();
  9. $count_c = count($c);
  10.  
  11. $suma = $count_a + $count_b + $count_c;


Ten post edytował lukasz1985 10.05.2015, 11:19:51
Go to the top of the page
+Quote Post
zoltodziob
post
Post #5





Grupa: Zarejestrowani
Postów: 8
Pomógł: 0
Dołączył: 9.05.2015

Ostrzeżenie: (0%)
-----


Takiego rozwiązania chciałem uniknąć. Chciałem to zrobić przy pomocy jednego zapytania. Generalnie potrzebuję tego do sprawdzenia, czy dany klient może zostać usunięty z bazy, to znaczy czy nie jest do niego przypisane żadne zamówienie, płatność i inne. Jeśli takowe wystąpią to system miałby nie pozwolić usunąć.
Go to the top of the page
+Quote Post
viking
post
Post #6





Grupa: Zarejestrowani
Postów: 6 381
Pomógł: 1116
Dołączył: 30.08.2006

Ostrzeżenie: (0%)
-----


Od tego jest sam silnik BD. Innodb oraz ON DELETE RESTRICT.
Go to the top of the page
+Quote Post
DarkAbso
post
Post #7





Grupa: Zarejestrowani
Postów: 60
Pomógł: 10
Dołączył: 17.11.2011

Ostrzeżenie: (0%)
-----


Cytat(viking @ 10.05.2015, 15:12:58 ) *
Od tego jest sam silnik BD. Innodb oraz ON DELETE RESTRICT.

Dokładnie, pod warunkiem, iż dobrze relacje są porobione. Jeśli nie to spróbuj usuwać takim zapytaniem :
  1. DELETE FROM klient
  2. WHERE id = 10
  3. AND id NOT IN ( SELECT id_klient FROM test1
  4. UNION ALL
  5. SELECT id_klient FROM test2)

Gdzie NOT IN łączysz sobie tabele za pomocą UNION ALL. , a gdzie id to interesujące Ciebie id klienta.

Jeżeli chcesz zliczać to:
  1. SELECT COUNT(id_klient) FROM
  2. (SELECT id_klient FROM test1
  3. UNION ALL
  4. SELECT id_klient FROM test2) AS test
  5. WHERE id_klient = 10


Ten post edytował DarkAbso 11.05.2015, 12:26:36
Go to the top of the page
+Quote Post
mmmmmmm
post
Post #8





Grupa: Zarejestrowani
Postów: 1 421
Pomógł: 310
Dołączył: 18.04.2012

Ostrzeżenie: (0%)
-----


Omatkoboska...
NOT IN bez warunku na NULL? http://sqlfiddle.com/#!9/9f0e5/1
Powinno być tak: http://sqlfiddle.com/#!9/9f0e5/2
W tym drugim wydajnościowo do dupy maksymalnie. WHERE powinien być w środku, w każdym podzapytaniu.
Go to the top of the page
+Quote Post
DarkAbso
post
Post #9





Grupa: Zarejestrowani
Postów: 60
Pomógł: 10
Dołączył: 17.11.2011

Ostrzeżenie: (0%)
-----


Cytat(mmmmmmm @ 11.05.2015, 15:08:37 ) *
Omatkoboska...
NOT IN bez warunku na NULL? http://sqlfiddle.com/#!9/9f0e5/1
Powinno być tak: http://sqlfiddle.com/#!9/9f0e5/2
W tym drugim wydajnościowo do dupy maksymalnie. WHERE powinien być w środku, w każdym podzapytaniu.

Ciężko odpowiedzieć precyzyjnie na pytanie jeżeli nie znam schematu bazy. Jeżeli w tabelach na id_klient jest ustawione, iż nie może przyjmować wartości null to niema problemu, w ręcz dodatkowy warunek jest zbędny "bo wydajność" (niby niewielka będzie różnica, ale przy komercyjnych rozwiązania każdy czas się liczy).
Co do drugiego zapytania to masz rację mogłem ograniczyć wcześniej i nie rzeźbić po wszystkich rekordach. Zresztą jest to proponowane rozwiązanie. Ten problem ma kilka rozwiązań, a mysql niestety ma kilka ograniczeń między innymi brak klauzuli WITH za pomocą której można w przejrzysty sposób ładnie załatwić sprawę bez zagnieżdżonych zapytań. Sprawę można załatwić również za pomocą INNER JOIN lub NOT EXISTS. Jak to się mówi temat woda, ale dzięki za zwrócenie uwagi. Na drugi raz będę dokładniej analizował problemy na tym forum, aby podać precyzyjną odpowiedź.

Ten post edytował DarkAbso 11.05.2015, 14:50:01
Go to the top of the page
+Quote Post
zoltodziob
post
Post #10





Grupa: Zarejestrowani
Postów: 8
Pomógł: 0
Dołączył: 9.05.2015

Ostrzeżenie: (0%)
-----


Zgadza się id_klient nie może przyjmować wartości NULL. Kombinuję z łączeniem JOIN, ale nie wychodzi. Zastanawiam się nad ON DELETE RESTRICT. Czytałem trochę o tym, ale nie wiem czy odpowiednio uda mi się ustawić odpowiednie relacje - jestem laikiem w tej sprawie. A wy jakie rozwiązanie proponujecie, żeby było jak najbardziej wydajne? Baza będzie się składać z ponad 3000 rekordów jeśli chodzi o klientów i do tego dochodzą tabele związane z zamówieniami itp.
Go to the top of the page
+Quote Post
salfunglandyare
post
Post #11





Grupa: Zarejestrowani
Postów: 150
Pomógł: 31
Dołączył: 10.01.2007
Skąd: Bydgoszcz/Inowrocław

Ostrzeżenie: (0%)
-----


on delete restrict lub no action spowoduje, że podczas próby usunięcia dostaniesz warning z bazy danych, że rekordu nie można usunąć, bo istnieją rekordy zależne - w innym przypadku - usuniesz. Constraints nie zadziała przy myisam (domyślnym silniku mysql), musisz ustawić na innodb.
Jeśli chcesz załatwić to jednym zapytaniem - da się - left join, np:
  1. SELECT klienci.id, (count(a.id) + count(b.id) + count(c.id)) AS ilosc FROM klienci
  2. LEFT JOIN costam1 AS a ON (a.id_klient = klienci.id)
  3. LEFT JOIN costam2 AS b ON (b.id_klient = klienci.id)
  4. LEFT JOIN costam3 AS c ON (c.id_klient = klienci.id)
  5. WHERE klienci.id = ID_KLIENTA
  6. GROUP BY klienci.id;


podstawiasz swoje dane, w wyniku dostajesz ID oraz ilosc jako sumę rekordów ze wszystkich tabel

//edit: Jeszcze info o constraints - fajna opcja - cascade, w przypadku, gdy chcesz usunąć rekord, cascade pozwoli Ci usunąć wszystkie rekordy zależne, ale WAŻNE jest odpowiednie stowrzenie struktury tabel, niekiedy przydaje się set null - tam gdzie zamiast usunięcia rekordu powinna zostać wyrzucona dana o identyfikatorze elementu

Ten post edytował salfunglandyare 12.05.2015, 20:49:24
Go to the top of the page
+Quote Post
zoltodziob
post
Post #12





Grupa: Zarejestrowani
Postów: 8
Pomógł: 0
Dołączył: 9.05.2015

Ostrzeżenie: (0%)
-----


Zrobię chyba tak jak zaproponował salfunglandyare. Wszystkim dziękuję za udział w dyskusji.
Go to the top of the page
+Quote Post
DarkAbso
post
Post #13





Grupa: Zarejestrowani
Postów: 60
Pomógł: 10
Dołączył: 17.11.2011

Ostrzeżenie: (0%)
-----


Zapytanie z left join'ami prawdopodobnie będzie najdłużej się wykonywało, ale 3000 rekordów to "żadna" ilość dla baz danych. Rozwiązanie wybierz takie na którym będzie najłatwiej Tobie pracować.
Go to the top of the page
+Quote Post
salfunglandyare
post
Post #14





Grupa: Zarejestrowani
Postów: 150
Pomógł: 31
Dołączył: 10.01.2007
Skąd: Bydgoszcz/Inowrocław

Ostrzeżenie: (0%)
-----


Cóż, jestem pewny, że szybciej niż subqueries (IMG:style_emoticons/default/wink.gif) ale też nie napisałem, że to rozwiązanie optymalne - ale chyba najlepsze jeśli chodzi o rozwiązanie w stylu 'jedno zapytanie' (IMG:style_emoticons/default/biggrin.gif)
Go to the top of the page
+Quote Post
DarkAbso
post
Post #15





Grupa: Zarejestrowani
Postów: 60
Pomógł: 10
Dołączył: 17.11.2011

Ostrzeżenie: (0%)
-----


Nie do końca i już wytłumaczę dlaczego (chociaż mogę się mylić ). Najpierw budujesz widok tabeli klienci połączonej z innymi (costam1 itd...), a następnie dopiero ograniczasz where (czyli kleimy wszystko a później dopiero wycinamy). Najlepiej było by od razu ograniczyć do rekordów które potrzebujemy. W tym wyniku takie zapytanie zagnieżdżone będzie szybsze:
  1. SELECT COUNT(klient_id) FROM
  2. (SELECT klient_id FROM tabela1 WHERE klient_id = 1
  3. UNION ALL
  4. SELECT klient_id FROM tabela2 WHERE klient_id = 1
  5. UNION ALL
  6. SELECT klient_id FROM tabela3 WHERE klient_id = 1) AS test

Do tego użyte jest jedno wywołanie funkcji count i brak dodawania co też wpływa na tempo zapytania.

W Twoim zapytaniu można kombinować z ograniczeniami od razu przy join'ach co by przyśpieszyło zapytanie.

Zresztą z ciekawości wykonałem test i go powtórzyłem kilka razy. Co prawda na małej ilości danycm ale moje zapytanie wykonuje się 0 ms, a Twoje 1 ms. Z ciekawości jutro sprawdzę na większej ilości danych. (IMG:style_emoticons/default/smile.gif)
Go to the top of the page
+Quote Post
salfunglandyare
post
Post #16





Grupa: Zarejestrowani
Postów: 150
Pomógł: 31
Dołączył: 10.01.2007
Skąd: Bydgoszcz/Inowrocław

Ostrzeżenie: (0%)
-----


masz rację, ale ja pisałem o subquerries w sensie select w where innego selecta. Co do unii, masz pewnie racje w przypadku, gdy tych tabel jest relatywnie mało, gdyż każdy select jest wykonywany z osobna i "doklejany" do wyniku, suma z tego może wykonywać się krócej dla takich zapytań, kto wie, czy nie agregując tak:
  1. SELECT sum(s) AS suma FROM
  2. (
  3. SELECT count(klient_id) AS s FROM tabela1 WHERE klient_id = 1
  4. UNION ALL
  5. SELECT cpunt(klient_id) AS s FROM tabela2 WHERE klient_id = 1
  6. UNION ALL
  7. SELECT count(klient_id) AS s FROM tabela3 WHERE klient_id = 1
  8. );

nie wyjdzie szybciej. Joiny maja swoja potege w join buffer i w indeksach (IMG:style_emoticons/default/smile.gif)
Go to the top of the page
+Quote Post
DarkAbso
post
Post #17





Grupa: Zarejestrowani
Postów: 60
Pomógł: 10
Dołączył: 17.11.2011

Ostrzeżenie: (0%)
-----


Jak już pisałem, temat woda. Jest kilka rozwiązań, ale dobrze, że ktoś porusza kwestię wydajności zapytań. Dla zoltodziob nie będzie różnicy, którą opcję wybierze jak ma kilka tabel po kilkaset rekordów. Problem pojawia się jak jest kilkadziesiąt tabel po kilka milionów rekordów. (IMG:style_emoticons/default/smile.gif) Miło, że ktoś chce się dzielić swoimi spostrzeżeniami i wiedzą. Jutro jak się uda to przetestuje rozwiązania na większe ilości danych. (IMG:style_emoticons/default/smile.gif)
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 3.04.2026 - 18:39