Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> MySQL Relacje - jak sformułować zapytanie?
rthwh
post 4.04.2013, 19:43:58
Post #1





Grupa: Zarejestrowani
Postów: 5
Pomógł: 0
Dołączył: 3.04.2013

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


Witam!

Ucząc się PHP i baz danych MySQL postanowiłem napisać aplikację zarządzania aktualnościami na stornie. Problem polega na tym, że nie wiem jak uzyskać dane z bazy danych najlepiej w 1 zapytaniu albo w jaki kolwiek sposób.

3 tabele relacja wiele do wielu, (kazdy article moze miec kilka kategori, każda kategoria może mieć kilka article). Nie wiem jak wyświetlić liste newsów (article) oraz kategorie do których należą bez powtarzania w liście tego samego Newsa z tym samym id
np w takiej formie

1. artykul 1, kategoria: kat_A, kat_B
2. artykul 2, kategoria: kat_A

Schemat bazy
  1. CREATE TABLE IF NOT EXISTS `article` (
  2. `id_article` smallint(6) NOT NULL AUTO_INCREMENT,
  3. `title` text COLLATE utf8_polish_ci,
  4. `content` text COLLATE utf8_polish_ci,
  5. `author` tinyint(4) DEFAULT NULL,
  6. `permission` tinyint(4) NOT NULL,
  7. `date` date DEFAULT NULL,
  8. PRIMARY KEY (`id_article`),
  9. KEY `author` (`author`)
  10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci AUTO_INCREMENT=17 ;
  11.  
  12. CREATE TABLE IF NOT EXISTS `article_category` (
  13. `id` bigint(11) NOT NULL AUTO_INCREMENT,
  14. `id_article` smallint(6) NOT NULL,
  15. `id_category` smallint(6) NOT NULL,
  16. PRIMARY KEY (`id`),
  17. KEY `id_article` (`id_article`),
  18. KEY `id_category` (`id_category`)
  19. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci AUTO_INCREMENT=13 ;
  20. ALTER TABLE `article_category`
  21. ADD CONSTRAINT `article_category_ibfk_1` FOREIGN KEY (`id_article`) REFERENCES `article` (`id_article`) ON DELETE CASCADE ON UPDATE CASCADE,
  22. ADD CONSTRAINT `article_category_ibfk_2` FOREIGN KEY (`id_category`) REFERENCES `category` (`id_category`) ON DELETE CASCADE ON UPDATE CASCADE;
  23.  
  24.  
  25. CREATE TABLE IF NOT EXISTS `category` (
  26. `id_category` smallint(6) NOT NULL AUTO_INCREMENT,
  27. `name` text COLLATE utf8_polish_ci NOT NULL,
  28. PRIMARY KEY (`id_category`)
  29. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci AUTO_INCREMENT=3 ;
  30.  


Metoda odpowiedzialna za pobieranie danych z MySQL - nie wiem jak sformuować zapytanie do mySQL
  1. public function showAllNews(){
  2. try
  3. {
  4. $stmt = $this->pdo->prepare("SELECT u.name AS name_user, a.title, a.id_article, a.permission, a.date,
  5. (SELECT GROUP_CONCAT(c.name) FROM category c, article_category ac
  6. WHERE c.id_category = ac.id_category
  7. AND a.id_article = ac.id_article) AS name_category
  8. FROM users u, category c, article a, article_category ac
  9. WHERE u.id_users = a.author
  10. AND ac.id_article = a.id_article
  11. AND ac.id_category = c.id_category");
  12.  
  13. $stmt->execute();
  14. }catch(PDOException $e){
  15. echo 'MYSQLDB ERROR CODE: LIST NEWS!!!';
  16. echo $e->getMessage();
  17. die();
  18. }
  19. if($stmt->rowCount() > 0){
  20.  
  21. $this->_news = $stmt->fetchAll(PDO::FETCH_ASSOC);
  22. //print_r($this->_news);
  23. }else{
  24. $this->_news = array();
  25. }
  26. return $this->_news;
  27. }


oraz plik template odpowiedzialny za wyświetlenie listy:
  1. <?php
  2. $lp = 1;
  3. if(!empty($this->_data)){
  4. foreach($this->_data as $row){
  5. echo $lp.' Tytuł: '.$row['title'].' kategoria: '.$row['name_category'].' Autor: '.$row['name_user'].' Data: '.$row['date'].' <a href="?action=news&mod=edit&id='.$row['id_article'].'"> EDYTUJ</a><br />' ;
  6. $lp++;
  7. }
  8. }else{
  9. echo 'Brak aktualnosci!';
  10. }
  11. ?>


Obecnie wynik wyglada nastepująco:
1 Tytuł: test kategoria: robocza Autor: Administrator Data: 2013-04-01 EDYTUJ
2 Tytuł: teswt 2 kategoria: testowa,robocza Autor: Administrator Data: 2013-04-01 EDYTUJ
3 Tytuł: teswt 2 kategoria: testowa,robocza Autor: Administrator Data: 2013-04-01 EDYTUJ

jak widać pozycja nr 2,3 jest to ten sam artykul...

Z góry dziękuję za odpowiedz, pozdrawiam.

Witam. Udało mi się samemu rozwiązać mój problem, nawet na 2 sposoby. Chce żebyście powiedzieli mi. który jest lepszy (poprawny) bo oba działają.

1 sposób
  1. $stmt = $this->pdo->prepare("SELECT a.title, a.id_article, a.permission, a.date, GROUP_CONCAT( c.name
  2. SEPARATOR ',' ) AS `name_category`
  3. FROM article a, category c, article_category ac WHERE ac.id_article = a.id_article
  4. AND ac.id_category = c.id_category
  5. GROUP BY a.id_article");


2 sposób
  1. $stmt = $this->pdo->prepare("SELECT a.title, a.id_article, a.permission, a.date, GROUP_CONCAT( category.name
  2. SEPARATOR ',' ) AS `name_category`
  3. FROM article a LEFT JOIN article_category ON article_category.id_article = a.id_article
  4. LEFT JOIN category ON category.id_category = article_category.id_category
  5. GROUP BY a.id_article");


Nie wiem po prostu kiedy użyć JOIN LEFT a kiedy je pominąć jak w 1 sposobie. Definicję JOIN (LEFT RIGHT) oczywiście znam ale jescze nie potrafię wykorzystać tego w praktyce.

Pozdrawiam, rthwh

Ten post edytował rthwh 3.04.2013, 11:50:35
Go to the top of the page
+Quote Post
Indeo
post 10.04.2013, 10:11:27
Post #2





Grupa: Zarejestrowani
Postów: 295
Pomógł: 7
Dołączył: 26.03.2004
Skąd: Opole

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


W mysql istnieje szybsza i czytelniejsza składnia definiowania JOIN'ów - z uzyciem słowa "using" - można je stostować jesli obie tabele łączysz jednym kluczem o tej samej nazwie.
Zamiast:
  1. SELECT a.title, a.id_article, a.permission, a.date, GROUP_CONCAT( category.name
  2. SEPARATOR ',' ) AS `name_category`
  3. FROM article a LEFT JOIN article_category ON article_category.id_article = a.id_article
  4. LEFT JOIN category ON category.id_category = article_category.id_category
  5. GROUP BY a.id_article


może być:
  1. SELECT a.title, a.id_article, a.permission, a.date, GROUP_CONCAT( category.name
  2. SEPARATOR ',' ) AS `name_category`
  3. FROM article a LEFT JOIN article_category USING(id_article)
  4. LEFT JOIN category USING(id_category)
  5. GROUP BY a.id_article



Pozdrawiam wink.gif

Twój drugi sposób z joinem jest lepszy, bo przy łączeniu wiekszej liczby tabel łatwiej sie połapać.
Left join używasz kiedy chcesz żeby zapytanie zwróciło również artykuły bez przypisanej kategorii (zamiast kategorii zwróci null).
Jeśli z góry wiesz, że każdemu artykułowi musi być przypisana kategoria to uzyjesz INNER JOIN.
Trzeci sposób to wykorzystanie podzapytania. Ale z tego co wiem joiny działają szybciej.


--------------------
Go to the top of the page
+Quote Post
rthwh
post 14.04.2013, 20:14:37
Post #3





Grupa: Zarejestrowani
Postów: 5
Pomógł: 0
Dołączył: 3.04.2013

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


Okej wszystko już działa dzięki za odpowiedz.
Teraz mam kolejny problem.
Jak wczesniej wspomniałem istnieje tabela article_category relacje miedzy article i category. Problem polega na tym kiedy użytkownik edytuje jakis artykuł i chce zrobic:
a)pozostawić bez zmian kategorie
b)zmienić kategorie
c)odjąć kategorie
d)dodać kategorie

w każdym z tych 4 przypadków robię to następująco:
1 - usuwam wszystkie wpisy w tabeli article_category gdzie id_article = id_article (przekazane przez POST)
2 - tworze od nowa relacje arrtykułu z kategoriami

Nie dokońca jestem przekonany, że to jest dobre roziwązanie (usuwanie potem dodawanie) zamiast edytowac przez UPDATE.
Problem z UPDATE jest wtedy gdy użytkownik edytując post usunie jakies kategorie. Zostaną zaktualizowane wpisy w tabeli ale zostana stare ... krótko mówiąc nie potrafie i nie wiem jak sformuować zapytanie do MySQL.

Pozdrawiam, rthwh.
Go to the top of the page
+Quote Post

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

 



RSS Wersja Lo-Fi Aktualny czas: 25.04.2025 - 05:37