在尽可能少的SQL语句中获取文件夹ID


Getting a folder ID in as few SQL statements as possible

考虑数据库表:文件夹

文件夹id parent_id名称1 0 a2 1 b3 2 c

"b"是"a"中的一个文件夹,因此它的parent_id是"a的id"
parent_id为0的文件夹只是表示它在根文件夹中。

我在php中写了一个递归函数,它可以帮助我获得我感兴趣的路径的ID

例如:

echo get_folder_id('a/b/c'); // 3 (3 SQL queries)  
echo get_folder_id('a'); // 1 (1 SQL query)  
echo get_folder_id('a/b'); // 2 (2 SQL queries)  
echo get_folder_id('a/c'); // false (2 SQL queries)

问题:对于路径中的每个文件夹,我都必须对DB服务器进行SQL查询。

问题:如果路径是"a/b/c",有没有办法减少查询次数?

这是我目前的工作解决方案供参考。

function get_folder_id($path, $parent_id=0) {
    $path = explode('/', $path);
    $id = 0;
    //if there's only 1 folder in the path, query the database for the ID
    if (count($path) == 1) {
        $rs = $this->db->select('id')
                ->from('folders')
                ->where('name', $path[0])
                ->where('parent_id', $parent_id)
                ->limit(1)
                ->get();
        if ($rs->num_rows() == 0) return FALSE;
        $result = $rs->row_array();
        return $result['id'];
    }
    foreach($path AS $i=>$p) {
        if ($i==0 && $p=='') continue; //if a path starts with / move on to the next item
        $id = $this->get_folder_id($p, $id);
    }
    return $id;
}

注意:我使用的是CodeIgniter框架,但这与我的问题无关。只是为了让那些想知道我为什么使用$this->get_folder_id()和$this->db

的人

对于您的问题,有一个非常简单的解决方案:

在数据库中创建一个递归存储函数,并从PHP调用它。这应该是DB功能,因为如果您在PHP中实现这一功能,则必须从PHP发送许多查询。

我刚刚提出的一个解决方案是在同一个表上使用一系列LEFT JOIN。

SELECT tb3.id FROM folders AS tb1
LEFT JOIN folders AS tb2 ON tb2.parent_id=tb1.id
LEFT JOIN folders AS tb3 ON tb3.parent_id=tb2.id
WHERE tb1.name='a' AND tb2.name='b' AND tb3.name='c'
LIMIT 1

根据路径中文件夹的数量,我可以动态生成上述SQL。我不确定这是否是一个完美的解决方案,但现在它正在按预期工作。

任何关于这是否是一个好主意的反馈都将不胜感激!

$path = 'a/b/c';
$path = explode('/', $path);
$sql = "SELECT tb".count($path).".id FROM folders AS tb1";
for($x=1, $max = count($path); $x<=$max; $x++) {
    $sql .= " LEFT JOIN folders AS tb".($i+2);
    $sql .= " ON tb".($i+2).".parent_id = tb"($i+1).".id";
}
$sql .= " WHERE 1";
foreach($path AS $x => $p) {
    $sql .= " AND tb".($x=1).".name='".$p."'";
}
$sql .= " LIMIT 1";