Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Форумы портала PHP.SU :: Версия для печати :: Вопрос по объединению таблиц
Форумы портала PHP.SU » » Работа с СУБД » Вопрос по объединению таблиц

Страниц (1): [1]
 

1. teddy - 29 Июля, 2013 - 16:49:18 - перейти к сообщению
Здравствуйте. Не понимаю как себя ведет следующий SQL запрос:

PHP:
скопировать код в буфер обмена
  1. $result = $db->query("SELECT DISTINCT parentmenu.name, submenu.sub_name FROM parentmenu INNER JOIN submenu ON `submenu`.parentid = `parentmenu`.id") or die($db->error);
  2.        
  3.     while($row = $result->fetch_assoc())
  4.     {
  5.          echo $row['name']."<br />";
  6.          echo $row['sub_name'];
  7.        
  8.     }
  9.  

Тоесть я говорю в запросе, выбери мне родительское меню, дочернее меню(подменю) из таблицы, которое содержит родительское меню объединяя его с подменю на основе того, что родительский id у подменю, развен id у основного меню. Как то так.

Проблема в том, что родительское меню при выводе ДУБЛИРУЕТСЯ. Я же написал DISTINCT - а он должен прекращать дублирование. Скажите пожалуйста, почему так?

Должно выводиться так:

Фильмы
- Комедии
- Боевики

Спорт
- Плавание
- Смешанные бои

Но категории повторяются. получается так:

Фильмы
Комедии
Фильмы
Боевики

и т.д
2. EuGen - 29 Июля, 2013 - 16:58:07 - перейти к сообщению
teddy
Потому что JOIN присоединяет все строки. Он не может понять, что существует некая "вложенность" (вернее, можно сконструировать замысловатый запрос, но в Вашем случае этого не нужно) элементов.
Вам достаточно сделать JOIN, после чего правильно выводить данные в PHP

CODE (SQL):
скопировать код в буфер обмена
  1. SELECT DISTINCT parentmenu.name, submenu.sub_name FROM parentmenu INNER JOIN submenu ON `submenu`.parentid = `parentmenu`.id ORDER BY parentmenu.name

- Вы получите отсортированный массив. Всё, что нужно - это выводить заголовки (например, можно просто смотреть, не сменился ли родительский элемент, и, если да - выводить его; цикл остаётся одиночным. Можно вместо этого пройтись по массиву строк и собрать данные в двумерный массив - на Ваш вкус).
Если интересует "замысловатый" запрос, то - пример: http://forum.php.su/topic.php?fo...30376#1190130376
3. teddy - 29 Июля, 2013 - 17:03:50 - перейти к сообщению
EuGen пишет:
Вам достаточно сделать JOIN, после чего правильно выводить данные в PHP

Запрос который вы написали отличается от моего только ORDER BY - его я тоже добавил но результат не изменился...

Можно подробнее про "правильный" вывод в PHP? Я сначала вывожу родительское меню а потом дочернее вроде бы все правильно... Или может вы имели ввиду что в php есть специальная функция для работы с массивами которая удаляет дубли? Если да, то подскажите пожалуйста, какая это функция

А не, про функцию я ляпнул...ведь mysqli возвращает объект
4. EuGen - 29 Июля, 2013 - 17:07:48 - перейти к сообщению
teddy пишет:
Запрос который вы написали отличается от моего только ORDER BY - его я тоже добавил но результат не изменился...

EuGen пишет:
Потому что JOIN присоединяет все строки. Он не может понять, что существует некая "вложенность" (вернее, можно сконструировать замысловатый запрос, но в Вашем случае этого не нужно) элементов.

PHP:
скопировать код в буфер обмена
  1. $result = $db->query("SELECT DISTINCT parentmenu.name, submenu.sub_name FROM parentmenu INNER JOIN submenu ON `submenu`.parentid = `parentmenu`.id") or die($db->error);
  2.  
  3. $rgResult = [];
  4. while($row = $result->fetch_assoc())
  5. {
  6.      $rgResult[$row['name']][] = $row['sub_name'];
  7. }
  8. //var_dump($rgResult)

- как работать с rgResult, уверен, догадаетесь. Альтернативный вариант (за один проход, с проверкой, не сменился ли родитель) - оставляю Вам (но даю подсказку - в том варианте без ORDER BY уже нельзя).
5. teddy - 29 Июля, 2013 - 18:17:59 - перейти к сообщению
EuGen
Спасибо за внимание, Евгений )

Я посмотрю обязательно что да как... сходу пока не понимаю.. всегда были проблемы с JOIN-ами
(Добавление)
Не доходит в упор.

Пробовал даже по другому. Получал из объекта массив потом при помощи array_unique пытался удалить дубли тоже не выходит...

Не доходит почему не срабатывает DISTINCT что он есть что его нет одно и тоже..



В мозгах ошибка уровня notice ))
6. Panoptik - 29 Июля, 2013 - 19:57:48 - перейти к сообщению
DISTINCT работает только по одному полю, если вы в выражении SELECT указываете 2 и более полей для выборки, то все они должны быть соответственно равны другим значениям чтобы опустить повторяющие значения

к примеру наборы чисел
1 2 3
1 3 2
1 1 1
1 2 3

через дистинкт дадут

1 2 3
1 3 2
1 1 1

то есть абсолютно одинаковые результаты отсеются
(Добавление)
если нужно убрать дублирующие значения по определенным полям при этом выбрать дополнительные, то пользуйте GROUP BY
7. teddy - 29 Июля, 2013 - 20:11:12 - перейти к сообщению
Panoptik пишет:
если нужно убрать дублирующие значения по определенным полям при этом выбрать дополнительные, то пользуйте GROUP BY

Не совсем то. Я уже пробовал, но тогда подкатегории выводятся только 1 раз, а остальные не выводятся
8. EuGen - 30 Июля, 2013 - 13:12:17 - перейти к сообщению
teddy
Всё же, я так и не понял, что же именно не получается.
CODE (SQL):
скопировать код в буфер обмена
  1. mysql> SHOW CREATE TABLE tbl20130730_parent;
  2. +--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  3. | TABLE              | CREATE TABLE                                                                                                                                                                                         |
  4. +--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  5. | tbl20130730_parent | CREATE TABLE `tbl20130730_parent` (
  6.   `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  7.   `title` varchar(255) DEFAULT NULL,
  8.   PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 |
  10. +--------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  11. 1 row IN SET (0.00 sec)
  12.  
  13. mysql> SHOW CREATE TABLE tbl20130730_child;
  14. +-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  15. | TABLE             | CREATE TABLE                                                                                                                                                                                                                                                                                                                                                                                                         |
  16. +-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  17. | tbl20130730_child | CREATE TABLE `tbl20130730_child` (
  18.   `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  19.   `parent_id` int(11) UNSIGNED DEFAULT NULL,
  20.   `title` varchar(255) DEFAULT NULL,
  21.   PRIMARY KEY (`id`),
  22.   KEY `fkParentid` (`parent_id`),
  23.   CONSTRAINT `fkParentKey` FOREIGN KEY (`parent_id`) REFERENCES `tbl20130730_parent` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
  24. ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 |
  25. +-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  26. 1 row IN SET (0.00 sec)

- исходные таблицы.
CODE (SQL):
скопировать код в буфер обмена
  1. mysql> SELECT * FROM tbl20130730_parent;
  2. +----+-------+
  3. | id | title |
  4. +----+-------+
  5. |  1 | Cars  |
  6. |  2 | Motos |
  7. +----+-------+
  8. 2 rows IN SET (0.00 sec)
  9.  
  10. mysql> SELECT * FROM tbl20130730_child;
  11. +----+-----------+--------+
  12. | id | parent_id | title  |
  13. +----+-----------+--------+
  14. |  1 |         1 | BMW    |
  15. |  2 |         1 | Audi   |
  16. |  3 |         1 | Reno   |
  17. |  4 |         1 | Ford   |
  18. |  5 |         2 | H-D    |
  19. |  6 |         2 | Honda  |
  20. |  7 |         2 | Yamaha |
  21. +----+-----------+--------+
  22. 7 rows IN SET (0.00 sec)

- исходные данные
CODE (SQL):
скопировать код в буфер обмена
  1. mysql> SELECT tbl20130730_parent.title AS category, tbl20130730_child.title AS item FROM tbl20130730_parent LEFT JOIN tbl20130730_child ON tbl20130730_child.parent_id=tbl20130730_parent.id;
  2. +----------+--------+
  3. | category | item   |
  4. +----------+--------+
  5. | Cars     | BMW    |
  6. | Cars     | Audi   |
  7. | Cars     | Reno   |
  8. | Cars     | Ford   |
  9. | Motos    | H-D    |
  10. | Motos    | Honda  |
  11. | Motos    | Yamaha |
  12. +----------+--------+
  13. 7 rows IN SET (0.00 sec)

- запрос на получение категорий с элементами.
PHP:
скопировать код в буфер обмена
  1. $rConnect = new mysqli('localhost', 'user', 'password', 'test') or exit(mysqli_error());
  2. $rSelect  = $rConnect->query('SELECT tbl20130730_parent.title AS category, tbl20130730_child.title AS item FROM tbl20130730_parent LEFT JOIN tbl20130730_child ON tbl20130730_child.parent_id=tbl20130730_parent.id') or exit(mysqli_error());
  3. $rgResult = [];
  4. while($rgRow = $rSelect->fetch_array())
  5. {
  6.    $rgResult[$rgRow['category']][]=$rgRow['item'];
  7. }
  8. foreach($rgResult as $sCategory=>$rgCategory)
  9. {
  10.    echo $sCategory,'<br/>';
  11.    foreach($rgCategory as $sItem)
  12.    {
  13.       echo ' ',$sItem,'<br/>';
  14.    }
  15. }

ну и
CODE (text):
скопировать код в буфер обмена
  1. Cars
  2.  BMW
  3.  Audi
  4.  Reno
  5.  Ford
  6. Motos
  7.  H-D
  8.  Honda
  9.  Yamaha

- примерный результат.
Всё-таки вариант без сбора данных в промежуточный массив оставлю Вам.
9. teddy - 30 Июля, 2013 - 14:17:48 - перейти к сообщению
EuGen
Упс, столько написали ) Даже стыдно как то стало, что поднял эту тему )) Спасибо за ваши старания и внимание!

Вот в этом и заключается ступор ) Я хотел решить вопрос одним циклом, цикл в цикле - так уже я решал честно говоря без промежуточного массива Улыбка

Если честно мне это меню особо не нужно, у меня есть рабочее "решение", я просто взялся за этот скрипт для тренировки JOIN-ов... Получается так что вывод в PHP не столь важен, сколько хорошенько понять прицип объединения таблиц... Когда например лучше применить одно, а когда другое. там когда INNER JOIN а когда LEFT JOIN или FULL OUTER JOIN и т.д ) - из за этого я плохо понимаю что я делаю, вот и решил разобраться...


Прошу ещё обратить внимание на этот ньюанс:
CODE (SQL):
скопировать код в буфер обмена
  1. KEY `fkParentid` (`parent_id`),
  2. 23.  CONSTRAINT `fkParentKey` FOREIGN KEY (`parent_id`)
  3.  

Этот код я взял с вашего примера выше при создании таблиц. Этого я не указывал когда у себя создавал таблицы. Разве здесь нужны такие ключи? Сори если вопрос не очень корректный, в данный момент не очень хорошо разбираюсь в грамотной архитектуре MySQL. Только на базовом уровне.



Надеюсь я не надоел Улыбка
10. EuGen - 30 Июля, 2013 - 14:26:26 - перейти к сообщению
Внешние ключи нужны для контроля целостности данных на уровне СУБД (о чём свидетельствует, например, указанные мной ON DELETE SET NULL ON UPDATE CASCADE).
Если не получается именно одним циклом, то, хорошо, вот пример:
PHP:
скопировать код в буфер обмена
  1.     $rConnect = new mysqli('localhost', 'user', 'password', 'test') or exit(mysqli_error());
  2.     $rSelect  = $rConnect->query('SELECT tbl20130730_parent.title AS category, tbl20130730_child.title AS item FROM tbl20130730_parent LEFT JOIN tbl20130730_child ON tbl20130730_child.parent_id=tbl20130730_parent.id ORDER BY tbl20130730_parent.id') or exit(mysqli_error());
  3.     $sCategory= null;
  4.     while($rgRow = $rSelect->fetch_array())
  5.     {
  6.        echo $sCategory!=$rgRow['category']?($sCategory=$rgRow['category']).'<br/>':'';
  7.        echo ' ',$rgRow['item'],'<br/>';
  8.     }

- насчёт внешних ключей можете почитать мануал: http://dev[dot]mysql[dot]com/doc/refman/[dot][dot][dot]constraints[dot]html или статьи в Сети, к примеру, http://stackoverflow[dot]com/questio[dot][dot][dot]gn-keys-in-mysql
11. teddy - 30 Июля, 2013 - 14:38:34 - перейти к сообщению
EuGen
Опаньки, спасибо огромное, Евгений! ))

Отличный фокус $sCategory!=$rgRow['category']*

Не встречал ранее подобное... Доки придется читать с помощью переводчика )

Спасибо ещё раз за помощь ) о таком варианте я даже не подумал
12. EuGen - 30 Июля, 2013 - 14:41:28 - перейти к сообщению
teddy пишет:
Отличный фокус $sCategory!=$rgRow['category']*

Думаю, становится понятно, почему для работы данного приёма необходим ORDER BY
13. teddy - 30 Июля, 2013 - 15:04:22 - перейти к сообщению
EuGen
Да, конечно понятно )) спасибо ещё раз, вы мне очень помогли!

 

Powered by ExBB FM 1.0 RC1