如何优化SQL查询以避免while()嵌套子查询?

问题描述:

我搜索了一下东西,以优化以下嵌套查询while(),但不幸的是我无法适应它为我的情况。如何优化SQL查询以避免while()嵌套子查询?

让我们想象一下下面的数据库关系:

ITEM 0,5---1,n ITEM_TAG 1,n---0,n TAG 

ITEM (id, name, ...) 
ITEM_TAG (id_item, id_tag) 
TAG (id, name, ...) 

现在,我们希望获得所有与他们的标签(在5限制)的项目。

我做什么是丑陋至极之后需要进行优化:

$req=mysql_query("SELECT id, name FROM ITEM WHERE 1 ORDER BY id DESC"); 
while($res=mysql_fetch_array($req)) 
{ 
    $items.="<h1>".$res['name']."</h1>"; 
    $req_tag=mysql_query("SELECT id, name 
         FROM TAG, ITEM_TAG 
         WHERE ITEM_TAG.id_item='".$res['id']."' 
         AND ITEM_TAG.id_tag=TAG.id 
         LIMIT 5"); 
    while($res_tag=mysql_fetch_array($req_tag)) 
    $items.="&bull; ".$res_tag['name']."<br/>"; 
} 

另一种方式可以选择所有的项目和他们的标签和一个数组进行筛选,如下所示:

$req=mysql_query("SELECT ITEM.id AS 'item_id', 
         ITEM.name AS 'item_name', 
         TAG.id AS 'tag_id', 
         TAG.name AS 'tag_name' 
         FROM ITEM, ITEM_TAG, TAG 
         WHERE ITEM_TAG.id_item=ITEM.id 
         AND ITEM_TAG.id_tag=TAG.id 
         ORDER BY ITEM.id DESC"); 
    while($res=mysql_fetch_array($req)) 
    { 
    $listTag[$res['item_id']][$res['tag_id']]=$res['tag_name']; 
    $listItem[$res['item_id']]=$res['item_name']; 
    } 
    foreach($listItem as $item_id=>$item_name) 
    { 
    $items.="<h1>".$val."</h1>"; 
    foreach($listTag[$id_item] $tag_id=>$tag_name) 
    $items.="&bull; ".$tag_name."<br/>"; 
    } 

任何想法或建议,做得更好,更快,更轻(并不像Daft朋克更强))?

部分LEFT JOIN,RIGHT JOIN或将tag1,tag2 ... tag5字段放入ITEM表或其他? 但请注意,ITEMs可能有0到5个TAG相关联...

+0

我有点谈论从数据库获取的结果和之前缓存他们PHP的时候犹豫了;但是您可以使用MySQLi/PDO和准备好的语句尝试当前的方法,因此您只需准备一次'SELECT id,name FROM tag ...'并在不重建逻辑的情况下获得相对较快的性能。 – Passerby 2013-02-19 04:37:05

+0

感谢您的回复,但我猜测查询循环会一样吗?即使有一些库MySQLi或PDO,学说... – Valky 2013-02-19 04:39:23

+0

你需要两个语句:'$项目= $ mysqli->准备( “SELECT ID FROM项...”); $标签= $ mysqli->准备(” SELECT name FROM tag ... WHERE id_item =?“)'。你仍然需要在循环内执行'$ tag',但'$ tag'已经准备好了,所以对于数据库来说它和直接的'mysql_query'完全不同,因此性能更好。 – Passerby 2013-02-19 04:44:24

这对于评论太长,所以我在这里发布。

当在输出之前对PHP中的DB结果进行缓存时,我总是会有点犹豫,因为这对* 数据库 * base和PHP的关系并不“直观”。

在这种情况下,特别是如果你有一个非常大的ITEM表,你会之前,你可以输出是缓存在PHP中有很多字符串。

所以我认为你可以使用目前的逻辑,并采取事先准备好的声明的优势:

$req=$mysqli->query("SELECT id, name FROM ITEM WHERE 1 ORDER BY id DESC"); 
$tag=$mysqli->prepare("SELECT id, name FROM tag, item_tag WHERE item_tag.id_item=? AND item_tag.id_tag=tag.id LIMIT 5"); 
$tag->bind_param("i",$tid); 
while($res=$req->fetch_assoc()) 
{ 
    echo "<h1>".$res['name']."</h1>"; 
    $tid=intval($res["id"]); 
    $tag->execute(); 
    $req_tag=$tag->get_result(); 
    while($res_tag=$req_tag->fetch_assoc()) 
    { 
    echo "&bull; ".$res_tag['name']."<br/>"; 
    } 
} 

上面的代码没有进行测试,但我认为你可以得到的想法。

+0

非常感谢,它似乎很有趣,即使我认为(我不知道为什么大声笑)查询将循环完成。要尝试这种性能,我会告诉你。无论如何。 – Valky 2013-02-19 05:20:19

+0

@Valky该查询其实也可以“做”的循环,但有一个显著不同(特别是如果你有一个大'ITEM'表)与'mysql_query',那'mysqli'保持与不同的执行相同的查询值,而'mysql'每次都会提出一个新的查询。 – Passerby 2013-02-19 07:05:50