选择所需的结果SQL。使用连接或左连接的多个表

时间:2013-01-05 12:51:31

标签: php mysql

我正在努力想出一个网络应用程序,有点像一步一步程序应用程序。我无法真正描述它并且不知道如何调用它,但它是一个应用程序,它提供了关于如何实现某些内容的逐步指令,如作业应用程序。< / p>

“帮助用户了解如何做事的应用程序。”

所以我设置了一些GUI,现在我面临问题的核心:数据库设置。

在我设计的GUI中,我认为它是这样的:

Figure 1.0

图例:

  • 绿色,检查完成
  • 橙色 - 用户当前正在执行该步骤
  • 灰色 - 他没有启动任何子步骤。

如图所示,我在每个程序下有3个顶级程序和其他一些子程序 。该图片中缺少的一件事是顶层程序也属于特定类别

我想要实现的是跟踪用户的活动。这是交易:

  1. 您没必​​要完成第一个顶级程序以继续下一个程序,您可以跳过并返回 - &gt;对此没有任何问题。
  2. 完成所有子程序后,顶级程序为该特定用户标记1,表示已完成。
  3. 同样适用于该类别,当该类别下的所有顶级程序完成后,该类别的类别标记1,表示他/她已完成该类别。
  4. 我打算像这样设置我的数据库:

    -tbl_users -

     id    |    username    |    password    |
     -----------------------------------------
       1   |    some_user   |  adf8jkdfndsa  |
    ...
    

    tbl_step_cat

     id    |      cat_name      |
     ---------------------------
       1   |    some_category   |
    ...
    

    tbl_steps

     id    |      step_shortdesc     |    step_longdesc      |  cat_id 
     -------------------------------------------------------------------
       1   |      some step one      |  do the following...  |     1
     -------------------------------------------------------------------
       2   |      some step two      |  do the following...  |     1
     -------------------------------------------------------------------
       3   |      some step three    |  do the following...  |     2
    ...
    

    tbl_substeps

     id    |     substep_shortdesc   |    substep_longdesc   |  step_id 
     -------------------------------------------------------------------
       1   |    some substep one     |  do the following...  |     1
     -------------------------------------------------------------------
       2   |    some substep two     |  do the following...  |     1
     -------------------------------------------------------------------
       3   |    some substep three   |  do the following...  |     1
     -------------------------------------------------------------------
       4   |    some substep a       |  do the following...  |     2
     -------------------------------------------------------------------
       5   |    some substep b       |  do the following...  |     2
     -------------------------------------------------------------------
       6   |    some substep 1       |  do the following...  |     3
    ...
    

    然后是用户和步骤之间的关系表

    tbl_user_stepcat

     id    |     user_id   |    stepcat_id   |  datetime 
     -------------------------------------------------------------------
       1   |      1        |       1         |  sometime
     -------------------------------------------------------------------
       2   |      1        |       2         |  sometime
     -------------------------------------------------------------------
    

    tbl_user_step

     id    |     user_id   |     step_id     |  datetime 
     -------------------------------------------------------------------
       1   |      1        |       1         |  sometime
     -------------------------------------------------------------------
       2   |      1        |       2         |  sometime
     -------------------------------------------------------------------
    

    tbl_user_substep

     id    |     user_id   |    substep_id   |  datetime 
     -------------------------------------------------------------------
       1   |      1        |       1         |  sometime
     -------------------------------------------------------------------
       2   |      1        |       2         |  sometime
     -------------------------------------------------------------------
    

    如果这有点长,我很抱歉,这只是因为代码。

    现在我的问题是如何返回我想要的结果。如您所见,当用户登录到应用程序时,我希望他/她能够立即查看这些信息。

    我从未尝试过这一刻,因为我的大脑刚关闭而且失焦,这是我迄今为止最好的时间。

    如果我要编写此应用程序的SQL代码,我将进行多个连接。

    我想首先选择所有类别并将其输出给用户。

    SELECT * FROM tbl_step_cat
    

    这将为我提供所有类别,接下来我想要做的是找出完成的步骤,以便我可以做'样式'

    我可能会这样做

    SELECT cat_name FROM tbl_step_cat JOIN
    tbl_user_stepcat ON tbl_user_stepcat.stepcat_id = tbl_step_cat.id
    ...
    

    我没有焦点,现在想不到。我该怎么做:

    1. 输出所有猫/步骤/子步骤
    2. 在关系表中获取具有条目的人,意味着已完成
    3. 其中user = session['user']
    4. 非常感谢,我只需要指导。

5 个答案:

答案 0 :(得分:5)

好的,如果您只是指定了JOIN,那将是一个内部联接,因此将排除用户尚未完成每​​个步骤和子步骤的结果。因此,为了确保您可以获得每次加入的结果,我将其设为LEFT JOIN。这可以使用null结果加入它,这很好,因为您可以测试子类别字段是否为null,以便填充表单上的刻度部分。

我一直认为将数据库逻辑拆分成单独的查询并没有什么坏处,而不是试图编写一个将所有内容连接到一切的巨型查询,这可能是过多的。

我想我会对每个类别进行单独查询,即:

(伪)

SELECT * FROM CATEGORIES 
foreach (category) {
    SELECT * FROM SUBCATEGORIES WHERE CATEGORY = CATEGORYID
    foreach (SUBCATEGORY) {
        SELECT * FROM SUBSUBCATEGORIES WHERE SUBCATEGORY = SUBCATEGORYID
    }
}

在每个点上查询用户表,以查看用户是否在遍历列表时完成了每个步骤,并使用该循环填充您的刻度。

它可能不如单个超级联盟那么优雅,但如果这不是你的事情,我不会看到简化查询逻辑以帮助你理解的任何伤害。

答案 1 :(得分:3)

您不需要表格user_stepcatuser_step,因为它们只会存储多余的信息。

每个completed状态都由子步骤的完成触发,并且可以通过此查询检索

-- cat completed or not
SELECT
  sc.*, 
  MIN( IF( uss.substep_id IS NULL, 0, 1 ) ) completed,
  IF( MIN( IF( uss.substep_id IS NULL, 0, 1 ) ), 
    MAX( uss.datetime ), 
    NULL ) completed_at
FROM
  step_cat sc
LEFT JOIN steps s ON s.cat_id = sc.id
LEFT JOIN substeps ss ON ss.step_id = s.id
LEFT JOIN user_substep uss ON uss.user_id = 1 AND uss.substep_id = ss.id
GROUP BY sc.id;

-- steps completed or not
SELECT
  s.*, 
  MIN( IF( uss.substep_id IS NULL, 0, 1 ) ) completed,
  IF( MIN( IF( uss.substep_id IS NULL, 0, 1 ) ), 
    MAX( uss.datetime ), 
    NULL ) completed_at
FROM
  steps s
LEFT JOIN substeps ss ON ss.step_id = s.id
LEFT JOIN user_substep uss ON uss.user_id = 1 AND uss.substep_id = ss.id
GROUP BY s.id;

-- substeps completed or not
SELECT
  ss.*, 
  IF( uss.substep_id IS NULL, 0, 1 ) completed,
  uss.datetime completed_at
FROM
  substeps ss
LEFT JOIN user_substep uss ON uss.user_id = 1 AND uss.substep_id = ss.id;

SQL Fiddle DEMO

<强>更新

为了给你一个更相关的GUI答案我已经更新了样本数据以匹配你的截图。

对于演示文稿,您需要知道当前用户ID,类别ID和步骤ID

SET @user_id = 1; -- actual user
SET @cat_id = 1; -- actual category
SET @step_id = 2; -- actual step

您可以使用

获取当前类别并完成状态
SELECT
  sc.*, 
  MIN( IF( uss.substep_id IS NULL, 0, 1 ) ) completed,
  IF( MIN( IF( uss.substep_id IS NULL, 0, 1 ) ), MAX( uss.datetime ), NULL ) completed_at
FROM
  step_cat sc
LEFT JOIN steps s ON s.cat_id = sc.id
LEFT JOIN substeps ss ON ss.step_id = s.id
LEFT JOIN user_substep uss ON 
-- limit to user
uss.user_id = @user_id 
AND uss.substep_id = ss.id
-- limit to cat
WHERE sc.id = @cat_id
GROUP BY sc.id;

使用此

的类别步骤和完成状态
SELECT
  s.*, 
  MIN( IF( uss.substep_id IS NULL, 0, 1 ) ) completed,
  IF( MIN( IF( uss.substep_id IS NULL, 0, 1 ) ), MAX( uss.datetime ), NULL ) completed_at
FROM
  steps s
LEFT JOIN substeps ss ON ss.step_id = s.id
LEFT JOIN user_substep uss
-- limit to user_id
ON uss.user_id = @user_id 
AND uss.substep_id = ss.id
-- limit to cat_id
WHERE s.cat_id = @cat_id
GROUP BY s.id;

至少子步骤及其完整状态

SELECT
  ss.*, 
  IF( uss.substep_id IS NULL, 0, 1 ) completed,
  uss.datetime completed_at
FROM
  substeps ss
LEFT JOIN user_substep uss 
-- limit to user_id
ON uss.user_id = @user_id 
AND uss.substep_id = ss.id
-- limit to step_id
WHERE ss.step_id = @step_id;

SQL Fiddle DEMO

通过所有这3个查询,您可以获得GUI演示所需的所有信息。

答案 2 :(得分:1)

您现在可能过分关注数据库结构:

  • 如果您正处于逐步程序的步骤页面,则可以查看该步骤是否已完成。更新步骤的状态。
  • 如果您在某个步骤页面上,您还可以查询此步骤所属的所有步骤,并且在访问单个步骤页面时保持数据最新,您只需显示该步骤的状态即可。
  • 如果没有逐步程序,您需要立即更新所有步骤,但只有在您第一次进入分步过程时才会更新。
  • 如果一步与其他成就一样,那就像其他成就一样。保留步骤的配置数据(单步)。

然后,您可以减少数据库结构,同时保持更灵活,也更容易重用成就系统的程序。

例如,如果你决定有一天你想要创建一个成就,如果有人已经完成你所谓的 stepcat 的整个类别,如果我正确地读了你的问题。

您需要做的就是检查一个 n 成就是否已经完成。因此,无论是否包含其他成就,都要将所有成就保留在一个表中。这属于其他地方。与类别相同。

一种常见的方法是对数据进行部分去标准化,例如:你可以保留一些自由文本与每个成就,以存储一些因素处理可以使用的数据。

运行janitor作业以保持数据顺序,同时检查您是否错过了检查更高阶的某些成就。无论如何,您需要这样做,因为在您处理时引入错误的第一刻,即使在完全规范化的形式中您也不会违反任何约束,您的数据也会被破坏。因此,无论如何你都需要进行“手动”检查。

由于所有这些,只需这样做。不要把头包住太多,保持简单,完成工作,然后你可以考虑改进它。我能给出的第一个建议就是保持简单,这样你就不会犯很多错误。您的系统将具有此处无法解决的专业。

所以你需要的只是:

  1. 有一张定义成就的表格
  2. 有一个表,用于存储用户是否已完成成就。
  3. 进行可以判断成就是否已经完成的处理。
  4. 有完成成就的处理。
  5. 虽然:

    • 多步骤形式的每个步骤也是一项成就。

    是否是一个多步骤并不是真的很有趣。这只是一个标准的成就。这将是最大的好处。将差异封装到处理中,而不是数据结构中。这将允许您现在和将来采用。

答案 3 :(得分:1)

您的意思是您的SQL架构看起来像step_cat&lt; - steps&lt; - substeps?

如果是这样,表tbl_user_stepcat和tbl_user_step是无用的,因为您可以使用JOIN查询从给定的子步骤/步骤/猫获取所有父信息。

如果您想要关注用户成就,您应该只有一个“achievement_history”表,重点关注用户,子步和定义的状态,这可能会根据其成就采取特定值。 这样每条线指的是给定的用户子步骤成就,然后每当给定的子步骤具有正确的成就状态时,用户将能够处理另一个子步骤并且将插入新的行。

请记住,使用嵌套查询是这种丑陋的方式继续,因为DBMS永远无法规划查询,并且可能存储预定义的方式来获得应有的快速结果,如果设置正确,则使用索引和外键。

答案 4 :(得分:1)

你必须在后端编写3个存储过程并使用前端的输入Session [Username]调用它们。

  
    
      

storedprocedure1 .. listofcategories ..输入 - 无,输出 - 类别属性

             

storedprocedure2 .. listofcategories_filtered_by_user ..输入 - 会话[用户名]输出 - 类别属性

             

在这个存储过程中写下你写的SELECT cat_name FROM tbl_step_cat JO的接合tbl_user_stepcat ON tbl_user_stepcat.stepcat_id = tbl_step_cat.id

             

storedprocedure3 .. listofsubcategories_filtered_by_user .. input- Session [UserName] output-(sub category attributes)

             

在此存储过程内写入子类别的连接

    
  
     

<强>&GT;现在使用输入从前端调用每个Storedprocedure   会话[用户名]参数