【问题标题】:MySQL best way to query the database to display a check listMySQL查询数据库的最佳方式显示检查列表
【发布时间】:2015-11-02 15:39:37
【问题描述】:

我在数据库中有 4 个表,允许我管理一种“检查表”。简而言之,对于每个 pathology,我有一大步 (process) 拆分为 tasks 的倍数。所有这些都与汇总表中的特定操作 (progress.case_id) 相关联。

database.pathology
+--------------+------------+
| id_pathology |    name    |
+--------------+------------+
|            1 | Pathology1 |
|            2 | Pathology2 |
|            3 | Pathology3 |
+--------------+------------+

database.process
+------------+----------+--------------+----------------+
| id_process |   name   | pathology_id | days_allocated |
+------------+----------+--------------+----------------+
| 1          | BigTask1 | 2            | 5              |
| 2          | BigTask2 | 2            | 3              |
| 3          | BigTask3 | 2            | 6              |
| ...        | ...      | ...          | ...            |
+------------+----------+--------------+----------------+

database.task
+---------+-------+------------+
| id_task | name  | process_id |
+---------+-------+------------+
| 1       | Task1 | 1          |
| 2       | Task2 | 1          |
| 3       | Task3 | 1          |
| 4       | Task4 | 2          |
| ...     | ...   | ...        |
+---------+-------+------------+

database.progress
+-------------+---------+---------+---------+------------+---------+
| id_progress | task_id | case_id | user_id |    date    | current |
+-------------+---------+---------+---------+------------+---------+
|           1 |       1 |     120 |       2 | 2015-11-02 |       1 |
|           2 |       2 |     120 |       2 | 2015-11-02 |       0 |
|           3 |       1 |     121 |       3 | 2015-11-02 |       1 |
+-------------+---------+---------+---------+------------+---------+

我必须展示类似的东西

我的问题是:最有效的方法是什么?

仅查询一个表(进度)以显示最多,然后再查询另一个表以获取不同进程和日期的名称是否更快?

也许联合函数更高效?

还是你觉得我的数据库结构不是更合适?

对于每种情况,我们可以有大约 50 个任务,将当前字段转换为复选框。后台脚本也在运行。它会根据剩余天数分析提供的天数,以确定此特定案例是否会出现延迟。

对于每个病例,进度表已经填满了与病例病理相关的所有任务。并且当前字段的开头总是'0'。

我已经尝试了多种方法,例如

$result = $db->prepare("SELECT DISTINCT process_id,process.name FROM task, progress,process WHERE progress.task_id = task.id_task AND task.process_id = process.id_process AND progress.case_id = ?");     
$result->execute(array($id));
foreach($result as $row)
{
  echo "<b>".$row[1]."</b><br>";
  $result = $db->prepare("SELECT name,id_task FROM task WHERE process_id = ?");     
  $result->execute(array($row[0]));
  foreach($result as $row)
  {
      echo $row[0];
      $result = $db->prepare("SELECT user_id, date, current FROM progress WHERE progress.task_id = ? AND case_id = ?");     
      $result->execute(array($row[1], $id));
      foreach($result as $row)
      {
        if($row[2] == 0)
        {echo "<input type='checkbox' />";}
        else
        {
          echo "<input type='checkbox' checked/>";
          echo "user : ".$row[0]." date : ".$row[1]."<br>";
        }            
      }          
  }

但我很确定我做得不对。我应该改变我的数据库基础设施吗?我应该使用特定的 MySQL 技巧吗?或者也许只是更高效的 PHP 处理?

【问题讨论】:

  • 嵌套查询,其中内部查询使用来自外部查询的数据,几乎总是可以更好地编写为单个 joined 查询。
  • 要从@MarcB 添加到 cmets,您应该将嵌套查询识别为软件开发反模式。当您看到这种反模式时,您应该始终寻找解决问题的替代方法。这并不是说总会有更好的方法,但如前所述,您几乎总会发现这种情况。
  • 在 MySQL 中,考虑 SET 数据类型。

标签: php mysql database performance


【解决方案1】:

就效率而言,数据库查询是您可以执行的最慢的操作之一。您可以采取任何措施来减少查询次数,这将大大有助于提高您的应用程序速度。

但更重要的是,您的应用程序需要按设计工作,这意味着开发人员需要了解正在发生的事情,数据不应该只是等待被覆盖,而初级开发人员将负责要在 3 年内保持这种状态,就不会想把头发扯下来。

快胜于慢。

慢胜于坏。

对于您的具体问题,如果可能的话,永远不要在循环内进行查询。尤其是当该循环由您从同一数据库中提取的数据控制时。这是一种代码异味,需要正确使用JOINs。

SQL Join Diagrams 的 Google 图片搜索显示了大量维恩图示例,这些示例显示了每个 JOIN 返回的不同类型的数据。如有疑问,您通常需要LEFT JOINs。

所以,让我们确定你的关系:

  • 病理学

    • 在您的结果中未使用。
    • 找到一种方法将其合并到您的查询中,因为“Pathology2”出现在您的模型中。
  • 过程

    • 以一对多关系引用病理学。每个进程可以有一个病理学,但每个病理学可以有 0 个或多个进程。
  • 任务

    • 一对多关系中的引用任务。 Task 包含 Process 的子级。
  • 进展

    • 参考任务,以及未显示的案例和用户。在引用特定案例和用户时,进度似乎是任务的详细信息。
    • 我假设存在一个业务约束,其中 task_id、case_id 和 user_id 必须是唯一的......也就是说,用户 1 只能有 1 个任务 1 和案例 100 的进度条目。
    • 除了保存任务的详细信息外,还充当任务、案例和用户之间的桥梁,为三个表提供多对多关系。由于 Task 是 Process 的直接子代,而 Process 是 Pathology 的直接子代,因此它提供了与 Pathology 的多对多关系。
  • 案例

    • 推断存在此表。
    • 由任务引用。
  • 用户

    • 推断存在此表。
    • 由任务引用。

基于此表结构,我们的主要分组将是病例、病理学和用户。

也就是说,如果您是登录用户并且想要按案例查看进度,您会希望看到以下内容:

Case 110:
    Pathology1:
        BigTask1:
            Task1: X
            Task2: []
        BigTask2:
            Task3: X
    Pathology2:
        BigTask3:
            Task4: []
Case 120:
    Pathology1:
        BigTask1: 
            Task1: []

我们希望用户 ID == 1; 我们的第一次排序将基于案例 我们的第二次排序将基于病理学 我们的第三次排序将基于 Process 我们最后的排序将在 Task...

因此,获得上述结果的数据将是:

+------+------------+----------+-------+----------+
| case | pathology  | process  | task  | progress |
+------+------------+----------+-------+----------+
| 110  | Pathology1 | BigTask1 | Task1 | 1        |
| 110  | Pathology1 | BigTask1 | Task2 | 0        |
| 110  | Pathology1 | BigTask2 | Task3 | 1        |
| 110  | Pathology2 | BigTask3 | Task4 | 0        |
| 120  | Pathology1 | BigTask1 | Task1 | 0        |
+------+------------+----------+-------+----------+

我们的 'ORDER BY' 子句是从最后到第一个...ORDER BY task, process, pathology, case...我们可以在 PHP 中对其进行排序,但数据库比我们更擅长。如果您的索引设置正确,数据库甚至可能不必对事物进行排序,它只会按顺序获取它。

获取特定用户的上述数据的查询是:

SELECT
    prog.case_id AS case,
    path.name AS pathology,
    proc.name AS process,
    task.name AS task,
    prog.current AS progress
FROM
    pathology path
LEFT JOIN process proc ON path.id_pathology = proc.pathology_id
LEFT JOIN task ON task.process_id = proc.id_process
LEFT JOIN progress prog ON task.id_task = prog.task_id
WHERE prog.user_id = :userid
ORDER BY task, process, pathology, case

你的 PHP 可能会是这样的

<?php

$sql = <<<EOSQL
SELECT
    prog.case_id AS case,
    path.name AS pathology,
    proc.name AS process,
    task.name AS task,
    prog.current AS progress
FROM
    pathology path
LEFT JOIN process proc ON path.id_pathology = proc.pathology_id
LEFT JOIN task ON task.process_id = proc.id_process
LEFT JOIN progress prog ON task.id_task = prog.task_id
WHERE prog.user_id = :userid
ORDER BY task, process, pathology, case
EOSQL;

$result = $db->prepare($sql);     
$result->execute(array(':userid' => $id));
$rows = $result->fetchAll(PDO::FETCH_ASSOC);

foreach ($rows as $row) {
    var_dump($row);
    // array(5) {
    //     ["case"]=>
    //     int(110)
    //     ["pathology"]=>
    //     string(10) "Pathology1"
    //     ["process"]=>
    //     string(8) "BigTask1"
    //     ["task"]=>
    //     string(5) "Task1"
    //     ["progress"]=>
    //     int(1)
    // }
}

【讨论】:

  • 如果您想直接显示结果集中的结果,而不必将结果放入某种中间数据结构中,那么您的 ORDER BY 列顺序似乎应该与您显示的相反允许在顶层按大小写分组。
  • @MikeBrant 也许,我没有直接运行它,而且我已经有一段时间没有写ORDER BY 子句了。我的理解是,数据库将首先按任务排序,然后按流程排序,然后按病理学,然后按案例,将所有内容放在同一个案例中。如果我的假设是错误的,那么颠倒列的顺序就很容易了。
猜你喜欢
  • 2023-03-18
  • 1970-01-01
  • 2016-11-21
  • 1970-01-01
  • 2019-04-03
  • 1970-01-01
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多