0

我一直在努力解决以下案例:

第一种方法:

SELECT * FROM vinyl_tracks vt
INNER JOIN vinyl_keys vk ON vt.mKeyID = vk.mKeyID
INNER JOIN vinyl_tracks_styles vts ON vt.trackID = vts.trackID
INNER JOIN vinyl_styles vs ON vts.styleID = vs.styleID
WHERE vinylID = :vinylID

对于某些曲目,表格vts将包含两种或多种样式(表格仅在单独的行中包含trackIDand styleID。这会导致整个结果重复。我尝试过DISTINCT,以及各种GROUP BY,包括DISTINCTand GROUP BY。这确实返回一个每个轨道的结果,但第二个styleID丢失了。

这是var_dump没有任何查询的GROUP BY

array (size=4)
0 => 
  array (size=10)
    'trackID' => string '868' (length=3)
    'vinylID' => string '249' (length=3)
    'side' => string 'A' (length=1)
    'trackArtist' => string '' (length=0)
    'trackTitle' => string 'Waiting (John Creamer & Stephane K Remix)' (length=41)
    'notes' => string '130 bpm' (length=7)
    'mKeyID' => string '1' (length=1)
    'mKey' => string 'A major' (length=7)
    'Camelot' => string '11B' (length=3)
    'styleID' => string '8' (length=1)
1 => 
  array (size=10)
    'trackID' => string '868' (length=3)
    'vinylID' => string '249' (length=3)
    'side' => string 'A' (length=1)
    'trackArtist' => string '' (length=0)
    'trackTitle' => string 'Waiting (John Creamer & Stephane K Remix)' (length=41)
    'notes' => string '130 bpm' (length=7)
    'mKeyID' => string '1' (length=1)
    'mKey' => string 'A major' (length=7)
    'Camelot' => string '11B' (length=3)
    'styleID' => string '17' (length=2)
2 => 
  array (size=10)
    'trackID' => string '869' (length=3)
    'vinylID' => string '249' (length=3)
    'side' => string 'B' (length=1)
    'trackArtist' => string '' (length=0)
    'trackTitle' => string 'Waiting (Jay Welsh (Black Ice) Remix)' (length=37)
    'notes' => string '135 bpm' (length=7)
    'mKeyID' => string '1' (length=1)
    'mKey' => string 'A major' (length=7)
    'Camelot' => string '11B' (length=3)
    'styleID' => string '17' (length=2)
3 => 
  array (size=10)
    'trackID' => string '869' (length=3)
    'vinylID' => string '249' (length=3)
    'side' => string 'B' (length=1)
    'trackArtist' => string '' (length=0)
    'trackTitle' => string 'Waiting (Jay Welsh (Black Ice) Remix)' (length=37)
    'notes' => string '135 bpm' (length=7)
    'mKeyID' => string '1' (length=1)
    'mKey' => string 'A major' (length=7)
    'Camelot' => string '11B' (length=3)
    'styleID' => string '8' (length=1)

然后我运行另一个查询来获取所有样式,以便我可以填充多选下拉菜单。这是创建下拉列表和屏幕截图的循环的样子foreach- 两个轨道,每个轨道都有两种样式,显示为四个轨道:

$allStylesQuery = $db->prepare("SELECT * FROM vinyl_styles ORDER BY styleID ASC");
$allStylesQuery->execute();
$styles = $allStylesQuery->fetchAll(PDO::FETCH_ASSOC);
$stylesSelector = "<select multiple=\"multiple\" class=\"form-control form-control-sm stylesSelector\" name=\"styleID[".$trow['trackID']."][]\"><option> &ndash; </option>";
foreach($styles as $style) {
    if(isset($trow['styleID']) && ($trow['styleID'] == $style['styleID'])) {
        $stylesSelector .= "<option selected=\"selected\" value=\"".$style['styleID']."\">".$style['styleName']."</option>";
    }
    else {
        $stylesSelector .= "<option value=\"".$style['styleID']."\">".$style['styleName']."</option>";
    }
}
$stylesSelector .= "</select>";

第一种方法的结果——两条轨迹显示为四个

替代方法: 我也尝试使用两个查询来完成相同的操作:

SELECT * FROM vinyl_tracks vt
INNER JOIN vinyl_keys vk ON vt.mKeyID = vk.mKeyID
WHERE vinylID = :vinylID

然后,在foreach循环内部从第一个查询中获取结果,查询如下:

SELECT * FROM vinyl_tracks_styles vts
RIGHT JOIN vinyl_styles vs ON vts.styleID = vs.styleID
WHERE vts.trackID = :trackID

这种替代方式会产生我想要的结果,例如:

array (size=2)
  0 => 
    array (size=3)
    'trackID' => string '868' (length=3)
    'styleID' => string '8' (length=1)
    'styleName' => string 'progressive house' (length=17)
 1 => 
   array (size=3)
    'trackID' => string '868' (length=3)
    'styleID' => string '17' (length=2)
    'styleName' => string 'progressive trance' (length=18)

但是后来我无法以我需要的方式显示结果,即: 每首曲目一行,多选显示所有样式

为了在选择下拉列表中列出所有可能的样式,我需要再运行一个查询(WHERE上面的条件会阻止显示所有样式名称和 ID,尽管RIGHT JOIN我知道):

SELECT * FROM vinyl_styles ORDER BY styleID ASC

突出显示我发现的选择选项(样式)的唯一方法是将两个foreach循环相互嵌套,这当然会再次导致重复的结果:

$trackStylesQuery = $db->prepare("SELECT * FROM vinyl_tracks_styles vts
                        RIGHT JOIN vinyl_styles vs ON vts.styleID = vs.styleID
                        WHERE vts.trackID = :trackID");
$trackStylesQuery->bindParam(':trackID', $trow['trackID'], PDO::PARAM_INT);
$trackStylesQuery->execute();
$trackStyles = $trackStylesQuery->fetchAll(PDO::FETCH_ASSOC);
//var_dump($trackStyles);

$allStylesQuery = $db->prepare("SELECT * FROM vinyl_styles ORDER BY styleID ASC");
$allStylesQuery->execute();
$styles = $allStylesQuery->fetchAll(PDO::FETCH_ASSOC);
$stylesSelector = "<select multiple=\"multiple\" class=\"form-control form-control-sm stylesSelector\" name=\"styleID[".$trow['trackID']."][]\"><option> &ndash; </option>";
foreach($styles as $style) {
    foreach($trackStyles as $trackStyle) {
        if(isset($trackStyle['styleID']) && ($trackStyle['styleID'] == $style['styleID'])) {
            $stylesSelector .= "<option selected=\"selected\" value=\"".$style['styleID']."\">".$style['styleName']."</option>";
        }
        else {
            $stylesSelector .= "<option value=\"".$style['styleID']."\">".$style['styleName']."</option>";
        }
    }
}
$stylesSelector .= "</select>";

样式在选择下拉列表中重复两次,第二种样式也突出显示

如果我坚持上面的第一个查询变体,整行输入重复两次,所以对于这两个轨道,我得到四行。

我真的没有想法,请帮助。

更新

使用@Ultimater 建议的更少查询方法,我现在已经达到了我需要的所有详细信息的情况,唯一的问题是样式列表在样式下拉列表中重复,用于轨道具有的每种样式。如果一个曲目只添加了一种样式或没有添加样式,则没有问题。在下面发布我的完整代码:

$tracksQuery = $db->prepare("SELECT vt.trackID, vt.vinylID, vt.side, vt.trackArtist, vt.trackTitle, vt.notes, vt.mKeyID, vk.mKey, vk.Camelot, 
                            GROUP_CONCAT(vts.styleID SEPARATOR ',') AS 'styleIDs',
                            GROUP_CONCAT(vs.styleName SEPARATOR ',') AS 'styleNames'
                            FROM vinyl_tracks vt
                            INNER JOIN vinyl_keys vk ON vt.mKeyID = vk.mKeyID
                            LEFT JOIN vinyl_tracks_styles vts ON vt.trackID = vts.trackID
                            LEFT JOIN vinyl_styles vs ON vts.styleID = vs.styleID
                            WHERE vt.vinylID = :vinylID
                            GROUP BY vt.trackID");
$tracksQuery->bindParam(':vinylID', $vinylID);
$tracksQuery->execute();
$tracks = $tracksQuery->fetchAll(\PDO::FETCH_ASSOC);
//var_dump($tracks);

// fetch all styles to use in the dropdown
$allStylesQuery = $db->prepare("SELECT * FROM vinyl_styles ORDER BY styleID ASC");
$allStylesQuery->execute();
$styles = $allStylesQuery->fetchAll(PDO::FETCH_ASSOC);

// build a dropdown menu for each of the tracks
// which contains all styles and where the current track's styles are selected
$stylesSelector = "";

foreach($tracks as $track) {

    // start building the selector
    $stylesSelector .= "<select multiple=\"multiple\" class=\"form-control form-control-sm\" name=\"styleID[".$track['trackID']."][]\"><option> &ndash; </option>";

            // check if any styles have been added for this track
    if(!empty($track['styleIDs'])) {

        // check if StyleIDs and StyleNames results contain a comma
        // which means more than one style has been added for that track
        if((strpos($track['styleIDs'], ',')) && (strpos($track['styleNames'], ','))) {

            // separate trackStyleIDs and trackStyleNames
            $trackStyleIDs = explode(',', $track['styleIDs']);
            $trackStyleNames = explode(',', $track['styleNames']);

            // now combine them in one array where styleID is the key and styleName is the value
            $styleIDs_and_Names = array_combine($trackStyleIDs, $trackStyleNames);

            // for each of the styleID => styleName pairs in the array
            // check against all available styles if selected
            foreach($styleIDs_and_Names as $styleID => $styleName) {

                // iterate over all available styles
                foreach($styles as $style) {

                    // if there's a match, add "selected" to the option
                    if($styleID == $style['styleID']) {
                        $stylesSelector .= "<option selected=\"selected\" value=\"".$style['styleID']."\">".$style['styleName']."</option>";
                    }
                    else {
                        $stylesSelector .= "<option value=\"".$style['styleID']."\">".$style['styleName']."</option>";
                    }
                }
            }
        }
        // only one style has been added for this track
        else {
            // iterate over all available styles
            foreach($styles as $style) {

                // if there's a match, add "selected" to the option
                if($track['styleIDs'] == $style['styleID']) {
                    $stylesSelector .= "<option selected=\"selected\" value=\"".$style['styleID']."\">".$style['styleName']."</option>";
                }
                else {
                    $stylesSelector .= "<option value=\"".$style['styleID']."\">".$style['styleName']."</option>";
                }
            }
        }
    }

    // if no styles have been added for this track
    else {
        // iterate all available styles
        foreach($styles as $style) {
            $stylesSelector .= "<option value=\"".$style['styleID']."\">".$style['styleName']."</option>";
        }
    }
    $stylesSelector .= "</select>";
}

以下屏幕截图显示了上述代码生成的实际页面部分。对于我想念但我仍然看不到的专家来说,这一定是显而易见的和容易的。 每种样式在选择器内重复的样式

4

2 回答 2

1

您可以使用GROUP_CONCAT将结果缩小到二维,这样您就不会仅仅为了填充样式下拉列表而获得重复的专辑。相反,您会将样式分组并使用换行符使其漂亮。

您的查询最终看起来像这样:

SELECT
  trackID,
  vinylID,
  side,
  trackArtist,
  trackTitle,
  notes,
  GROUP_CONCAT(mKeyID SEPARATOR '\n') as 'mKeyIDs',
  GROUP_CONCAT(mKey SEPARATOR '\n') as 'mKeys',
  Camelot,
  GROUP_CONCAT(styleID SEPARATOR '\n') AS 'styleIDs',
  GROUP_CONCAT(styleName SEPARATOR '\n') AS 'styleNames'
FROM
  vinyl_tracks vt
INNER JOIN
  vinyl_keys vk ON vt.mKeyID = vk.mKeyID
INNER JOIN
  vinyl_tracks_styles vts ON vt.trackID = vts.trackID
INNER JOIN
  vinyl_styles vs ON vts.styleID = vs.styleID
GROUP BY
  vt.trackID
WHERE
  vinylID = :vinylID

我在这里按曲目 ID 分组,因为这似乎是您用来确定某物是否重复的标准。从那里,我使用带有新行分隔符的 GROUP_CONCAT 来在同一个单元格中显示受该组影响的所有样式。您的 PHP 可以引用此单元格,并在换行符上展开以获得填充下拉列表所需的内容。

首先确保此查询直接在数据库上运行,以查看是否需要对其进行调整。

JSON方法:

或者,如果您的 MySQL 版本支持JSON_ARRAYAGG,您可以使用它而不是 GROUP_CONCAT 来完成功能等效的行为,尽管更简洁:

SELECT
  trackID,
  vinylID,
  side,
  trackArtist,
  trackTitle,
  notes,
  JSON_ARRAYAGG(mKeyID) AS 'mKeyIDs',
  JSON_ARRAYAGG(mKey) AS 'mKeys',
  Camelot,
  JSON_ARRAYAGG(styleID) AS 'styleIDs',
  JSON_ARRAYAGG(styleName) AS 'styleNames'
FROM
  vinyl_tracks vt
INNER JOIN
  vinyl_keys vk ON vt.mKeyID = vk.mKeyID
INNER JOIN
  vinyl_tracks_styles vts ON vt.trackID = vts.trackID
INNER JOIN
  vinyl_styles vs ON vts.styleID = vs.styleID
GROUP BY
  vt.trackID
WHERE
  vinylID = :vinylID

然后在您的 PHP 中,您只需引用$row['styleIDs']$row['styleNames']为“样式”下拉列表中使用的每个选项生成 key=>value 对。

对于音乐“Keys”下拉选项,您将引用$row['mKeyID']$row['mKeys'] 为“Keys”下拉列表中使用的每个选项生成 key=>value 对。

更新:

我在本地进行了设置,并查看了 OP 遇到的问题。我的查询很好。但是,当尝试显示下拉列表时,OP 遇到了他或她的 PHP 循环逻辑的错误。更具体地说,以下逻辑是错误的:

        foreach($styles as $style) {

            // if there's a match, add "selected" to the option
            if($track['styleIDs'] == $style['styleID']) {
                $stylesSelector .= "<option selected=\"selected\" value=\"".$style['styleID']."\">".$style['styleName']."</option>";
            }
            else {
                $stylesSelector .= "<option value=\"".$style['styleID']."\">".$style['styleName']."</option>";
            }
        }
    }

现在我了解了 OP 想要做什么,让我们重写整个混乱。我已经重构了代码。我也在本地对此进行了测试:

$tracksQuery = $db->prepare("SELECT vt.trackID, vt.vinylID, vt.side, vt.trackArtist, vt.trackTitle, vt.notes, vt.mKeyID, vk.mKey, vk.Camelot, 
                            GROUP_CONCAT(vts.styleID SEPARATOR ',') AS 'styleIDs'
                            FROM vinyl_tracks vt
                            INNER JOIN vinyl_keys vk ON vt.mKeyID = vk.mKeyID
                            LEFT JOIN vinyl_tracks_styles vts ON vt.trackID = vts.trackID
                            WHERE vt.vinylID = :vinylID
                            GROUP BY vt.trackID");
$tracksQuery->bindParam(':vinylID', $vinylID);
$tracksQuery->execute();
$tracks = $tracksQuery->fetchAll(\PDO::FETCH_ASSOC);

$allStylesQuery = $db->prepare("SELECT * FROM vinyl_styles ORDER BY styleID ASC");
$allStylesQuery->execute();
$styles = $allStylesQuery->fetchAll(PDO::FETCH_ASSOC);


$stylesSelector = "";

foreach($tracks as $track)
{

    $stylesSelector .= "<select multiple=\"multiple\" class=\"form-control form-control-sm\" name=\"styleID[".$track['trackID']."][]\"><option> &ndash; </option>";
    $trackStyleIDs = explode(',', $track['styleIDs']);
    foreach($styles as $style)
    {
        $optionValue = $style['styleID'];
        $optionText = $style['styleName'];
        $optionSelected = in_array($optionValue, $trackStyleIDs) ? ' selected="selected"' : '';
        $stylesSelector .= sprintf('<option value="%s"%s>%s</option>', $optionValue,$optionSelected,$optionText);
    }

    $stylesSelector .= "</select>";
}
于 2019-05-01T08:22:47.247 回答
0

正如我在评论中所说,你必须分两步分解这个过程。

  • 第一步是简单地获取轨道的基本信息。
  • 第二步是在单独的数组中获取轨道的所有相应样式

单独存储样式后,您现在将拥有一个这样的数组,

[
    track_id_1 => [ style_id_1, style_id_2, ]
    track_id_2 => [ style_id_x, style_id_y, ]
]

这意味着如果您知道轨道 ID 和样式 ID,您可以测试样式是否附加到当前轨道in_array

<?php
     if (in_array($style_id, $styles_per_track[$track_id])) {
         echo 'Style '.$style_id.' is attached to track '.$track_id;
     }

该解决方案的模型如下。
请注意我没有测试任何这段代码,只是为了给你一个分离数据的想法

    <?php
        // Step 1: Fetch all tracks w/out worrying about styles
        $tracks_stmt = $db->prepare('SELECT * FROM vinyl_tracks vt WHERE vinylID=:vinylID');
        $track_stmt->execute([':vinylID' => $vinylID, ]);

        $tracks = $track_stmt->fetchAll(\PDO::FETCH_ASSOC);

        // Step 2: Loop each track and store the styles accordingly
        $styles_per_track = [];
        $styles_per_track_stmt = $db->prepare('SELECT * FROM vinyl_tracks_styles vts WHERE trackId=:trackId')

        foreach($tracks as $track) {
            $vinyl_tracks_styles[$track['trackID']] = [];
            $styles_per_track_stmt->execute($track['trackID']);
            $temp = $styles_per_track_stmt->fetchAll(\PDO::FETCH_ASSOC);
            foreach($temp as $style_per_track) $vinyl_tracks_styles[$track['trackID']] = $style_per_track['styleID'];
        }

        //fetch styles
        /**
        ...
        ...
        ...
        **/
        foreach($tracks as $track) {
    ?>
            <select name="track[<?=$track['trackID'];?>" multiple>
    <?php
            foreach($styles as $style) {
    ?>
                <option value="<?= $style['styleID']; ?>" <?php if(in_array($style['styleID'], $styles_per_track[$track['trackID']])) { echo ' selected'; } ?>><?= $style['styleName']; ?></option>
    <?php
            }
    ?>
        </select>
    <?php
    }
    ?>
于 2019-05-01T08:13:24.927 回答