sql >> データベース >  >> RDS >> Mysql

親子フォームの順序なしリストphpで階層データを印刷しますか?

    バックエンドからフロントエンドに向かって作業します...

    メッセージ階層を生成するphpスクリプトから単一の非再帰的ストアドプロシージャ(sproc)を呼び出すことができます。このアプローチの利点は、シングルを作成するだけでよいことです。 phpからデータベースへの呼び出し。インラインSQLを使用する場合は、レベルと同じ数の呼び出しを行うことになります(少なくとも)。もう1つの利点は、非再帰的なsprocであるため、非常にパフォーマンスが高く、phpコードをきれいに保つことです。最後に、これを記録として言う必要があります。ストアドプロシージャの呼び出しは、他のどの方法よりも安全で効率的です。これは、アプリユーザーに実行権限を付与するだけでよく、ストアドプロシージャは、データベースへのラウンドトリップが他のどの方法よりも少ないためです。 1つのクエリに対して少なくとも2回の呼び出しを必要とするパラメータ化されたクエリを含む他のメソッド(1つはデータベースにクエリテンプレートを設定し、もう1つはパラメータを設定します)

    したがって、MySQLコマンドラインからストアドプロシージャを呼び出す方法は次のとおりです。

    call message_hier(1);
    

    これが作成される結果セットです。

    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
    

    これで、必要な開始ノードを使用してsprocを呼び出すだけで、メッセージツリー全体または部分をフェッチできるようになりましたが、結果セットをどうするのでしょうか??

    この例では、それを使用してXML DOMを生成することにしました。次に、XMLを変換(XSLT)するだけで、ネストされたメッセージのWebページが作成されます。

    PHPスクリプト

    phpスクリプトは非常に単純で、データベースに接続し、sprocを呼び出し、結果セットをループしてXMLDOMを構築します。 dbを呼び出すのは1回だけであることを忘れないでください。

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

    XML出力

    これは、phpスクリプトが生成するXMLです。このXMLをファイルに保存してブラウザで開くと、レベルを展開したり折りたたんだりできるようになります。

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

    これで、必要に応じてXML DOMを構築し、XSLを使用してWebページをレンダリングするのをやめ、結果セットをループしてメッセージを直接レンダリングすることができます。例をできるだけ包括的で有益なものにするために、この方法を選択しただけです。

    MySQLスクリプト

    これは、テーブル、sproc、テストデータを含む完全なスクリプトです。

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

    この回答の完全なソースはここにあります: http://pastie.org/1336407 。すでにお気づきのとおり、私はXSLTを省略しましたが、おそらくXMLルートに進むことはなく、そうすると、Web上に多数の例があります。

    これがお役に立てば幸いです:)

    編集:

    複数のルートメッセージ(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
    

    これで、ルートノードに固有のメッセージ(開始メッセージ)だけを取得したい場合は、必要なルートの開始msg_idを渡して元のストアドプロシージャを呼び出すことができます。上記の新しいデータを使用すると、msg_ids1,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
    

    任意のmsg_idを渡すことができるので、msg 1-2-2-1の下のすべてのメッセージが必要な場合は、msg_id=6を渡すことになります。

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

    ただし、すべてのルートのすべてのメッセージが必要な場合は、次のように作成したこの新しいsprocを呼び出すことができます。

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

    これに関する主な問題は、メッセージテーブルが大きくなると、大量のデータが返されることです。そのため、特定のルートノードのメッセージのみをフェッチするかmsg_idを開始するより具体的なsprocに焦点を当てていました。

    新しいsprocコードは元のコードと実質的に同じであるため、投稿しませんが、すべての修正をここで見つけることができます: http: //pastie.org/1339618

    行う必要のある最後の変更は、次のように新しいsprocを呼び出すphpスクリプトにあります。

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

    これがお役に立てば幸いです:)

    call message_hier_all();
    


    1. SQLServerでのデータ圧縮の節約を見積もる

    2. Sequelize:WHERELIKE句のフィールドを連結します

    3. このテキストファイルを読み取ってMySQLに挿入するにはどうすればよいですか?

    4. SQLServerの行レベルのセキュリティの概要