Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   PHP, PERL, MySQL, JavaScript (https://forum.antichat.xyz/forumdisplay.php?f=37)
-   -   как грамотно выбрать дерево из БД? (https://forum.antichat.xyz/showthread.php?t=105031)

Дикс 06.02.2009 23:02

как грамотно выбрать дерево из БД?
 
есть таблица с комментариями, у некоторых комментариев так называемый 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)
   
то выбираем к нему все комментарии второго уровня



в результате мы делаем тока два запроса (может можно и один сделать через иннер джоин?)
и потом выводим средставми пхп в нужном порядке.

.:EnoT:. 07.02.2009 01:05

Совершенно не понял про массив 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;
        }
    }
}           

?>


AkyHa_MaTaTa 07.02.2009 04:24

Имхо для таких ситуаций использования join(и не обязательно инер, для той структуры даных вполне можно и left) являеться более рациональным решением чем прибегать к нескольким запросам и тем более создавать какие то непонятные рекурсии на php которые при порядочном объеме данных и частых запросах могут сыграть злую шутку с сервером.l

Shadow_p1raT 07.02.2009 06:37

.: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

по поводу интвал - что за странная проверка?
я всегда использовал 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
ну а как, как его использовать?

AkyHa_MaTaTa 07.02.2009 17:14

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

нужны два запроса:
первый выдирает все нужные комменты с parent_id=null
второй все, у которых в parent_id - айдишники уже выбранных.
PHP код:

select from comments where parent_id=null limit 010
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 010 

выдаёт какую-то хрень с кучей одноименных столбцов.
прежде чем копать дальше, хочу узнать - реально ли вообще объединить те два запроса в один?

AkyHa_MaTaTa 07.02.2009 18:49

В даном примере тебе не нужен 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 значений), не совсем понятна мне структура данных которые тебе необходимо выбрать.

Pashkela 07.02.2009 18:59

PHP код:

select from comments where parent_id=null limit 010 
select 
from comments where parent_id in(список id из первого запроса

а вот так конкретно это одним запросом:

PHP код:

select from comments where parent_id=null limit 010 

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

Helios 07.02.2009 20:14

Как уже отмечали, средств для рекурсивных запросов в мускуле нет, но выбрать два уровня записей из дерева одним обращением к БД выбрать можно. Запрос будет выглядеть так:

Код:

SELECT *
FROM `comments`
WHERE `parent_id` = NULL
OR `parent_id` IN (
    SELECT `id` FROM `comments` WHERE `parent_id` = NULL
)

Подзапросом мы вибираем идентификаторы тех элементов, которые не имеют родительских элементов, а основным запросом мы вибираем все те же корневые элементы и все их элементы-потомки.

Проблему с несколькими обращениями к БД мы решили, теперь остается только придумать как обрабатывать полученные данные.


Время: 03:05