效率低下的SQL查询

时间:2011-04-03 12:21:32

标签: php mysql database performance mysqli

我正在建立一个简单的网络应用程序,我有一天会开源。就目前而言,导航是在每个页面加载时生成的(将在一天内更改为缓存)但是目前,它是使用下面的代码生成的。使用PHP 5.2.6和MySQLi 5.0.7.7,以下代码的效率如何?我认为加入可能会有所帮助,但我会接受建议。任何提示将不胜感激。

<?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>';
    }
?>

5 个答案:

答案 0 :(得分:15)

您可以运行此查询:

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>');

请注意,我使用的是printf,但您应该使用自己的函数来包装printf,但是通过参数运行htmlspecialchars(当然除了第一个)。

免责声明:我不一定鼓励使用<ul> s。

此代码仅用于显示使用一个查询处理分层数据的基本思路。

答案 1 :(得分:3)

首先,您不应在视图中查询数据库。这将混合您的业务逻辑和您的表示逻辑。只需将查询结果分配给控制器中的变量并迭代它。

对于查询,是的,连接可以在1个查询中执行。

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;
  }
}

这应该为您提供一个类别数组,其中包含数组中的所有相关片段。您只需循环遍历此数组即可重现列表。

答案 2 :(得分:1)

我试试这个:

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
但是,我没有测试它。还要使用EXPLAIN语句检查索引,以便MySQL不对表进行全面扫描。

使用这些结果,您可以在PHP中循环结果并检查类别名称何时更改,并根据需要构建输出。

答案 3 :(得分:1)

除了单个组合查询,您还可以使用两个单独的查询。

这里有一个基本的树结构,包含分支元素(类别表)和叶元素(片段表)。单查询解决方案的缺点是您为每个叶元素重复获取所有者brach-element。这是冗余信息,根据叶子数量和您从每个分支元素查询的信息量可能会产生大量额外流量。

双查询解决方案如下:

$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>';
}

答案 4 :(得分:0)

它应该打印完全与示例相同的代码

$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>';