Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Wydrukować dane hierarchiczne w postaci nadrzędnej potomnej z nieuporządkowanej listy php?

OK, pracuję od backendu do frontendu...

Możesz wywołać pojedynczą nierekurencyjną procedurę składowaną (sproc) ze swojego skryptu php, która generuje dla Ciebie hierarchię wiadomości. Zaletą tego podejścia jest to, że wystarczy zrobić POJEDYNCZY wywołanie z php do bazy danych, podczas gdy jeśli użyjesz wbudowanego SQL, wykonasz tyle wywołań, ile jest poziomów (przynajmniej). Kolejną zaletą jest to, że ponieważ jest to nierekurencyjny sproc, jest niezwykle wydajny, a także utrzymuje ładny i czysty kod php. Na koniec muszę powiedzieć to dla przypomnienia, że ​​wywoływanie procedur składowanych jest bezpieczniejsze i wydajniejsze niż jakakolwiek inna metoda, ponieważ wystarczy PRZYZNAĆ uprawnienia do wykonywania użytkownikowi aplikacji, a procedury składowane wymagają mniej podróży w obie strony do bazy danych niż jakakolwiek inna metoda inne metody, w tym zapytania parametryczne, które wymagają co najmniej 2 wywołań dla pojedynczego zapytania (1 do ustawienia szablonu zapytania w bazie danych, drugi do wypełnienia parametrów)

Oto jak można wywołać procedurę składowaną z wiersza poleceń MySQL.

call message_hier(1);

a oto zestaw wyników, który tworzy.

msg_id  emp_msg    parent_msg_id    parent_msg   depth
======  =======    =============    ==========   =====
1        msg 1            NULL          NULL          0
2        msg 1-1             1          msg 1         1
3        msg 1-2             1          msg 1         1
4        msg 1-2-1           3          msg 1-2       2
5        msg 1-2-2           3          msg 1-2       2
6        msg 1-2-2-1         5          msg 1-2-2     3
7        msg 1-2-2-1-1       6          msg 1-2-2-1   4
8        msg 1-2-2-1-2       6          msg 1-2-2-1   4

Ok, więc teraz mamy możliwość pobrania pełnego lub częściowego drzewa komunikatów, po prostu wywołując nasz sproc z dowolnym węzłem początkowym, którego potrzebujemy, ale co zrobimy z zestawem wyników?

Cóż, w tym przykładzie zdecydowałem, że wygenerujemy za jego pomocą XML DOM, a potem wszystko, co muszę zrobić, to przekształcić (XSLT) XML i będziemy mieli zagnieżdżoną stronę wiadomości.

Skrypt PHP

Skrypt php jest dość prosty, po prostu łączy się z bazą danych, wywołuje sproc i zapętla zestaw wyników w celu zbudowania XML DOM. Pamiętaj, że dzwonimy do bazy tylko raz.

<?php

// i am using the resultset to build an XML DOM but you can do whatever you like with it !

header("Content-type: text/xml");

$conn = new mysqli("localhost", "foo_dbo", "pass", "foo_db", 3306);

// one non-recursive db call to get the message tree !

$result = $conn->query(sprintf("call message_hier(%d)", 1));

$xml = new DomDocument;
$xpath = new DOMXpath($xml);

$msgs = $xml->createElement("messages");
$xml->appendChild($msgs);

// loop and build the DOM

while($row = $result->fetch_assoc()){

    $msg = $xml->createElement("message");
    foreach($row as $col => $val) $msg->setAttribute($col, $val); 

    if(is_null($row["parent_msg_id"])){
        $msgs->appendChild($msg);
    }
    else{
        $qry = sprintf("//*[@msg_id = '%d']", $row["parent_msg_id"]);
        $parent = $xpath->query($qry)->item(0);
        if(!is_null($parent)) $parent->appendChild($msg);
    }
}
$result->close();
$conn->close();

echo $xml->saveXML();
?>

Wyjście XML

To jest kod XML generowany przez skrypt php. Jeśli zapiszesz ten kod XML w pliku i otworzysz go w przeglądarce, będziesz mógł rozwijać i zwijać poziomy.

<messages>
    <message msg_id="1" emp_msg="msg 1" parent_msg_id="" parent_msg="" depth="0">
        <message msg_id="2" emp_msg="msg 1-1" parent_msg_id="1" parent_msg="msg 1" depth="1"/>
        <message msg_id="3" emp_msg="msg 1-2" parent_msg_id="1" parent_msg="msg 1" depth="1">
            <message msg_id="4" emp_msg="msg 1-2-1" parent_msg_id="3" parent_msg="msg 1-2" depth="2"/>
            <message msg_id="5" emp_msg="msg 1-2-2" parent_msg_id="3" parent_msg="msg 1-2" depth="2">
                <message msg_id="6" emp_msg="msg 1-2-2-1" parent_msg_id="5" parent_msg="msg 1-2-2" depth="3">
                    <message msg_id="7" emp_msg="msg 1-2-2-1-1" parent_msg_id="6" parent_msg="msg 1-2-2-1" depth="4"/>
                    <message msg_id="8" emp_msg="msg 1-2-2-1-2" parent_msg_id="6" parent_msg="msg 1-2-2-1" depth="4"/>
                </message>
            </message>
        </message>
    </message>
</messages>

Teraz możesz zrezygnować z budowania XML DOM i używania XSL do renderowania strony internetowej, jeśli chcesz i być może po prostu zapętlić zestaw wyników i bezpośrednio renderować komunikaty. Po prostu wybrałem tę metodę, aby mój przykład był jak najbardziej wyczerpujący i pouczający.

Skrypt MySQL

Jest to kompletny skrypt zawierający tabele, sprocs i dane testowe.

drop table if exists messages;
create table messages
(
msg_id smallint unsigned not null auto_increment primary key,
msg varchar(255) not null,
parent_msg_id smallint unsigned null,
key (parent_msg_id)
)
engine = innodb;

insert into messages (msg, parent_msg_id) values
('msg 1',null), 
  ('msg 1-1',1), 
  ('msg 1-2',1), 
      ('msg 1-2-1',3), 
      ('msg 1-2-2',3), 
         ('msg 1-2-2-1',5), 
            ('msg 1-2-2-1-1',6), 
            ('msg 1-2-2-1-2',6);


drop procedure if exists message_hier;

delimiter #

create procedure message_hier
(
in p_msg_id smallint unsigned
)
begin

declare v_done tinyint unsigned default(0);
declare v_dpth smallint unsigned default(0);

create temporary table hier(
 parent_msg_id smallint unsigned, 
 msg_id smallint unsigned, 
 depth smallint unsigned
)engine = memory;

insert into hier select parent_msg_id, msg_id, v_dpth from messages where msg_id = p_msg_id;

/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */

create temporary table tmp engine=memory select * from hier;

while not v_done do

    if exists( select 1 from messages e inner join hier on e.parent_msg_id = hier.msg_id and hier.depth = v_dpth) then

        insert into hier select e.parent_msg_id, e.msg_id, v_dpth + 1 
            from messages e inner join tmp on e.parent_msg_id = tmp.msg_id and tmp.depth = v_dpth;

        set v_dpth = v_dpth + 1;            

        truncate table tmp;
        insert into tmp select * from hier where depth = v_dpth;

    else
        set v_done = 1;
    end if;

end while;

select 
 m.msg_id,
 m.msg as emp_msg,
 p.msg_id as parent_msg_id,
 p.msg as parent_msg,
 hier.depth
from 
 hier
inner join messages m on hier.msg_id = m.msg_id
left outer join messages p on hier.parent_msg_id = p.msg_id;

drop temporary table if exists hier;
drop temporary table if exists tmp;

end #

delimiter ;

-- call this sproc from your php

call message_hier(1);

Pełne źródło tej odpowiedzi można znaleźć tutaj:http://pastie.org/1336407 . Jak już zauważyłeś, pominąłem XSLT, ale prawdopodobnie nie pójdziesz drogą XML, a jeśli to zrobisz, w sieci jest mnóstwo przykładów.

Mam nadzieję, że okaże się to pomocne :)

EDYTUJ:

Dodano trochę więcej danych, dzięki czemu masz więcej niż jedną wiadomość główną (msg_ids 1,9,14).

truncate table messages;

insert into messages (msg, parent_msg_id) values
('msg 1',null), -- msg_id = 1
  ('msg 1-1',1), 
  ('msg 1-2',1), 
      ('msg 1-2-1',3), 
      ('msg 1-2-2',3), 
         ('msg 1-2-2-1',5), 
            ('msg 1-2-2-1-1',6), 
            ('msg 1-2-2-1-2',6),
('msg 2',null), -- msg_id = 9
    ('msg 2-1',9), 
    ('msg 2-2',9), 
    ('msg 2-3',9), 
        ('msg 2-3-1',12),
('msg 3',null); -- msg_id = 14

Teraz, jeśli chcesz po prostu uzyskać komunikaty specyficzne dla węzła root (komunikat początkowy), możesz wywołać oryginalną procedurę składowaną przekazując początkowy msg_id żądanego roota. Używając nowych danych powyżej, to msg_ids 1,9,14.

call message_hier(1); -- returns all messages belonging to msg_id = 1

call message_hier(9); -- returns all messages belonging to msg_id = 9

call message_hier(14); -- returns all messages belonging to msg_id = 14

możesz podać dowolny msg_id, więc jeśli chcę wszystkie wiadomości poniżej msg 1-2-2-1, możesz podać msg_id =6:

call message_hier(6); -- returns all messages belonging to msg_id = 6

Jednakże, jeśli chcesz wszystkie wiadomości dla wszystkich korzeni, możesz nazwać ten nowy sproc, który stworzyłem w następujący sposób:

call message_hier_all(); -- returns all messages for all roots.

Główny problem polega na tym, że wraz ze wzrostem tabeli komunikatów będzie ona zwracała dużo danych, dlatego skupiłem się na bardziej konkretnym sprocu, który pobierał komunikaty tylko dla danego węzła głównego lub rozpoczynając msg_id.

Nie opublikuję nowego kodu sproc, ponieważ jest on praktycznie taki sam jak oryginał, ale wszystkie poprawki można znaleźć tutaj:http://pastie.org/1339618

Ostatnia zmiana, którą musisz wprowadzić, znajduje się w skrypcie php, który teraz wywoła nowy sproc w następujący sposób:

//$result = $conn->query(sprintf("call message_hier(%d)", 1)); // recommended call

$result = $conn->query("call message_hier_all()"); // new sproc call

Mam nadzieję, że to pomoże :)

call message_hier_all();


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Samouczek MySQL — konfiguracja i zarządzanie SSL na serwerze MySQL

  2. SQLDependency w bazie danych MariaDB/MySQL

  3. Czy możesz użyć wielu kolumn dla zapytania, którego nie ma?

  4. MySQL INSERT ... ON DUPLICATE KEY UPDATE z django 1.4 do zbiorczego wstawiania

  5. Konfiguracja CentOs PHP i MySql