无法在具有 3 个 JOIN 的 sql 上列出一对多关系的列数据

can't list column data from one to many relation on sql with 3 JOINs

我有 4 tables -

    CREATE TABLE `group_user` (
        `id` int(11) NOT NULL,
        `user_id` int(11) NOT NULL,
        `group_id` int(11) NOT NULL,
        `accepted` tinyint(4) NOT NULL DEFAULT 0
     ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    CREATE TABLE `group_table` (
        `id` int(11) NOT NULL,
        `groupName` varchar(255) NOT NULL,
        `owner` varchar(255) NOT NULL,
        `date_dreated` datetime NOT NULL DEFAULT current_timestamp()
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    CREATE TABLE `training` (
        `id` int(11) NOT NULL,
        `groupName` varchar(25) COLLATE utf8_unicode_ci NOT NULL,
        `sessDate` date NOT NULL,
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

    CREATE TABLE `session_plan` (
        `id` int(11) NOT NULL,
        `training_id` int(11) NOT NULL,
        `workout_id` int(11) NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

group_table保存每个组的id和名称,

group_user 保存哪些用户是哪些组的成员,

training 保存会话的 ID 和日期以及它们属于哪个组

session_plan 保存在任何给定日期的特定会话的个人锻炼。锻炼 ID 来自名为 workout_title 的 table,它位于下方并包含锻炼的名称,我在下面列出了它,但现在我只是想获取 workout_id 的列表

session_plan 中可能有很多结果与其他 3 table 个结果相匹配。

我确实将此设置为逗号分隔列表,但被告知要规范化我的 tables,

虽然这确实解决了我遇到的一些其他问题,但我现在有一个问题,对于 session_plan 中的每个结果,它都会重复之前的结果,然后只添加 [=16] 中的下一个项目=].

我目前的代码是

    $accepted = true;
    $ps = $db_conx->prepare("
        SELECT u.group_id
              , g.groupName
              , t.sessDate
              , s.workout_id
        FROM group_user u
        JOIN group_table g
             ON u.group_id = g.id
        JOIN training t
             ON g.groupName = t.groupName
        JOIN session_plan s
             ON t.id = s.training_id
        WHERE g.user_id = ? 
          && u.accepted = ?
    ");
    
        $ps->bind_param("ii", $id, $accepted); 
        $ps->execute();
        $result = $ps->get_result();

    while ($row = mysqli_fetch_assoc($result)) {
        $testGroupName = $row['groupName'];
        $testGroupId = $row['group_id'];
        $testSessDate = $row['sessDate'];
        $testPlan = $row['workout_id'];

        $workout_plan .= " + " .$testPlan;

        $testSession .= $testGroupId. " : " .$testGroupName. "<br/>" .$testSessDate. " - " .$workout_plan. "<br/>";
    }  

$id 在页面上方进一步定义。

我要找的是:

1 : TheBestGroup 2020-12-05 - + 1 + 2

1 : TheBestGroup 2020-12-04 - + 4

但我得到的是:

1 : TheBestGroup 2020-12-05 - + 1

1 : TheBestGroup 2020-12-05 - + 1 + 2

1 : TheBestGroup 2020-12-04 - + 1 + 2 + 4

我是规范化和 table JOIN 的新手,所以不确定我是否错误地使用了一个或另一个或两者。

我在这里搜索了一个答案,虽然有类似的答案,但我找不到经常用我无法理解的编码语言解决我的问题的答案。

从逻辑上讲,我认为问题是 while 循环每次通过时只会从每个 table 中选择一个结果,然后 $workout_plan .= " + " .$testPlan; 将其存储到下一个 运行 通过循环,我尝试在循环外添加 $workout_plan = "";unset($workout_plan) 但得到了相同的结果。如果我删除 $workout_plan .= " + " .$testPlan; 结果如下:

1 : TheBestGroup 2020-12-05 - 1

1 : TheBestGroup 2020-12-05 - 2

1 : TheBestGroup 2020-12-04 - 4

我只是不确定如何从 session_plan 的每一行中为其他人获取所有相关条目。

您只需要逻辑来区分何时要将 workout_id 添加到 testSession,以及何时移动到新的组或日期。

类似...

$testGroupId = "";
$testSessDate = "";
$testSession = "";

while ($row = mysqli_fetch_assoc($result)) {

  // If this row's Group and Date are the same as the preceding row...
  // - Add this row's WorkoutId to the testSession and move to the next row

  if (($testGroupId == $row['group_id']) && ($testSessDate == $row['sessDate']))
    $testSession .= " + " .$row['workout_id'];
    continue;


  // If this row's Group or Date are Different from the preceding row...
  // - Add a break to the end of the previous testSession
  // - Do something with the previous testSession
  // - Start a new testSession with the current row's values

  $testSession .= "<br/>";
  # Code to use the previous testSession goes here

  $testGroupName = $row['groupName'];
  $testGroupId = $row['group_id'];
  $testSessDate = $row['sessDate'];
  $testPlan = $row['workout_id'];

  $testSession = $testGroupId. " : " .$testGroupName. "<br/>" .$testSessDate. " - " .$testPlan. "<br/>";

}

// The very last Group/Date combination won't have been processed by the While loop
// So, process it here

$testSession .= "<br/>";
# Code to use the previous testSession goes here