多层评论回复:显示和存储

2023-11-30

因此,我正在尝试创建一个评论系统,您可以在其中回复已经回复的评论(允许您创建理论上无限的回复线程)。我希望它们按时间顺序显示(最新的在顶部),但回复当然应该直接位于原始评论的下方。如果有多个评论回复同一条评论,回复也应按时间顺序排列(仍在原始评论下方)。我还想将评论组的数量(一组带有单个评论的评论,根本不是回复)限制为 25 个。我应该如何设置 MySQL 表,以及我将使用哪种查询提取我想要的东西?

这是我的数据库的简化版本:IDint(11) 非空自动增量,DatePosted日期时间不为空,InReplyToint(11) 非空默认“0”,

抱歉,如果这有点令人困惑,我不知道如何以不同的方式表达它。这个问题已经在我脑海里萦绕了几个月了,每次我解决一个问题时,我都会遇到另一个问题......


有很多方法。这是我喜欢的一种方法(并且经常使用)。

数据库

考虑以下数据库结构:

CREATE TABLE comments (
  id int(11) unsigned NOT NULL auto_increment,
  parent_id int(11) unsigned default NULL,
  parent_path varchar(255) NOT NULL,

  comment_text varchar(255) NOT NULL,
  date_posted datetime NOT NULL,  

  PRIMARY KEY  (id)
);

你的数据将如下所示:

+-----+-------------------------------------+--------------------------+---------------+
| id  | parent_id | parent_path             | comment_text             | date_posted   |
+-----+-------------------------------------+--------------------------+---------------+
|   1 | null      | /                       | I'm first                | 1288464193    | 
|   2 | 1         | /1/                     | 1st Reply to I'm First   | 1288464463    | 
|   3 | null      | /                       | Well I'm next            | 1288464331    | 
|   4 | null      | /                       | Oh yeah, well I'm 3rd    | 1288464361    | 
|   5 | 3         | /3/                     | reply to I'm next        | 1288464566    | 
|   6 | 2         | /1/2/                   | this is a 2nd level reply| 1288464193    | 

... and so on...

以可用的方式选择所有内容相当容易:

select id, parent_path, parent_id, comment_text, date_posted
from comments 
order by parent_path, date_posted;

订购依据parent_path, date_posted通常会按照生成页面时需要的顺序生成结果;但您需要确保注释表上有一个可以正确支持这一点的索引——否则查询可以工作,但效率真的非常低:

create index comments_hier_idx on comments (parent_path, date_posted);

对于任何给定的单个评论,很容易获得该评论的整个子评论树。只需添加一个 where 子句:

select id, parent_path, parent_id, comment_text, date_posted
from comments 
where parent_path like '/1/%'
order by parent_path, date_posted;

添加的 where 子句将使用我们已经定义的相同索引,所以我们可以开始了。

请注意,我们还没有使用parent_id然而。事实上,这并不是绝对必要的。但我之所以将其包括在内,是因为它允许我们定义传统的外键来强制引用完整性,并根据需要实现级联删除和更新。外键约束和级联规则仅在 INNODB 表中可用:

ALTER TABLE comments ENGINE=InnoDB;

ALTER TABLE comments 
  ADD FOREIGN KEY ( parent_id ) REFERENCES comments 
    ON DELETE CASCADE 
    ON UPDATE CASCADE;

管理层次结构

当然,为了使用这种方法,您必须确保设置parent_path当您插入每条评论时正确地插入。如果您移动评论(这无疑是一个奇怪的用例),您必须确保手动更新从属于移动评论的每个评论的每个parent_path。 ...但这些都是相当容易跟上的事情。

如果你真的想变得更奇特(并且如果你的数据库支持它),你可以编写触发器来透明地管理parent_path——我将把这个作为练习留给读者,但基本思想是插入和更新触发器将触发在提交新插入之前。他们会走上树(使用parent_id外键关系),并重建值parent_path因此。

甚至有可能打破parent_path放入一个单独的表中,该表完全由注释表上的触发器管理,并使用一些视图或存储过程来实现您需要的各种查询。因此,将中间层代码与了解或关心存储层次结构信息的机制完全隔离。

当然,无论如何都不需要任何花哨的东西——通常只需将parent_path放入表中,并在中间层编写一些代码以确保它与所有其他字段一起得到正确管理就足够了你已经必须管理了。


施加限制

MySQL(和其他一些数据库)允许您使用LIMIT clause:

SELECT * FROM mytable LIMIT 25 OFFSET 0;

不幸的是,在处理这样的分层数据时,仅使用 LIMIT 子句不会产生所需的结果。

-- the following will NOT work as intended

select id, parent_path, parent_id, comment_text, date_posted
from comments 
order by parent_path, date_posted
LIMIT 25 OFFSET 0;

相反,我们需要在想要施加限制的级别进行单独的选择,然后将其与“子树”查询连接起来以给出最终所需的结果。

像这样的东西:

select 
  a.*
from 
  comments a join 
  (select id, parent_path 
    from comments 
    where parent_id is null
  order by parent_path, post_date DESC 
  limit 25 offset 0) roots
  on a.parent_path like concat(roots.parent_path,roots.id,'/%') or a.id=roots.id)
order by a.parent_path , post_date DESC;

注意声明limit 25 offset 0,埋在内部选择的中间。该语句将检索最近 25 条“根级”评论。

[编辑:您可能会发现您必须稍微尝试一些东西才能完全按照您喜欢的方式排序和/或限制事物。这可能包括在编码的层次结构中添加信息parent_path。例如:而不是/{id}/{id2}/{id3}/,您可能决定将 post_date 作为 Parent_path 的一部分:/{id}:{post_date}/{id2}:{post_date2}/{id3}:{post_date3}/。这将使您很容易获得所需的顺序和层次结构,但代价是必须预先填充字段,并在数据更改时对其进行管理]

希望这可以帮助。 祝你好运!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多层评论回复:显示和存储 的相关文章

  • MaxListenersExceededWarning:检测到可能的 EventEmitter 内存泄漏。添加了 11 条消息列表。使用emitter.setMaxListeners()来增加限制

    我知道这可能会标记为重复的解决方案 但堆栈溢出的解决方案对我不起作用 Problem node 5716 MaxListenersExceededWarning Possible EventEmitter memory leak detec
  • 获取带有计数的不同记录

    我有一张桌子personid and msg列 personid msg 1 msg1 2 msg2 2 msg3 3 msg4 1 msg2 我想得到总计msg对于每个personid 我正在尝试这个查询 select distinct
  • posts_search 中的自定义查询

    如何使用此查询作为我的自定义搜索查询 add filter posts search my search is perfect 20 2 function my search is perfect search wp query sWord
  • 将“php”作为 shell 脚本执行时的自定义 php.ini 文件

    我在跑php作为 shell 脚本 我不确定 shell脚本 是否正确 该文件以 usr bin php 这很好用 但 MongoDB 类没有正确加载php ini文件 具有extension mongo so 未使用 我该如何使用它tha
  • Symfony2 Assetic 和 Less Sourcemaps

    我不确定如何破解 assetic less 过滤器以输出源映射文件 我这里指的是LessFilterhttps github com kriswallsmith assetic blob master src Assetic Filter
  • 如何在原则 2 迁移中删除外键

    我想在原则 2 迁移中删除外键 但没有 dropForeignKeyConstraint 有谁知道怎么丢掉吗 public function down Schema schema table schema gt getTable table
  • 从 .phar 存档中提取文件

    对于 Phar 文件 我完全错过了一些东西 我正在安装一个需要 phpunit pdepend 和其他依赖项的项目 我将它们作为 phar 文件获取 但是 我无法使用命令行工具 php 命令 从中提取文件 我用谷歌搜索了这个问题 但没有发现
  • 使用 SSL 证书验证 Web 浏览器

    是否可以使用 ssl 证书对 Web 浏览器进行身份验证 假设我在应用程序中存储私钥 有什么方法可以从浏览器读取密钥并尝试基于该私钥进行身份验证 您可以使用 SSL TLS 客户端证书身份验证来对浏览器 用户进行身份验证 服务器必须请求客户
  • PHP 与 MySQL 查询性能( if 、 函数 )

    我只看到这个artice http www onextrapixel com 2010 06 23 mysql has functions part 5 php vs mysql performance 我需要知道在这种情况下什么是最好的表
  • PHP preg_filter 返回意外的长值

    尝试在 Woocommerce 中删除标签并过滤值 但无法以正确的格式获取它 有东西有腥味 我正在使用WC gt cart gt get cart subtotal 来检索该值 在此示例中 我的值是 2 429kr 原始返回值是 span
  • 覆盖控制器 Symfony 3.4/4.0

    我目前正在尝试覆盖 FOSUserBundle 中的控制器 在新的文档中 https symfony com doc 3 4 bundles override html https symfony com doc 3 4 bundles o
  • 在 apache docker 容器中运行虚拟主机

    我在同一个 apache 容器中有两个 php 应用程序 我试图在端口上运行其中一个应用程序 因为它需要通过根域而不是子文件夹进行访问 我想在端口 8060 上运行应用程序 我尝试使用 apache 虚拟主机执行此操作 但它不会加载页面 h
  • SQL 最近日期

    我需要在 php 中获取诸如 2010 04 27 之类的日期作为字符串 并在表中找到最近的 5 个日期 表中的日期保存为日期类型 您可以使用DATEDIFF http dev mysql com doc refman 5 1 en dat
  • 如果循环中内存超出,我可以在 for 循环中抛出异常吗?

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 如何处理 foreach 循环中发生
  • 表单计算器脚本基本价格未加载 OnLoad

    我的表单中有一个计算器来计算我的下拉选项选择 function select calculate on change calc input type checkbox calculate on click calc function cal
  • Doctrine EntityManager 清除嵌套实体中的方法

    我想用学说批量插入处理 http doctrine orm readthedocs org en latest reference batch processing html为了优化大量实体的插入 问题出在 Clear 方法上 它表示此方法
  • php,统计字符并删除超过140个字符的内容

    我需要一个 PHP 函数来计算短语的字符数 如果短语长度超过 140 个字符 则此函数应删除所有其他字符并在短语末尾添加三个点 例如我们有 message I am what I am and you are what you are et
  • ini_set 'session.gc_maxlifetime' 为 1 天

    If I do ini set session gc maxlifetime 86400 这是否意味着用户可以将浏览器留在同一页面 非活动状态 最多 1 天 而不必担心会话被垃圾收集并被注销 如果服务器配置不支持此功能会发生什么 它会给我一
  • post php mysql 的拆分关键字

    我有一个表存储帖子 ID 它的标签如下 Post id Tags 1 keyword1 keyword2 keyword3 我想循环遍历该表中的每一行并执行以下操作 将关键字1 关键字2 关键字3放入新表中 word id word val
  • 无法显示 Laravel 欢迎页面

    我的服务器位于 DigitalOcean 云上 我正在使用 Ubuntu 和 Apache Web 服务器 我的家用计算机运行的是 Windows 7 我使用 putty 作为终端 遵循所有指示https laracasts com ser

随机推荐

  • 如何更改 Handsontable 中已更改单元格的颜色?

    我正在使用 Handsontable 插件 当用户更改单元格中的值时 它应该变成黄色 以便他们可以跟踪更改的内容 在本例中 黄色是类 changeInput 棘手的部分是 当您双击单元格进行更改时 它会变成文本区域 而不再是 td 有任何想
  • 在 matplotlib 中显示表面前面的轮廓

    我一直在寻找答案 但我似乎不明白为什么在我的代码中我无法让投影轮廓显示在表面 后面 from mpl toolkits mplot3d import axes3d import matplotlib pyplot as plt from m
  • 使用 Selection 的 RangeElements 获取 Google 文档中的所有嵌套文本元素

    在类似于上面的文档中 我可以使用以下代码获取所有段落 var paras body getParagraphs 请注意 上面的代码不仅返回顶级段落 还返回内部的所有子级别段落ListItems Tables etc 如何在选定的范围内做同样
  • 如何处理 Angular 4 中的窗口滚动事件?

    我似乎无法捕获窗口滚动事件 在几个网站上我发现了类似的代码 HostListener window scroll onWindowScroll console log Scrolling 这些片段通常来自版本 2 这在 Angular 4
  • 将内部文本文件加载为 XML

    我正在编写一个 VB NET 应用程序 我想从资源加载内部文本文件 但代码显示无效 URI Uri 字符串太长 代码是 document Load C Users Sou Documents Visual Studio 2010 Proje
  • 在交替行上交换列(左/右)

    我有一系列行 每行包含两列 宽度按 50 50 分开 我希望每隔一行交换左列 image 在右边 但我需要维护 HTML 中的顺序 因为它在较小的屏幕上显示为一列 CSS ul list style none padding left 0
  • 变量赋值后的尾随注释颠覆比较

    在 GNU make 中 附加到变量赋值的尾随注释会阻止后续比较 通过ifeq 无法正常工作 这是生成文件 A a B b trailing comment C c RESULT ifeq A a RESULT a endif ifeq B
  • DataTables 警告 - 请求第 0 行、第 0 列的未知参数“0”

    我使用 jQuery DataTable 来显示使用存储过程和 Web 服务从数据库获取的数据 我可以使用 Fiddler 运行 SP 或服务 但是当涉及到填充 DataTable 时 我收到了错误记录here 在我的具体情况下 消息是 D
  • 我如何使用注释进行聚合,如 @Query(value = "{"query":""}")

    如何使用注释通过 spring data elasticsearch 进行像 Query value query 这样的聚合 你不能用 Query注释的唯一目的是发送查询 而不是聚合 使用 Spring Data Elasticsearch
  • 将字符串替换为 DataTable 的 DataColumn 中的另一个字符串

    I have DataTable它检索多列和行 其中一栏 评论 包含数据 我想用 n dtOutput Generix getTickets DateTime Parse 1 1 1900 DateTime Now iTicket iDis
  • android:安装应用程序无需用户干预

    我想在 Android 上安装应用程序而无需用户干预 我正在使用权限INSTALL PACKAGES我正在将应用程序安装在 download 文件夹中 下载完成后 会出现一个对话框 要求我安装该应用程序 如何隐藏此对话框并在无需用户干预的情
  • POSIX 正则表达式不适用于 [:digit:] 字符类

    我在日志文件中有以下日志行access 20170118 14 log 127 0 0 1 18 Jan 2017 14 22 16 0000 GET fam shared generate test devicelist php HTTP
  • Delphi:为什么断点有时不可用(IDE 上的绿色突出显示线)?

    有时我会失去 Delphi 中的断点功能 我认为这是 Delphi 2009 的问题 但现在我在 Delphi XE 中也有它 在 Delphi 2009 中 通过删除 dproj 文件 我使断点再次起作用 在 Delphi XE 中 我无
  • 不允许销毁资产以避免数据丢失

    我的RemoveTail 函数执行以下代码时遇到了麻烦 destroy gameObject 这个蛇游戏创建了我的蛇预制件的克隆 我通过分配 尾巴 来控制蛇的长度 并在达到 maxSize 时删除 尾巴 游戏对象 我知道我的错误是由于游戏删
  • ImageMagick 将 jpg 图像转换为 gif 速度慢

    我正在使用Magick IM 7 0 3平台 CentOS Linux版本7 0 将图像转换为gif 我从文件创建 Image 对象 问题是当我将 9 个 png 文件 每个 50kb 转换为 gif 时 只需要 50ms 但当变成9个jp
  • 类别重叠分析

    我正在尝试执行一些类别重叠分析并需要帮助 我有由客户服务票组成的数据 门票上标有类别数据 票证可以包含多个类别标签 我有一个提取票证 ID 和类别的查询 我得到多行包含多个类别的 ID 我正在寻找一种显示类别重叠的方法 例如 有多少票有类别
  • 获取当前地址位置的最佳方式 Kotlin 2023

    我想创建一个当前位置按钮来获取用户的当前地址 目前我正在使用fusedLocationClient lastLocation但它很慢 有时甚至不起作用 2023 年是否有更好且更新的方法使用 Kotlin 从用户处获取当前地址 祝你今天过得
  • 是否可以将可滚动的 TextView 添加到 ListView 中?

    我有一个 ListView 其中每行都有固定的高度 每行在一些图像旁边都包含一个 TextView 有时 我想要显示的文本太大 因此我想使其可滚动 所以我添加了 基于使 TextView 在 Android 上可滚动 将以下行添加到我的 T
  • 如何从 Promise 中提取数据

    我有一个返回数据的承诺 我想将其保存在变量中 由于异步性质 这在 JavaScript 中是不可能的吗 我需要使用吗onResolve作为回调 我可以以某种方式使用它 例如用 async await 包装它 const foo bar Pr
  • 多层评论回复:显示和存储

    因此 我正在尝试创建一个评论系统 您可以在其中回复已经回复的评论 允许您创建理论上无限的回复线程 我希望它们按时间顺序显示 最新的在顶部 但回复当然应该直接位于原始评论的下方 如果有多个评论回复同一条评论 回复也应按时间顺序排列 仍在原始评