Php 低效的SQL查询
我正在构建一个简单的web应用程序,总有一天我会开源的。目前,nav是在每次页面加载时生成的(有一天会被缓存),但目前,它是用下面的代码生成的。使用PHP5.2.6和MySQLI5.0.7.7,下面的代码有多高效?我想加入也许会有帮助,但我需要的是建议。任何提示都将不胜感激Php 低效的SQL查询,php,mysql,database,performance,mysqli,Php,Mysql,Database,Performance,Mysqli,我正在构建一个简单的web应用程序,总有一天我会开源的。目前,nav是在每次页面加载时生成的(有一天会被缓存),但目前,它是用下面的代码生成的。使用PHP5.2.6和MySQLI5.0.7.7,下面的代码有多高效?我想加入也许会有帮助,但我需要的是建议。任何提示都将不胜感激 <?php $navQuery = $mysqli->query("SELECT id,slug,name FROM categories WHERE live=1 ORDER BY name ASC")
<?php
$navQuery = $mysqli->query("SELECT id,slug,name FROM categories WHERE live=1 ORDER BY name ASC") or die(mysqli_error($mysqli));
while($nav = $navQuery->fetch_object()) {
echo '<li>';
echo '<a href="/'. $nav->slug .'">'. $nav->name .'</a>';
echo '<ul>';
$subNavQuery = $mysqli->query("SELECT id,name FROM snippets WHERE category='$nav->id' ORDER BY name ASC") or die(mysqli_error($mysqli));
while($subNav = $subNavQuery->fetch_object()) {
echo '<li>';
echo '<a href="/'. $nav->slug .'/'. $subNav->name .'">'. $subNav->name .'</a>';
echo '</li>';
}
echo '</ul>';
echo '</li>';
}
?>
我想试试这个:
SELECT
c.slug,c.name,s.name
FROM
categories c
LEFT JOIN snippets s
ON s.category = c.id
WHERE live=1 ORDER BY c.name, s.name
不过,我没有测试它。还要使用语句检查索引,以便MySQL不会对表进行完整扫描
有了这些结果,您可以在PHP中循环结果,并在类别名称更改时进行检查,然后根据需要生成输出。首先,您不应该在视图中查询数据库。这将混合您的业务逻辑和表示逻辑。只需将查询结果分配给控制器中的一个变量并对其进行迭代即可
至于查询,是的,一个连接可以在一个查询中完成
SELECT * -- Make sure you only select the fields you want. Might need to use aliases to avoid conflict
FROM snippets S LEFT JOIN categiries C ON S.category = C.id
WHERE live = 1
ORDER BY S.category, C.name
这将得到一个初始结果集。但这并不能像您预期的那样为您提供有序的数据。您需要使用一些PHP将其分组到一些数组中,以便在循环中使用
类似于
$categories = array();
foreach ($results as $result) {
$snippet = array();
//assign all the snippet related data into this var
if (isset($categories[$result['snippets.category']])) {
$categories[$result['snippets.category']]['snippet'][] = $snippet;
} else {
$category = array();
//assign all the category related data into this var;
$categories[$result['snippets.category']]['snippet'] = array($snippet);
$categories[$result['snippets.category']]['category'] = $category;
}
}
这将为您提供一个类别数组,其中包含数组中的所有相关片段。您可以简单地循环此数组以复制列表 您可以运行此查询:
SELECT c.id AS cid, c.slug AS cslug, c.name AS cname,
s.id AS sid, s.name AS sname
FROM categories AS c
LEFT JOIN snippets AS s ON s.category = c.id
WHERE c.live=1
ORDER BY c.name, s.name
然后迭代结果以创建适当的标题,如:
// last category ID
$lastcid = 0;
while ($r = $navQuery->fetch_object ()) {
if ($r->cid != $lastcid) {
// new category
// let's close the last open category (if any)
if ($lastcid)
printf ('</li></ul>');
// save current category
$lastcid = $r->cid;
// display category
printf ('<li><a href="/%s">%s</a>', $r->cslug, $r->cname);
// display first snippet
printf ('<li><a href="/%s/%s">%s</a></li>', $r->cslug, $r->sname, $r->sname);
} else {
// category already processed, just display snippet
// display snippet
printf ('<li><a href="/%s/%s">%s</a></a>', $r->cslug, $r->sname, $r->sname);
}
}
// let's close the last open category (if any)
if ($lastcid)
printf ('</li></ul>');
//最后一个类别ID
$lastcid=0;
而($r=$navQuery->fetch\u object()){
如果($r->cid!=$lastcid){
//新类别
//让我们关闭最后一个打开的类别(如果有)
如果($lastcid)
printf(“”);
//保存当前类别
$lastcid=$r->cid;
//显示类别
printf(“”,$r->cslug,$r->cname);
//显示第一个代码段
printf(“ ”,$r->cslug,$r->sname,$r->sname);
}否则{
//类别已处理,仅显示代码段
//显示片段
printf(“”,$r->cslug,$r->sname,$r->sname);
}
}
//让我们关闭最后一个打开的类别(如果有)
如果($lastcid)
printf(“ ”);
请注意,我使用了printf
,但您应该使用自己的函数,该函数围绕printf,但通过参数运行htmlspecialchars
(当然,第一个除外)
免责声明:我不一定鼓励使用
s
这里的代码只是为了展示处理通过一个查询获得的分层数据的基本思想。除了单个组合查询之外,还可以使用两个单独的查询
这里有一个基本的树结构,包含分支元素(categories表)和叶元素(snippets表)。单一查询解决方案的缺点是,对于每个单独的叶元素,都会重复获取owner brach元素。这是冗余信息,根据叶的数量和从每个分支元素查询的信息量,可能会产生大量额外流量
两个查询解决方案如下所示:
$navQuery = $mysqli->query ("SELECT id, slug, name FROM categories WHERE live=1 ORDER BY name")
or die (mysqli_error ($mysqli));
$subNavQuery = $mysqli->query ("SELECT c.id AS cid, s.id, s.name FROM categories AS c LEFT JOIN snippets AS s ON s.category=c.id WHERE c.live=1 ORDER BY c.name, s.name")
or die (mysqli_error ($mysqli));
$sub = $subNavQuery->fetch_object (); // pre-reading one record
while ($nav = $navQuery->fetch_object ()) {
echo '<li>';
echo '<a href="/'. $nav->slug .'">'. $nav->name .'</a>';
echo '<ul>';
while ($sub->cid == $nav->id) {
echo '<li>';
echo '<a href="/'. $nav->slug .'/'. $sub->name .'">'. $sub->name .'</a>';
echo '</li>';
$sub = $subNavQuery->fetch_object ();
}
echo '</ul>';
}
$navQuery=$mysqli->query(“从类别中选择id、slug、name,其中live=1按名称排序”)
或者死亡(mysqli_错误($mysqli));
$subNavQuery=$mysqli->query(“从类别中选择c.id作为cid、s.id、s.name作为c左连接片段作为s ON s.category=c.id,其中c.live=1按c.name、s.name排序”)
或者死亡(mysqli_错误($mysqli));
$sub=$subNavQuery->fetch_object();//预读一条记录
而($nav=$navQuery->fetch_对象()){
回音“”;
回声';
回声“”;
而($sub->cid==$nav->id){
回音“- ”;
回声';
回音“
”;
$sub=$subNavQuery->fetch_object();
}
回声“
”;
}
它应该打印与您的示例完全相同的代码
$navQuery = $mysqli->query("SELECT t1.id AS cat_id,t1.slug,t1.name AS cat_name,t2.id,t2.name
FROM categories AS t1
LEFT JOIN snippets AS t2 ON t1.id = t2.category
WHERE t1.live=1
ORDER BY t1.name ASC, t2.name ASC") or die(mysqli_error($mysqli));
$current = false;
while($nav = $navQuery->fetch_object()) {
if ($current != $nav->cat_id) {
if ($current) echo '</ul>';
echo '<a href="/'. $nav->slug .'">'. $nav->cat_name .'</a><ul>';
$current = $nav->cat_id;
}
if ($nav->id) { //check for empty category
echo '<li><a href="/'. $nav->slug .'/'. $nav->name .'">'. $nav->name .'</a></li>';
}
}
//last category
if ($current) echo '</ul>';
$navQuery=$mysqli->query(“选择t1.id作为cat\u id,t1.slug,t1.name作为cat\u name,t2.id,t2.name
从类别中选择t1
在t1.id=t2.category上将代码段左连接为t2
其中t1.live=1
按t1.name ASC、t2.name ASC)或die(mysqli_错误($mysqli))订购;
$current=false;
而($nav=$navQuery->fetch_object()){
如果($current!=$nav->cat_id){
如果($current)回显“”;
回声“”;
$current=$nav->cat_id;
}
如果($nav->id){//检查空类别
回音“”;
}
}
//最后一类
如果($current)回显“
”;
+1用于发布此问题;其中包含查询的循环不好。我没有足够好的连接来帮助你,但是还有很多其他人!MVC不是唯一可能的方法。是的,但是将数据层分开总是一个好主意。@v因此,假设代码的这一部分是独立的是相当危险的。OP明确表示他正在构建一个web应用程序。这正是将受益的项目类型<代码>。将DB的这一部分发生变化的概率添加到等式中。
正是您应该有一个摘要的原因。否则,你会因为顺序改变或其他琐事而浏览所有页面。@PaulAdamDavis No,No,No。在使用任何框架之前,你必须使用php的“本机”工具创建一个项目。我个人同意@JohnP的解决方案,但我缺少的一点是,没有人建议对列使用适当的索引,到目前为止,请确保在columns live和category中设置索引,@PaulAdamDavis还请记住,如果进行联接,则用于联接的列的顺序必须与索引定义中的列的顺序相匹配。必须抖动UL&LI标记,但理论是完美的。导航现在加载速度提高了2到3倍。谢谢你,本斯!:)为什么这个问题有悬赏?@JohnP我对其他(也许更好)的解决方案感兴趣。