【问题标题】:How to show summary table with other table data in a row?如何在一行中显示包含其他表数据的汇总表?
【发布时间】:2016-08-16 10:59:43
【问题描述】:

我有以下 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

图像表示:

我需要从方法中显示 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 多行)。

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

我正在使用以下方法从数据库中获取信息:

//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>';

【问题讨论】:

    标签: php mysql sql database-design


    【解决方案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;
    }
    

    【讨论】:

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

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

    注意:在下面的查询和 php 代码中,我使用 name 列而不是 comment 列来表示 okptnved 表。

    所以,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 代码也可以重构。我已经提供了基本示例。

    【讨论】:

      【解决方案3】:

      基本问题是半笛卡尔积。如果“okp”(对于给定的 m_id)有 5 行,“tnved”有 6 行,则连接操作将匹配“okp”中的每一行与“tnved”中的每一行,总共 30 行。每个“okp”行六个副本,每个“tnved”行五个副本。

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


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

      如果我要对“方法”运行查询,并在循环中处理每一行,一次一个“m_id”...

      对于“方法”中的每一行,我将执行另一个查询以取回“okp”、“tnved”等中的所有行。通过将单个查询与 UNION ALL 结合起来。这将消除行的重复。定义了适当的索引,例如“... 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”鉴别器列。我们的代码可以使用此列中的值来确定哪个查询返回了它。在这种情况下,每个查询都来自一个表,因此我们可以只识别值来自的表。

      如果其中一个表比其他表有更多的列要返回,我们将“虚拟”表达式添加到其他查询的 SELECT 列表中。这让我们可以满足 UNION ALL 的要求,其中所有组合的集合必须具有相同的列数和相同的数据类型。

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


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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-12
        • 2019-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多