2011-04-03 25 views
14

我正在構建一個簡單的網絡應用程序,那時我會有一天開源。就目前而言,導航是在每一次頁面載入時生成的(這將改變爲一天被緩存),但目前,它是由下面的代碼製作的。使用PHP 5.2.6和MySQLi 5.0.7.7,下面的代碼效率如何?我認爲加入可能會有所幫助,但我接受了建議。任何提示將非常感謝。低效的SQL查詢

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

+1張貼了這個問題;循環查詢他們是壞壞壞。 JOIN不足以幫助你,但還有很多其他人! – Bojangles 2011-04-03 12:24:36

回答

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。

這段代碼僅僅是爲了展示處理一個查詢得到的分層數據的基本思想。

+0

不得不微動UL&LI標籤,但理論是完美的。導航現在加載速度提高2到3倍。謝謝,本斯! :) – PaulAdamDavis 2011-04-06 23:13:34

+0

爲什麼這個問題有一個賞金? – JohnP 2011-04-07 08:42:48

+0

@JohnP我對其他(也許更好)解決方案感興趣。我發了兩個問題,但他們都有缺點。如果還有更多的方法,我真的很感興趣。 – vbence 2011-04-07 08:57:24

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

首先,你不應該在你的視圖中查詢你的數據庫。這將混合您的業務邏輯和演示邏輯。只需將查詢結果分配給控制器中的變量並遍歷它即可。

至於查詢,yup加入可以在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; 
    } 
} 

此行應該給你已在陣列中的所有相關片斷類別的數組。你可以簡單地遍歷這個數組來重現你的列表。

+0

MVC不是唯一可行的方法。 – vbence 2011-04-03 13:11:52

+1

是,但將數據層分開是個好主意。 – JohnP 2011-04-03 13:15:13

+0

不是必然的。你總是要考慮利弊。在這種情況下,有一個帶有HTML輸出的查詢,整個過程需要5分鐘的時間才能完成,您可以隨時將它扔掉,而不會感到很心痛。作爲回報,您將獲得沒有任何抽象層和額外內存佔用的代碼,每次加載頁面時都會運行這些代碼。同時增加了這部分數據庫變化的概率,它可以使情況變得輕量級的「支出」代碼。 - 不用說我不反對抽象層,但在任何情況下它們都不一定需要*。 – vbence 2011-04-03 13:36:05

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