如何连续显示包含其他表数据的汇总表?

时间:2016-04-22 17:40:16

标签: 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

图像表示:

Link to image represenation

我需要从方法中显示HTML摘要表。但是每个方法(每一行)都可以包含来自字段中其他表的许多数据,我需要全部显示它们。

看起来像这样:

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

我尝试使用SQL JOIN,但它看起来很怪异,有多个冗余。所以我不知道如何更好地使用查询。

我用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>';

3 个答案:

答案 0 :(得分:1)

也许我缺少一些东西,因为我缺乏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;
}

答案 1 :(得分:0)

为什么不使用单个SQL查询,但在使用INNER JOIN时要小心。 因为您可以在 okp 表中使用方法#1相关的行,但是 tnved 表中的方法#1的任何行都没有,反之亦然。

小心:在查询和php代码中,我使用 name 列代替 comment okp < / em>和 tnved 表。

所以,SQL应该是这样的:

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

执行上面的查询后,你会像这样: https://drive.google.com/a/thepaydock.com/file/d/0B3C0Ywfdde5Fa2lPc0FBdlZENG8/view?usp=drivesdk

下一步使用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数组键是 method_id

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

答案 2 :(得分:0)

基本问题是半笛卡尔产品。如果&#34; okp&#34;有五行(对于给定的m_id)和来自&#34; tnved&#34;的六行,一个连接操作将匹配来自&#34; okp&#34;的每一行。来自&#34; tnved&#34;的每一行,总共三十行。每份六份&#34; okp&#34;行,并且每个&#34; tnved&#34;行。

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

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

如果我要针对&#34;方法&#34;运行查询,并在循环中处理每一行,则一个&#34; m_id&#34;一次......

对于&#34;方法&#34;中的每一行,我会执行另一个单一查询以从&#34; okp&#34;,&#34; tnved&#34;等获取所有行。通过将各个查询与UNION ALL组合在一起。这将消除行的重复。使用适当的索引定义,例如&#34; ...在okp(m_id,id)&#34;上,查询应该非常有效。

例如:

  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,因此结果只是连接在一起,避免了排序和删除重复行的开销。

请注意,我们包含了一个&#34; src&#34;鉴别列。我们的代码可以使用此列中的值来确定返回哪个查询。在这种情况下,每个查询都来自一个表,因此我们可以只识别值来自的表。

如果其中一个表的列数要多于其他表,我们会添加&#34; dummy&#34;表达式到其他查询的SELECT列表。这使我们能够满足UNION ALL的要求,其中所有要组合的集合必须具有相同的列数和相同的数据类型。

这种方法消除了我们使用半笛卡尔产品所带来的大量重复。这种方法需要额外的&#34;查询我们处理的每个m_id,但如果我们只在页面上放置20 m_id,那么它只能运行20个查询。

就此而言php ...执行查询,为每个绑定占位符提供m_id值。获取结果,并将每一行放入适当的(清空)数组中。来自&#34; okc&#34;的行进入$ okc []数组,来自&#34; tnved&#34;进入$ tnved []数组。

看起来该设置适用于当前&#34;输出&#34;代码。

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