Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей.
Здесь обсуждаются безопасность, программирование, технологии и многое другое.
Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
 |
|
как грамотно выбрать дерево из БД? |

06.02.2009, 23:02
|
|
Познавший АНТИЧАТ
Регистрация: 16.04.2006
Сообщений: 1,488
Провел на форуме: 2209675
Репутация:
537
|
|
как грамотно выбрать дерево из БД?
есть таблица с комментариями, у некоторых комментариев так называемый parent_id - "комментарии на комментарии".
искал решение грамотной выборки без миллиона запросов к бд.
нашёл вот это:
http://habrahabr.ru/blogs/sql/43955/
там огорчили, что в mysql нет нормальных средств для этого и типа пользуйтесь пхп, и ссылка вот сюда:
http://www.codenet.ru/webmast/php/tree.php
это понятно - нерациональный говнокод.
собственная мысль (сонный мозг уже ниче не соображает):
PHP код:
-select comments where parent_id == null
$ids = все выбранные ид, через запятую
-select comments where parent_id in($ids)
фореач comments{
если in_array(текущий ид, $parent_ids)
то выбираем к нему все комментарии второго уровня
}
в результате мы делаем тока два запроса (может можно и один сделать через иннер джоин?)
и потом выводим средставми пхп в нужном порядке.
|
|
|

07.02.2009, 01:05
|
|
Постоянный
Регистрация: 29.05.2007
Сообщений: 852
Провел на форуме: 4832771
Репутация:
1916
|
|
Совершенно не понял про массив id шников.
Но я как-то переделывал вышеупомянутую ф-цию примерно таким образом (в виде рекурсии).
Возможно какие-то ошибки допустил, писал с ходу
PHP код:
<?php
function tree($id, $parent_id=0){
$sql = mysql_query('SELECT * FROM `comments` WHERE `id` = '.intval($id).'
AND`parent_id` = '.$parent_id);
if($sql && mysql_num_rows($sql)){
while($res = mysql_fetch_object($sql)){
if($res->parent_id) return tree($id, $res->parent_id);
else return $res->text;
}
}
}
?>
Последний раз редактировалось .:EnoT:.; 20.03.2009 в 19:34..
Причина: Исправил)
|
|
|

07.02.2009, 04:24
|
|
Постоянный
Регистрация: 19.03.2007
Сообщений: 684
Провел на форуме: 3152874
Репутация:
1020
|
|
Имхо для таких ситуаций использования join(и не обязательно инер, для той структуры даных вполне можно и left) являеться более рациональным решением чем прибегать к нескольким запросам и тем более создавать какие то непонятные рекурсии на php которые при порядочном объеме данных и частых запросах могут сыграть злую шутку с сервером.l
|
|
|

07.02.2009, 06:37
|
|
Участник форума
Регистрация: 09.03.2008
Сообщений: 193
Провел на форуме: 2140897
Репутация:
267
|
|
.:EnoT:. думаю вместо intval лучше заюзать is_numeric,так как цитирую
У intval() есть интересная особенность - она возвращает TRUE если первой в аргументе содержится хотя бы одна цифра.
И у разработчиков тоже есть интересная особенность =)) -- они периодически используют intval()/(int) в логичесих условиях,
допуская непростительные ошибки.
Ведь наличие цифр в строке вовсе не гарантирует отсутствие других символов.
Пример:
/index.php?id=1'"qwerty
PHP код:
$id=$_GET['id'];
if(intval($id) && (int)$id)
{
sql_query("select $id from table_name");
}
else die('Id not integer!');
Несмотря на кажущуюся незначительность баги, встречается она в диком тырнете достаточно регулярно.
Для безопасного сравнения используйте is_numeric()
взято от сюда
_ttps://forum.antichat.ru/threadnav56756-1-10.html
|
|
|

07.02.2009, 08:16
|
|
Познавший АНТИЧАТ
Регистрация: 16.04.2006
Сообщений: 1,488
Провел на форуме: 2209675
Репутация:
537
|
|
по поводу интвал - что за странная проверка?
я всегда использовал if(intval($_GET['s']) > 0) $s = intval($_GET['s'])
Енот
ты похоже не открывал мою вторую ссылку - я же говорю что рекурсия в данном случае говнокод, потому что плодит запросы.
PHP код:
tree($id, $parent_id=false){
$parent_id = $parent_id ? $parent_id : NULL;
это ваще че?))
не проще так:
PHP код:
tree($id, $parent_id=NULL){
AkyHa_MaTaTa
ну а как, как его использовать?
Последний раз редактировалось Дикс; 07.02.2009 в 09:12..
|
|
|

07.02.2009, 17:14
|
|
Постоянный
Регистрация: 19.03.2007
Сообщений: 684
Провел на форуме: 3152874
Репутация:
1020
|
|
2 Shadow_p1raT не пойму при чем здеся is_numeric() так как у .:EnoT:. в примере идет intval($id) - которая всегда вернет int значения $id даже если оно будет отричательным тоже самое касаеться и is_numeric() оно вернет true даже при отрицатенльном значении $id, но, имхо, ты прав в другом что перед гверей лутче бы проверить данные на то что они являються число(если нам нужно число) и как сказал Дикс что оно больше чем 0, но как я понял .:EnoT:. привел в качестве примера(хотя при чем тут это я не пойму так как вопрос у Дикса немного другого характера).
2 Дикс ну думаю гуглом ты умешь пользоваться не хуже чем я, там полно примеров с использование и описание JOIN в любых вариациях, вот неплохое обьяснения использования JOIN http://www.codinghorror.com/blog/archives/000976.html.
|
|
|

07.02.2009, 18:14
|
|
Познавший АНТИЧАТ
Регистрация: 16.04.2006
Сообщений: 1,488
Провел на форуме: 2209675
Репутация:
537
|
|
нужны два запроса:
первый выдирает все нужные комменты с parent_id=null
второй все, у которых в parent_id - айдишники уже выбранных.
PHP код:
select * from comments where parent_id=null limit 0, 10
select * from comments where parent_id in(список id из первого запроса)
можно как-то объединить это в один запрос?
типа вот так:
PHP код:
select * from trazh_comment as c1 inner join trazh_comment as c2 on c1.parentid=null or c2.parentid = c1.cid limit 0, 10
выдаёт какую-то хрень с кучей одноименных столбцов.
прежде чем копать дальше, хочу узнать - реально ли вообще объединить те два запроса в один?
|
|
|

07.02.2009, 18:49
|
|
Постоянный
Регистрация: 19.03.2007
Сообщений: 684
Провел на форуме: 3152874
Репутация:
1020
|
|
В даном примере тебе не нужен JOIN так как у тебя 1 табличка, потому как я понял тебе надо что бы результат выборки одного запроса передовался в другой запрос для это легче и логичней использовать подзапрос, я не пойму что пытаешься зделать, у тебя 2 одинаковых запроса, то есть
select * from comments where parent_id=null limit 0, 10
вы берет все значения из comments parent_id =null а во втором запросе ты делаешь тоже самое
select * from comments where parent_id in(список id из первого запроса)
то есть фактически дублируешь 1 запрос, пытаешься
найти все значения comments у которых id из первого запроса, то есть с parent_id = null(вернее 10 значений), не совсем понятна мне структура данных которые тебе необходимо выбрать.
|
|
|

07.02.2009, 18:59
|
|
Динозавр
Регистрация: 10.01.2008
Сообщений: 2,841
Провел на форуме: 9220514
Репутация:
3338
|
|
PHP код:
select * from comments where parent_id=null limit 0, 10
select * from comments where parent_id in(список id из первого запроса)
а вот так конкретно это одним запросом:
PHP код:
select * from comments where parent_id=null limit 0, 10
если мне логика не изменяет. Т.е. первый же запрос делает то, что тебе надо получить типо во втором
|
|
|

07.02.2009, 20:14
|
|
Постоянный
Регистрация: 14.01.2007
Сообщений: 459
Провел на форуме: 1469995
Репутация:
589
|
|
Как уже отмечали, средств для рекурсивных запросов в мускуле нет, но выбрать два уровня записей из дерева одним обращением к БД выбрать можно. Запрос будет выглядеть так:
Код:
SELECT *
FROM `comments`
WHERE `parent_id` = NULL
OR `parent_id` IN (
SELECT `id` FROM `comments` WHERE `parent_id` = NULL
)
Подзапросом мы вибираем идентификаторы тех элементов, которые не имеют родительских элементов, а основным запросом мы вибираем все те же корневые элементы и все их элементы-потомки.
Проблему с несколькими обращениями к БД мы решили, теперь остается только придумать как обрабатывать полученные данные.
|
|
|
|
 |
|
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|