Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

In dữ liệu hierachical trong một dạng cha mẹ danh sách không có thứ tự php?

OK khi làm việc từ phần phụ trợ đối với giao diện người dùng ...

Bạn có thể gọi một thủ tục được lưu trữ không đệ quy duy nhất (Spc) từ tập lệnh php của bạn để tạo ra hệ thống phân cấp thông báo cho bạn. Ưu điểm của phương pháp này là bạn chỉ cần thực hiện một DUY NHẤT gọi từ php đến cơ sở dữ liệu của bạn trong khi nếu bạn sử dụng SQL nội tuyến thì bạn sẽ thực hiện nhiều lệnh gọi theo cấp độ (ở mức tối thiểu). Một ưu điểm khác là vì nó là một chương trình mầm non đệ quy nên nó cực kỳ hiệu quả và nó cũng giữ cho mã php của bạn đẹp và sạch sẽ. Cuối cùng, tôi phải nói điều này cho hồ sơ, rằng việc gọi các thủ tục được lưu trữ an toàn hơn và hiệu quả hơn bất kỳ phương pháp nào khác bởi vì bạn chỉ cần CẤP quyền thực thi cho người dùng ứng dụng của mình và các thủ tục được lưu trữ yêu cầu ít vòng lặp đến cơ sở dữ liệu hơn bất kỳ phương thức nào các phương thức khác bao gồm các truy vấn được tham số hóa yêu cầu ít nhất 2 lệnh gọi cho một truy vấn (1 để thiết lập mẫu truy vấn trong db, phương thức còn lại để điền các tham số)

Vì vậy, đây là cách bạn gọi thủ tục được lưu trữ từ dòng lệnh MySQL.

call message_hier(1);

và đây là bộ kết quả mà nó tạo ra.

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

Được rồi, vậy bây giờ chúng ta có khả năng tìm nạp một cây thông báo đầy đủ hoặc một phần bằng cách chỉ cần gọi tên mã của chúng ta với bất kỳ nút bắt đầu nào mà chúng ta yêu cầu nhưng chúng ta sẽ làm gì với tập kết quả ??

Trong ví dụ này, tôi đã quyết định chúng ta sẽ tạo một DOM XML với nó, sau đó tất cả những gì tôi cần làm là chuyển đổi (XSLT) XML và chúng ta sẽ có một trang web thông báo lồng nhau.

Tập lệnh PHP

Tập lệnh php khá đơn giản, nó chỉ kết nối với cơ sở dữ liệu, gọi chương trình mầm non và lặp lại tập kết quả để xây dựng XML DOM. Hãy nhớ rằng chúng tôi chỉ gọi vào db một lần.

<?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();
?>

Đầu ra XML

Đây là XML mà tập lệnh php tạo ra. Nếu bạn lưu XML này trong một tệp và mở nó trong trình duyệt của mình, bạn sẽ có thể mở rộng và thu gọn các cấp.

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

Bây giờ bạn có thể bỏ qua việc xây dựng XML DOM và sử dụng XSL để hiển thị một trang web nếu bạn muốn và có lẽ chỉ cần lặp lại tập kết quả và hiển thị trực tiếp các thông báo. Tôi chỉ đơn giản là chọn phương pháp này để làm cho ví dụ của tôi toàn diện và nhiều thông tin nhất có thể.

Tập lệnh MySQL

Đây là một tập lệnh hoàn chỉnh bao gồm bảng, mầm và dữ liệu thử nghiệm.

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);

Nguồn đầy đủ cho câu trả lời này có thể được tìm thấy tại đây: http://pastie.org/1336407 . Như bạn đã lưu ý, tôi đã bỏ qua XSLT nhưng có thể bạn sẽ không đi theo lộ trình XML và nếu bạn làm vậy thì có rất nhiều ví dụ trên web.

Hy vọng bạn thấy điều này hữu ích :)

CHỈNH SỬA:

Đã thêm một ít dữ liệu hơn để bạn có nhiều hơn một thư gốc (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

Bây giờ nếu bạn muốn chỉ nhận được các thông báo cụ thể cho một nút gốc (thông báo bắt đầu), bạn có thể gọi thủ tục được lưu trữ ban đầu chuyển vào msg_id bắt đầu của nút gốc mà bạn yêu cầu. Sử dụng dữ liệu mới ở trên sẽ là 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

bạn có thể chuyển vào bất kỳ msg_id nào bạn muốn, nếu tôi muốn tất cả các thông báo bên dưới msg 1-2-2-1 thì bạn sẽ chuyển trong msg_id =6:

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

Tuy nhiên, nếu bạn muốn tất cả các thông báo cho tất cả các rễ thì bạn có thể gọi là mầm mới mà tôi đã tạo như sau:

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

Vấn đề chính với điều này là khi bảng thông báo của bạn phát triển, nó sẽ trả lại nhiều dữ liệu, đó là lý do tại sao tôi tập trung vào một mầm cụ thể hơn chỉ tìm nạp các thông báo cho một nút gốc nhất định hoặc bắt đầu msg_id.

Tôi sẽ không đăng mã mầm mới vì nó hầu như giống với mã gốc nhưng bạn có thể tìm thấy tất cả các sửa đổi tại đây: http://pastie.org/1339618

Thay đổi cuối cùng mà bạn cần thực hiện là trong tập lệnh php mà bây giờ sẽ gọi là chương trình mầm non mới như sau:

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

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

Hy vọng điều này sẽ giúp :)

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. Cách hoạt động của hàm EXPORT_SET () trong MySQL

  2. Tìm vị trí số trong chuỗi

  3. Thêm các trường bí danh MySQL với nhau

  4. Thay thế cho bảng tạm thời MySQL trong Oracle

  5. Sự cố với MySQL cho Visual Studio / Visual Studio 2017 Datasource Wizard. Tham chiếu đối tượng không được đặt thành một phiên bản của đối tượng