如何不重复基于 JOIN 查询的表列?

Posted

技术标签:

【中文标题】如何不重复基于 JOIN 查询的表列?【英文标题】:How to not repeat a table column based on JOIN query? 【发布时间】:2019-08-20 01:00:04 【问题描述】:

简单来说,这就是我的 index.php 页面的样子:

[comment_id 10]
[commenter_name here]
    [reply_id 1]
    [replier_name here]

[comment_id 10]
[commenter_name here]
    [reply_id 2]
    [replier_name here]

[comment_id 10]
[commenter_name here]
    [reply_id 3]
    [replier_name here]

这就是我需要的样子:

[comment_id 10]
[commenter_name here]
    [reply_id 1]
    [replier_name here]
    [reply_id 2]
    [replier_name here]
    [reply_id 3]
    [replier_name here]

我的代码的作用:单击回复按钮时,将对 comment_id 的回复提交到我数据库中的 replies 表。问题:也许我根据所使用的 JOIN 类型错误地编写了我的SELECT 语句,但我的comment_id 与replier_names 一起被重复回显。结果应该只是:每个唯一的comment_id 以及所有replier_names 在下面回显。这是我的长代码的虚拟版本:

$sid = $_SESSION['userid'];
$query = $conn->query("
    SELECT comments.*, replies.*, users.username
    FROM comments
    LEFT JOIN replies
    ON comments.comid = replies.repcomid
    INNER JOIN users
    ON comments.comuserid = users.userid
    ORDER BY comments.comid DESC;
");
$comreps = [];
while($row = $query->fetch_object()) 
  $comreps[] = $row;

?>

<?php foreach($comreps as $comrep): ?>
    <b><?php echo $comrep->username; ?></b>
    <div><?php echo $comrep->repname; ?></div>
<?php endforeach; ?>

@Tim Morton:只是想知道,这就是您所指的确切方法吗? (见下文)

<?php
$last_username = '';
foreach($comreps as $comrep):
    if ($comrep->username != $last_username): ?>
        <b><?php echo $comrep->username; ?></b>
    <?php endif;
    $last_username = $comrep->username; ?> 
    <div><?php echo $comrep->repname; ?></div>
<?php endforeach; ?>

【问题讨论】:

你能提供一个小提琴吗? @MosesSchwartz 但我的问题包括 php 代码和从我的数据库中选择查询。我不认为 php 代码可以在小提琴上看到? 两种方法:将所有 cmets 放入一个数组中,然后在遍历它们时查询回复,或者使用用户 ID 作为键将整个内容放入一个数组中,即$row[$comid][‘relies’] = 回复数组。我会倾向于更 oop-ish 的第一个选项。 @TimMorton 嗨,我在问题的末尾添加了一段代码。除非你指的是别的意思,否则这就是你所指的确切方法吗? 这与第二个建议的想法相同,只是没有数组的开销。 【参考方案1】:

我能想到三种方式:

    嵌套查询(外循环是 cmets,内循环是回复) 根据当前查询构建数据数组 使用临时变量保存要显示的最后一条评论信息,直到评论信息发生变化

每种技术都有优缺点,那么哪种方法最好呢?这取决于...

临时变量

您在编辑中几乎确定了 #3。这可能是最有效的方法(一个查询,很少的开销)。唯一的缺点是它在视图中的逻辑比我喜欢的要多(使视图混乱),而且它完全是程序性的,但这是一个品味问题。

数据数组

这也使用单个查询,但使用命名数组来聚合 cmets。缺点:数组本身可能会令人困惑。

首先,如果数据是 json 字符串,您将如何表示数据?

[
    comment:
    
        commentor_id:’$comment_id’, 
        commentor_name:’$commentor_name’
    ,
        reply
        [
            
                reply_id:’$reply_id1’,
                reply_name:’$reply_name1’
            , 
                reply_id:’$reply_id2’,
                reply_name:’$reply_name2’
            
        ]
    
]

(旁注,在我写这篇文章时,duck 提出了一个更好的方法,我将在此之后展示)

为了使数组本身去重复,我要做一个改变:

[$comment_id:
    comment:
    
        commentor_id:’$commentor_id’, 
        commentor_name:’$commentor_name’
    ,
        reply
        [$reply_id1:
            
                reply_id:’$reply_id1’,
                reply_name:’$reply_name1’
            , $reply_id2:
            
                reply_id:’$reply_id2’,
                reply_name:’$reply_name2’
            
        ]
    
]

在您遍历模型中的查询结果时创建此数组:

$data = [];
while($row = $query->fetch_object()) 
    $data[$row->comid] = array(
        ‘comment_id’=>$row->comid,
        ‘comment_name’=>$row->username,
    );
    if(!isset($data[$row->comid][‘reply’]) 
        $data[$row->comid][‘reply’]=array();
    
    $data[$row->comid][‘reply’][$row->repid] = array(
        ‘reply_id’=>$row->repid,
        ‘reply_name’=>$row->repname
    );


然后就可以在视图中遍历数组结构了:

<?php foreach($data as $comments): ?>
    <div><?= $comments[‘comment_name’] ?></div>
    <ol>
        <?php foreach($comments[‘reply’] as $reply): ?>
            <li><?= $reply[‘reply_id’] ?></li>
        <?php endforeach; ?>
    </ol>
<?php endforeach; ?>

数据数组,第 2 部分

数据库可以使用aggregate functions以您需要的方式输出数据。 (我将其留作更多研究)

嵌套查询

这使用模型中的数据访问对象。我没有足够的空间来解释,但是考虑每个 crud 对象都能够通过其 id 找到记录

// assuming custom data access objects Comment and Reply each with method ‘find()’
<?php
$comment = new Comment (new DB);
$reply = new Reply(new DB);
?>


<?php $comment->find($comment_id); ?>
<?php while($row = $comment->fetch() ): ?>
    <div><?= $row->name?></div>
    <ol>
    <?php $reply->find($row->id); ?>             
    <?php while($r = $reply->fetch()): ?>
         <li><?= $r->name ?></li>
    <?php endwhile; ?>
    </ol>
<?php endwhile; ?>

优点:使用可重用的对象并将模型和视图分开

缺点:OOP 可以隐藏昂贵的数据库调用,尤其是当它们处于循环中时。但实际上,除非它失控,否则这不是问题。

【讨论】:

哦,哇,远远超出我对评论的预期!这些是您提供的一些出色的解决方案,我肯定会用我的代码来实现这些。而你今天教会了我很多!真的,非常感谢你!! @misner3456 如果对您有价值,请随时点赞或接受。抱歉最后一个有点稀疏;添加的框架太多。很高兴帮助:) 我忘记标记为最佳答案了,抱歉!非常感谢!

以上是关于如何不重复基于 JOIN 查询的表列?的主要内容,如果未能解决你的问题,请参考以下文章

获取当月记录[重复]

如何将表列从十进制更改为 varchar [重复]

如何修改此工作 SQL SELECT/JOIN 查询以删除重复项?

SQL 查询更新输入参数不为 Null 的表列?

LEFT JOIN 与重复的大查询

IdentityUserRole 表列重复且无法删除主键