Php 如何在一行中显示包含其他表数据的汇总表?

Php 如何在一行中显示包含其他表数据的汇总表?,php,mysql,sql,database-design,Php,Mysql,Sql,Database Design,我有以下MySQL方案: Method table: PK id, name, comment Okp table: PK id, FK method_id, comment Tnved table: PK id, FK method_id, comment Okp --many to one--> Method <-- many to one-- Tnved 我尝试过使用SQLJOIN,但它看起来有多个冗余。所以我不知道如何更好地使用查询 我用PHP解决了这个问题,通过单

我有以下MySQL方案:

Method table: PK id, name, comment
Okp table: PK id, FK method_id, comment
Tnved table: PK id, FK method_id, comment

Okp --many to one-->  Method   <-- many to one-- Tnved
我尝试过使用SQLJOIN,但它看起来有多个冗余。所以我不知道如何更好地使用查询

我用PHP解决了这个问题,通过单独的查询接收每行的相关数据,但是这个解决方案太慢了。(事实上,我有1000多行)

那么如何查询和显示这些数据呢

我使用以下方法从DB获取信息:

//Model-file pseudo-code
$result = array();
$methods = $this->db
    ->select('*')
    ->from('method')
    ->get()
    ->result();
$i = 0;
foreach ($methods as $method){
    $result[$i]['method_t'] = $method;
    $result[$i]['okps'] = $this->db
        ->select('*')
        ->from('okp')
        ->where('method_id', $method['id]')
        ->get()
        ->result();
    $result[$i]['tnveds'] = $this->db
        ->select('*')
        ->from('tnved')
        ->where('method_id', $method['id]')
        ->get()
        ->result();
    //so on
    $i++;
}
我使用以下方法显示汇总表:

//View-file pseudo-code
//Table header
foreach ($result as $method) {
    echo '<tr>';
    echo '<td>' . $method['method_t']['id'] . '</td>';
    echo '<td>' . $method['method_t']['name'] . '</td>';
    echo '<td><ul>';
    foreach ($method['okps'] as $okp) {
        echo '<li>' . $okp['id'] . '</li>';
        //in fact much more data from $okp with separate template
    }
    echo '</ul></td>';
    echo '<td><ul>';
    foreach ($method['tnveds'] as $tnved) {
        echo '<li>' . $tnved['id'] . '</li>';
    }
    //in fact much more data from $tnveds with separate template
    echo '</ul></td>';
    //so on
    echo '</tr>';
}
echo '</table>';
//查看文件伪代码
//表头
foreach($result as$method){
回声';
回显“”。$method['method_t']['id'.';
回显'.$method['method_t']['name'].';
回声“
    ”; foreach($okp方法['okps']为$okp){ 回显“
  • ”.$okp['id']”。
  • ; //事实上,使用单独的模板从$okp获得更多数据 } 回声“
”; 回声“
    ”; foreach($tnveds'方法为$tnved){ 回音“
  • ”。$tnved['id']”。
  • ; } //事实上,使用单独的模板从$tnveds获得的数据要多得多 回声“
”; //诸如此类 回声'; } 回声';
也许由于我缺乏php技能,我遗漏了一些东西,但我看不出为什么不能一次获得所有记录,然后使用php来显示您想要的方式。下面是一个示例,但php只能最好地描述为伪代码,因为我对它几乎没有经验:

select
  m.id as m_id,
  m.name as m_name, 
  m.comment as m_comment,
  o.id as o_id,
  o.comment as o_comment,
  t.id as t_id, 
  t.comment as t_comment
from
  method m
  inner join okp o
    on m.id = o.method_id
  inner join tnved t
    on m.id = t.method_id
order by m.id, o.id, t.id;
对于php,如下所示。我省略了tvned的内容,因为您可以通过复制okp部件的模型来添加它

$is_first=true;
$m_id_last = 0;
$m_id = 0;
$o_id_last = 0;
$o_id = 0;
$o_str = "";
foreach ($result as $method) {
    $m_id_last = $m_id;
    $m_id = $method['m_id'];
    if ((!is_first) && ($m_id_last != $m_id)) {
        echo '<tr>';
        echo '<td>' . $m_id . '</td>';
        echo '<td>' . $method['name'] . '</td>';
        echo '<td><ul>';

        echo o_str;

        echo '</ul></td>';
        echo '</tr>';

        $o_str = "";
    }
    $o_str .= '<li>' . $method['o_id'] . '</li>';
    $is_first=false;
}
$is_first=true;
$m_id_last=0;
$m_id=0;
$o_id_last=0;
$o_id=0;
$o_str=“”;
foreach($result as$method){
$m_id_last=$m_id;
$m_id=$method['m_id'];
如果((!is_first)&($m_id_last!=$m_id)){
回声';
回显“.$m_id.”;
回显'.$method['name'].';
回声“
    ”; 埃科奥乌街; 回声“
”; 回声'; $o_str=“”; } $o_str.='
  • 。$method['o_id'].
  • ; $is_first=false; }
    为什么不使用单个SQL查询,但在使用
    内部联接时要小心。
    因为在okp表中可以有与方法#1相关的行,但在tnved表中没有与方法#1相关的行,反之亦然

    注意:在下面的查询和php代码中,我对okp和tnved表使用了名称列而不是注释列

    因此,SQL应该是这样的:

        Methods summary
    +-----+-----------+--------------+---------------+-----+---------+
    | id  | name      | All OKP data | All TNVED data| ... | Comment |
    +-----+-----------+--------------+---------------+-----+---------+
    | 1   | Cloth 1   | 841000       | 5007000000    | ... | Special |
    |     |           | 842000       | 5111000000    |     |         |
    |     |           | 843000       | 5112000000    |     |         |
    |     |           | 844000       | ... much more |     |         |
    |     |           | ...much more |               |     |         |
    +-----+-----------+--------------+---------------+-----+---------+
    | 2   | Game 76   | 259000       | 6100000000    | ... | Nice    |
    |     |           | 816700       | 6200000000    |     |         |
    |     |           | 880000       | 6400000000    |     |         |
    |     |           | ...much more | ...much more  |     |         |
    +-----+-----------+--------------+---------------+-----+---------+
    | ... | ...       | ...          | ...           |     | ...     |
    +-----+-----------+--------------+---------------+-----+---------+
    | 999 | T-shirt 3 | 831701       | 6302600000    | ... | Bad     |
    +-----+-----------+--------------+---------------+-----+---------+
    
    SELECT m.id, o.id AS oid, o.name AS oname, t.id AS tid, t.name as tname, m.comment 
    FROM 
    (method AS m LEFT JOIN okp AS o ON m.id = o.method_id) 
    LEFT JOIN tnved AS t ON m.id = t.method_id
    ORDER BY m.id
    
    执行上述查询后,您将获得如下smth:

    接下来,使用PHP迭代查询结果行:

    $output = array();
    foreach($results AS $id => $method) {
    
        $output[$id]['okps']   = array();
        $output[$id]['tnveds'] = array();
    
        if(!is_null($method['oid'])) {
            $output[$id]['okps'][$method['oid']] = array(
                'name' => $method['oname'];
            );
        };
    
        if(!is_null($method['tid'])) {
            $output[$id]['tnveds'][$method['tid']] = array(
                'name' => $method['tname'];
            );
        } 
    }
    
    下一个渲染
    $output
    数组,其中
    $output
    数组键是方法id


    上面的php代码也可以重构。我提供了基本的例子。

    基本问题是半笛卡尔积。如果“okp”(对于给定的m_id)中有五行,而“tnved”中有六行,那么一个连接操作将把“okp”中的每一行与“tnved”中的每一行匹配,总共三十行。每“okp”行六份,每“tnved”行五份

    如果联接涉及三个、四个、五个表,并且每个表都有十几行或更多行。。。由此产生的重复的聚集变得难以处理


    有几种方法可以解决这个问题

    如果我要对“method”运行一个查询,并在一个循环中处理来自的每一行,一次一个“m_id”

    对于“method”中的每一行,我将执行另一个查询,通过将单个查询与UNION all组合,从“okp”、“tnved”等中获取所有行。这将消除行的重复。通过定义适当的索引,例如“…on okp(m_id,id)”,查询应该非常高效

    例如:

      SELECT okp.m_id     AS m_id
           , 'okp'        AS src
           , okp.id       AS id
           , okp.comment  AS comment  
        FROM okp
       WHERE okp.m_id = ?
       ORDER BY okp.m_id, okp.id
    
       UNION ALL
    
       SELECT tnved.m_id    AS m_id
            , 'tnved'        AS src
            , tnved.id       AS id
            , tnved.comment  AS comment
        WHERE tnved.m_id = ?
        ORDER BY tnved.m_id, tnved.id
    
    我们使用UNION ALL而不是UNION,因此结果只是连接在一起,避免了排序和删除重复行的开销

    注意,我们包括一个“src”鉴别器列。我们的代码可以使用此列中的值来确定哪个查询返回了它。在本例中,每个查询都来自一个表,因此我们只需确定值来自哪个表

    如果其中一个表要返回的列比其他表多,我们将向其他查询的选择列表中添加“伪”表达式。这使我们能够满足UNION ALL的要求,其中所有被合并的集合必须具有相同的列数和相同的数据类型

    这种方法消除了我们用半笛卡尔积得到的大量重复。这种方法需要对我们处理的每个m_id进行“额外”查询,但是如果我们在一个页面上只放置20个m_id,那么只需要运行20个查询


    至于这个php。。。执行查询,为每个绑定占位符提供m_id值。获取结果,并将每一行放入相应的(空的)数组中。从“okc”到$okc[]数组的行,从“tnved”到$tnved[]数组的行

    看起来该设置适用于当前的“输出”代码

    只要处理数量有限的m_id,并且有适当的可用索引,这种方法就可以相当有效。

    循环中的基本“控制中断”处理。。。按m的顺序处理行。如果当前行用于相同的m(此m_id=以前的m_id),则跳过输出有关新m的信息。当我们有一个新的m时,只输出关于m的信息。这是一种常见的方法。(我没有深入研究答案中提供的php。)+10。