Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [PHP][MSSQL] Kilka kategorii jednego artykułu
Kshyhoo
post
Post #1





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Witam. Pytanie czysto teoretyczne: jak najlepiej trzymać kategorie artykułu przyjmując, że będzie go można przydzielić do kilku kategorii?
Naczytałem się trochę w Google, ale mam mątwik w głowie - wszystkie znalezione przeze mnie sposoby mają tylko wady a żaden ni jednej zalety (tak twierdzą postujący).
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 19)
mortus
post
Post #2





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


W osobnej tabeli, przy czym mogłaby ona posiadać dodatkową kolumnę mówiącą o priorytecie kategorii.
CATEGORIES: { id, name }
ARTICLES: { id, title, content }
ARTICLES_CATEGORIES: { article_id, category_id, category_priority }

EDIT: Jeśli nie ma jakiejś kategorii głównej dla danego artykułu, to ta trzecia kolumna jest zbędna.

Ten post edytował mortus 1.01.2012, 12:32:10
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #3





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




To tej pory miałem tak dla kategorii:
  1. kat_id kat_kategoria

i dla artykułów (gdzie trzymałem ID kategorii:
  1. tut_id tut_kat_id)


Ten post edytował Kshyhoo 1.01.2012, 12:41:31
Go to the top of the page
+Quote Post
mortus
post
Post #4





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


Domyślam się. Musisz to zmienić, jeśli jeden artykuł może posiadać więcej niż jedną kategorię, to potrzebujesz dodatkowej tabeli realizującej relację wiele do wielu. W tej chwili masz relację jeden do wielu - jedna kategoria może być przypisana wielu artykułom.
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #5





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Czyli trzymanie w tut_kat_id ID wielu kategorii, nie jest dobrym (optymalnym) rozwiązaniem?
Go to the top of the page
+Quote Post
mortus
post
Post #6





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


Cytat(Kshyhoo @ 1.01.2012, 12:51:42 ) *
Czyli trzymanie w tut_kat_id ID wielu kategorii, nie jest dobrym (optymalnym) rozwiązaniem?

Jakimś rozwiązaniem jest, ale na pewno nieoptymalnym.

EDIT: Gwoli uzupełnienia. Jeśli każdy z artykułów może mieć - dajmy na to - trzy do pięciu kategorii, to i takie rozwiązanie przejdzie. Ale jeśli liczba artykułów i kategorii z biegiem czasu znacznie wzrośnie, podobnie jak i liczba użytkowników, to np. przeszukiwanie po kategorii będzie prawdziwą katorgą dla serwera baz danych.
Według mnie jedynym słusznym rozwiązaniem jest stworzenie dodatkowej tabeli, bowiem przy takiej strukturze bazy danych zbudowanie zapytania będzie dużo łatwiejsze, a samo manipulowanie danymi będzie bardziej wydajne.

Ten post edytował mortus 1.01.2012, 13:28:05
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #7





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Czy tak wystarczy, czy mam jakieś indeksy zakładać?
  1. CREATE TABLE `tut_kat` (
  2. `tut_id` INT NOT NULL ,
  3. `kat_id` INT NOT NULL ,
  4. `kat_poziom` INT NOT NULL
  5. ) ENGINE = MYISAM ;

Jak mam rozwiązać zapytania? Do tej pory miałem tak (kategorie):
  1. SELECT kat_kategoria, kat_ile, kat_id
  2. FROM kategorie
  3. LEFT JOIN (SELECT tut_kat_id,
  4. COUNT(*) AS kat_ile
  5. FROM tutoriale
  6. GROUP BY tut_kat_id) AS tutoriale
  7. ON kat_id = tut_kat_id
  8. ORDER BY kat_kategoria ASC


Ten post edytował Kshyhoo 1.01.2012, 13:46:46
Go to the top of the page
+Quote Post
mortus
post
Post #8





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


Zdaje się, że operacje na tabeli zdecydowanie przyspieszy PRIMARY KEY na dwie kolumny jednocześnie, czyli:
  1. CREATE TABLE `tut_kat` (
  2. `tut_id` INT NOT NULL ,
  3. `kat_id` INT NOT NULL ,
  4. `kat_poziom` INT NOT NULL,
  5. PRIMARY KEY `tut_kat_pk` (`tut_id`, `kat_id`)
  6. ) ENGINE = MYISAM ;


Co do zapytania, to jakoś tak:
  1. SELECT kat_kategoria, kat_ile, kat_id
  2. FROM kategorie
  3. LEFT JOIN (
  4. SELECT kat_id, COUNT(*) AS kat_ile
  5. FROM tutoriale
  6. GROUP BY kat_id) AS tutoriale
  7. USING (`kat_id`)
  8. ORDER BY kat_kategoria ASC

Choć to tak na szybko wymyślone i dopiero później mogę ewentualnie poprawić.

Ten post edytował mortus 1.01.2012, 14:02:48
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #9





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Przeglądając to, coś spłodziłem i nawet nieźle wygląda:
  1. CREATE TABLE `tut_kat` (
  2. `kat_id_1` int(11) NOT NULL,
  3. `kat_id_2` int(11) NOT NULL,
  4. `kat_poziom` int(11) NOT NULL,
  5. PRIMARY KEY (`kat_id_1`,`kat_id_2`),
  6. KEY `kat_id_2` (`kat_id_2`)
  7. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

gdzie dwie pierwsze kolumny to poziomy kategorii a trzecia to zagłębienie.
Mam teraz jedynie problem, ze zliczeniem artykułów w poszczególnych kategoriach. Przedtem działałem tak:
  1. SELECT kat_kategoria, kat_ile, kat_id
  2. FROM kategorie
  3. LEFT JOIN (SELECT tut_kat_id,
  4. COUNT(*) AS kat_ile
  5. FROM tutoriale
  6. GROUP BY tut_kat_id) AS tutoriale
  7. ON kat_id = tut_kat_id
  8. ORDER BY kat_kategoria ASC
Go to the top of the page
+Quote Post
mortus
post
Post #10





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


A to zapytanie, które wyżej napisałem daje jakieś wyniki? Pisałem "na szybko", bo musiałem wyjść i nie wiem, czy działa i czy działa prawidłowo.
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #11





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Zwraca błąd Unknown column 'kat_id' in 'field list' .

W sumie, ja nadal nie wiem, jak to trzymać w bazie - jak artykuł ma mieć wiele kategorii.
Go to the top of the page
+Quote Post
mortus
post
Post #12





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


To może po polsku (kolumny przykładowe).
KATEGORIE: {
id_kategorii (klucz główny)
nazwa
}
ARTYKUŁY: {
id_artykułu (klucz główny)
tytuł,
data_utworzenia
}
KATEGORIE_ARTYKUŁÓW: {
id_kategorii (klucz obcy połączony z tabelą KATEGORIE kolumną id_kategorii)
id_artykułu (klucz obcy połączony z tabelą ARTYKUŁY kolumną id_artykułu)
ważność (kolumna dodatkowa określająca, która z kategorii jest ważniejsza - kolumnę tę można pominąć)
}

Nie do końca wiem, co ma robić to zapytanie. Ma liczyć liczbę artykułów/tutoriali w każdej z kategorii? Czy może ma liczyć do ilu kategorii należy dany artykuł/tutorial?

Z tymi kategoriami jest dokładnie tak samo, jak z tagami (słowami kluczowymi) opisującymi dany artykuł.
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #13





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Czyli, jak dobrze rozumiem, KATEGORIE_ARTYKUŁÓW ma wiązać całość:
Kod
id_artykułu | id_kategorii
     1      |      1
     1      |      4
     1      |      5
     2      |      1
     2      |      2
     2      |      4
     2      |      6

Czyli, że artykuł ID=1 należy do kategorii ID=1, 4, 5 a artykuł ID=2, do kategorii ID=1, 2, 4, 6...

Ten post edytował Kshyhoo 1.01.2012, 20:11:25
Go to the top of the page
+Quote Post
mortus
post
Post #14





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


Dokładnie, wtedy zapytaniem:
  1. SELECT `K`.`id_kategorii`, `K`.`nazwa`, COUNT(*) AS `liczba_artykułów` FROM `KATEGORIE` `K` LEFT JOIN `KATEGORIE_ARTYKUŁÓW` `K_A` USING (`id_kategorii`) GROUP BY `K`.`id_kategorii`

wyciągniesz identyfikatory, nazwy oraz liczbę artykułów dla każdej z kategorii.
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #15





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Mam problem jedynie z kluczami obcymi, bo baza w MyISAM.
No i muszę znaleźć sposób, na wybór i zapis (również edycję) wpisów w bazie poprzez PHP.

No i pobierałem artykuł tak:
  1. $zapytanie1 = "SELECT * FROM tutoriale LEFT JOIN kategorie ON (kat_id = tut_kat_id) WHERE tut_id='$tut_id'";

Teraz trzeba będzie to zmienić.

Ten post edytował Kshyhoo 1.01.2012, 20:53:55
Go to the top of the page
+Quote Post
mortus
post
Post #16





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


Nie ma znaczenia, czy to MyISAM, czy InnoDB. Najwyżej nie założysz kluczy obcych, a poza tym i tak wszystko w PHP musisz zrealizować. Wybór to proste potrójne złączenie, a zapis to po prostu dwa zapytania w tym MULTIINSERT.
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #17





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Jak mniemam, moje zapytanie o pojedynczy rekord powinno wyglądać tak:
  1. $zapytanie1 = "SELECT * FROM tut_kat, tutoriale, kategorie
  2. WHERE tut_kat.id_tut=tutoriale.tut_id AND tut_kat.id_kat=kategorie.kat_id
  3. AND tut_kat.id_tut='$tut_id'";


Zmieniłem nieco moje zapytanie i wygląda ono obecnie tak:
  1. $zapytanie1 = "SELECT tut_id, tut_nr, tut_obraz, tut_tytul, tut_autor, kat_kategoria, tut_data, tut_zrodlo, tut_notatki, tut_moderator, tut_czytano, tut_stan, tut_psd, tut_tresc
  2. FROM tut_kat, tutoriale, kategorie
  3. WHERE tut_kat.id_tut=tutoriale.tut_id
  4. AND tut_kat.id_kat=kategorie.kat_id
  5. AND tut_kat.id_tut='$tut_id'";

Jednak, gdy artykuł ma kilka kategorii, w phpMyAdmin mam kilka takich samych wyników, różniących się jedynie ID kategorii. Jaki mam zastosować warunek, żeby tego uniknąć?
Go to the top of the page
+Quote Post
mortus
post
Post #18





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

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


Ta nadmiarowość jest jak najbardziej zrozumiała (i nawet do wybaczenia), ale można się jej pozbyć łącząc wszystkie kategorie za pomocą GROUP_CONCAT:
  1. SELECT tut_id, tut_nr, tut_obraz, tut_tytul, tut_autor, GROUP_CONCAT(kat_kategoria) kategorie, tut_data, tut_zrodlo, tut_notatki, tut_moderator, tut_czytano, tut_stan, tut_psd, tut_tresc
  2. FROM tut_kat, tutoriale, kategorie
  3. WHERE tut_kat.id_tut=tutoriale.tut_id
  4. AND tut_kat.id_kat=kategorie.kat_id
  5. AND tut_kat.id_tut='{$tut_id}'
  6. GROUP BY tut_id
Go to the top of the page
+Quote Post
thek
post
Post #19





Grupa: Moderatorzy
Postów: 4 362
Pomógł: 714
Dołączył: 12.02.2009
Skąd: Jak się położę tak leżę :D




Ja bym się już zastanawiał nad silnikiem. Liczba artykułów z czasem rośnie. Modyfikacje tabeli złączeniowej będą może nie tak częste, ale w MyISAM głupi update czy insert robią lock na całą tabelę :/ Lepiej przejść na InnoDB w przypadku złączeniówki i mieć tylko lock na konkretne wiersze. Ogólnie mortus dobrze radzi i sam identycznie postępuję. Co do edycji lub dodania to masz zapewne MySQL, więc nie widze problemu:
1) Dodajesz nowy artykuł: INSERT zwróci Ci id nowo dodanego. Tylko go podstaw do złączeniówki (IMG:style_emoticons/default/smile.gif)
2) Uaktualniasz artykuł: Najprościej DELETE istniejących aktualnie i insert wybranych przez usera. W wersji inteligentniejszej porównujesz stare i nowe. Usuwasz to, które są w starej wersji i dodajesz te, których w starej nie ma. Plus jest taki, że może Ci się zredukować całość do tylko usuwania albo tylko dodania. Minus? Zapewne usunięcie i dodanie według pierwszej metody będą szybsze niż po stronie php z kombinowaniem co ma być dodane, a co usunięte (IMG:style_emoticons/default/smile.gif)
Go to the top of the page
+Quote Post
Kshyhoo
post
Post #20





Grupa: Opiekunowie
Postów: 3 855
Pomógł: 317
Dołączył: 4.01.2005
Skąd: że




Ufff, udało mi się wymodzić takiego potwora:
  1. $kat_list = ""; $sel = "";
  2. $result = dbquery("SELECT kat_id, kat_kategoria FROM kategorie WHERE kat_cat='0' ORDER BY kat_order");
  3. if (dbrows($result) != 0) {
  4. while ($data = dbarray($result)) {
  5. $result2 = dbquery("SELECT kat_id, kat_kategoria FROM kategorie WHERE kat_cat='".$data['kat_id']."' ORDER BY kat_order");
  6. if (dbrows($result2) != 0) {
  7. $kat_list .= "<optgroup label='".$data['kat_kategoria']."'>\n";
  8. while ($data2 = dbarray($result2)) {
  9. if ($_GET['kat_id'] == $data2['kat_id']) { $sel = " selected"; } else { $sel = ""; }
  10. $kat_list .= "<option value='".$data2['kat_id']."'$sel>".$data2['kat_kategoria']."</option>\n";
  11. }
  12. $kat_list .= "</optgroup>\n";
  13. }
  14. }
  15. }
  16. echo '<select name="kat_id[]" style="width:150px;" size="5" multiple>\n'.$kat_list.'</select>

Odebrać mógłbym tak:
  1. if (isset($_POST['kat_id'])) {
  2.  
  3. if (count($_POST['kat_id'])==0) echo "Musisz wybrać!";
  4. else echo "Wybrane kategorie: ";
  5.  
  6. for ($i=0;$i<count($_POST['kat_id']);$i++) echo $_POST['kat_id'][$i]."; ";
  7.  
  8. }

I działa. Nie wiem teraz tylko, jak mam do zapisać do bazy, gdzie mam takie pola
  1. id_tut id_kat

czyli muszę zapisać, kilka id_kat z jednym id_tut

Podpowie mi ktoś? Da się to zrobić na raz, czy pętlą?

PS. Oczywiście, brak filtrowania (nie bijcie za mocno)...

EDIT. Coś Geshi się buntuje, całe szczęście miałem kopię (IMG:style_emoticons/default/wink.gif)

Ten post edytował Kshyhoo 3.01.2012, 23:27:49
Powód edycji: [Spawnm]: hmm coś się zepsuło :P
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: 10.10.2025 - 17:29