我在PHP / MySQL中设计票务系统。 每张票都代表“票”中的一行。表。 用户可以添加多个评论'到每张票。
查看故障单时,我只是对评论'进行SQL查询。表格,用于选择与相应的ticketid链接的所有评论
但是,现在我必须设计一个搜索页面,它会返回任何匹配的票证并立即显示(部分,例如最后5个)他们的链接评论。
我通过执行我在单个故障单视图页面中使用的相同SQL查询来完成此操作,但是对搜索查询中的每个匹配行重复此操作。因此,如果您有1500个匹配的行,这意味着对'评论'表,是的,效率不高......
现在我想知道:有没有办法将评论表加入到故障单表中,但是评论表中有多行,并且已经挤压了#39;进入一栏?
我正在考虑在注释行上执行CONCAT(),将它们作为逗号分隔值返回,然后我可以在php中再次爆炸()以获取包含我对每个票证的所有注释的数组。但这是最有效的方式还是有更好的方法?
编辑:请记住,我们还应该通过查询返回没有任何评论的门票
答案 0 :(得分:2)
啊,是的,许多人往往会遇到“典型分组”的典型问题......
当我们谈论将多个记录(注释)加入到一行(事件)中时,使用MySQL执行此操作的唯一可靠方法(如计算可变数量的行)是使用{ {1}}将一行或多行的信息分组为单个分隔的字符串。
您还可以使用名为GROUP_CONCAT()
的功能将信息分组到可变数量的实际列中,但不幸的是,PIVOT
在MySQL中不可用。
无论哪种方式,您都需要一些应用程序逻辑(爆炸等)来格式化并显示每张票的评论子集。
对于SQL,您可以执行以下操作:
PIVOT
除了SELECT
a.ticket_id,
a.ticket_title,
a.date_created,
SUBSTRING_INDEX(GROUP_CONCAT(CONCAT(LEFT(b.comment_txt, 150), '...', ':::', b.date_posted) ORDER BY b.date_posted DESC SEPARATOR '|||'), '|||', 5) AS comment_list
FROM
tickets a
LEFT JOIN
comments b ON a.ticket_id = b.ticket_id
WHERE
a.ticket_title LIKE '%search_term%'
GROUP BY
a.ticket_id
中的第4列之外,这里的所有内容都相当简单......所以让我们分解一下:
在最里面的部分,我们有SELECT
。它的作用是将每个注释的字段连接在一起,这样您就可以获得每个注释的多个属性(例如日期,实际文本以及ID等等)。
单独CONCAT()
之后,单个评论可能类似于:
CONCAT()
Lorem Ipsum dolor sit amet consecteur...:::2012-06-21 00:00:00
是您:::
分隔每个属性的分隔符之一。
向外移动,explode()
然后将每个 行 连接在一起。在这一点上,我们基本上连接了连接。此外,由于函数中的GROUP_CONCAT()
,最近的注释出现在字符串的开头。
评论列表可能如下所示:
ORDER BY b.date_posted
Lorem Ipsum dolor sit amet consecteur...:::2012-06-21 00:00:00|||Cras aliquam neque quam, eget facilisis nulla...:::2012-06-18 00:00:00
是您用来分隔每条评论的分隔符。
进一步向前移动,|||
仅选择前五个评论。由于我们首先按最近的顺序订购了评论,因此它基本上只选择每张票中的五条最新评论。
然后在你的PHP代码中,你可以粗略地做:
SUBSTRING_INDEX
我使用那些特定的分隔符,因为逗号可以出现在标题等字段中,并且您不希望脚本在错误的位置分隔字符串。这种错误分离的可能性是使用foreach($tickets as $ticket)
{
// First check if the ticket has comments. Value will be NULL if not.
if(!empty($ticket['comment_list']))
{
foreach(explode('|||', $ticket['comment_list']) as $comment)
{
$attributes = explode(':::', $comment);
$comment_preview = $attributes[0]; // Get first attribute
$date_posted = $attributes[1]; // Get second attribute
}
}
}
的主要缺点之一,因此您必须根据它们在字段值中存在的不可能性来决定哪些分隔符最佳使用。
答案 1 :(得分:2)
CONCAT
问题,我不会发表评论,因为没有太多要补充,但你也问过这是否是最有效的方式,所以我会选择这个。
我真的不喜欢CONCAT
解决方案。这不是SQL的哲学,你永远不会知道用户将在评论区写什么。
就SQL而言,正确的做法是:
SELECT
tickets.*,
comments.*
FROM tickets
LEFT JOIN comments ON comments.ticket_id = tickets.id
WHERE tickets.title LIKE '%whatever%';
然后在PHP中,您只需迭代结果集并构建输出。请注意,LEFT JOIN将正确地返回没有评论的票证。
如果您感到有点不安 - 但不应该! - 拥有每行的故障单信息,您可以在2中分割查询:
SELECT
tickets.*
FROM tickets
WHERE tickets.title LIKE '%whatever%';
和
SELECT
comments.*
FROM comments
INNER JOIN tickets ON comments.ticket_id = tickets.id
WHERE tickets.title LIKE '%whatever%';
这意味着在应用程序方面需要做更多工作,因为您必须将注释链接回故障单。
我的建议:选择一体化查询。
答案 2 :(得分:1)
评论必须有票证ID的记录才能与他们相关联,这样您就可以从票证中获取ID并使用JOIN来获取评论。
我正在做一些与Wordpress DB非常相似的东西,所以我只能按照那种布局去做。在wordpress中,帖子有一个与评论相关的ID。
所以你应该能够用我认为的一个查询来做到这一点。也就是说,如果表的设计允许这种类型的关联 - 它应该是。
SELECT tickets.ID, tickets.title, tickets.content, comments.content
FROM tickets
INNER JOIN comments ON comments.ticket_id = tickets.ID
WHERE tickets.content LIKE '%the search term%'
ORDER BY comments.comment_date
我只掌握SQL的功能,所以这里的其他人可能会以正确的方式纠正我,但我认为它应该有效。此查询的实际搜索元素仅用于示例,我不知道您实际搜索的是什么。
修:
$query = "SELECT tickets.ID ticket_id, tickets.title title, tickets.content content
FROM tickets
WHERE tickets.content LIKE '%the search term%'
AND tickets.title LIKE '%the search term%'";
$tickets = $db->fetch_array($query);
foreach ($tickets as $ticket) {
echo "<h1>$ticket['title']</h1>";
echo "<h1>$ticket['content']</h1>";
$query = "SELECT comments.title title, comments.content content
FROM comments
WHERE comments.ID = {$ticket['ticket_id']}
ORDER BY comments.date DESC
LIMIT 5";
$comments = $db->fetch_array($query);
if ($comments) {
echo "<div class="comments">";
foreach($comments as $comment) {
echo "<div class="comment">";
echo "<h1>$comment['title']</h1>";
echo "<h1>$comment['content']</h1>";
echo "</div>";
}
echo "</div>";
}
}