【问题标题】:PHP + SQL, correct tre with parent and child category and item [closed]PHP + SQL,正确的带有父子类别和项目的tre [关闭]
【发布时间】:2020-06-25 04:33:26
【问题描述】:

我正在尝试显示正确的数据树,但我是股票,所以我希望有人能指出我正确的方向。

我已经尝试过使用 PHP 代码和纯 SQL 代码,让我先展示一下到目前为止我尝试过的内容。

public function supported($ids) {
        
        $arr = json_decode($ids, true);
        
        $STH = $this->dbh->query("SELECT cid,device FROM supported_devices WHERE id IN (".implode(',',$arr).")");
        
        $a = $STH->fetchAll();
        
        if(count($a)) {
            
            foreach($a as $b) {
                $newArr[] = $b['cid'];
            }
            
            $STH = $this->dbh->query("SELECT cid,cat_name FROM supported_devices_cats WHERE id IN (".implode(',',$newArr).")");

            $c = $STH->fetchAll();
            
            foreach($c as $d) {
                $newArr2[] = $d['cid'];
            }
            
            $STH = $this->dbh->query("SELECT cat_name FROM supported_devices_cats WHERE id IN (".implode(',',$newArr2).")");

            $e = $STH->fetchAll();
            
            foreach($e as $parent) {
                $return[] = $parent['cat_name'];
                foreach($c as $child) {
                    $return[] = $child['cat_name'];
                    foreach($a as $device) {
                        $return[] = $device['device'];
                    }
                }
            }
            
            return $return;
            
        } else {
            return array();
        }
    }
SELECT DISTINCT c.*
FROM supported_devices_cats c
    LEFT JOIN supported_devices_cats pc
    ON c.id = pc.cid
WHERE
    c.cid IS NOT NULL 
    OR (c.cid IS NULL
       AND pc.cid IS NULL)

我正在尝试首先显示父类别,然后是所有子类别,然后是每个子类别下的正确设备。我不知道我还能如何解释它,我添加了一幅画,也许这更好地解释了我正在尝试做的事情。

更新,我需要将我的 MySQL 服务器升级到更高版本。

【问题讨论】:

    标签: php mysql sql pdo categories


    【解决方案1】:

    我不太确定为什么您需要在表supported_devices 中添加一个“cid”列。外键只需 id 即可。

    话虽如此,这是我用作数据库架构和测试内容的内容:

    CREATE DATABASE cats_devs;
    
    USE cats_devs
    
    CREATE TABLE supported_devices_cats(
        id INT PRIMARY KEY,
        cid INT NULL DEFAULT NULL,
        FOREIGN KEY(cid) REFERENCES supported_devices_cats(id),
        cat_name VARCHAR(64) NOT NULL,
        dt DATETIME NOT NULL DEFAULT NOW()
    );
    
    CREATE TABLE supported_devices(
        id INT,
        FOREIGN KEY(id) REFERENCES supported_devices_cats(id),
        device VARCHAR(128) NOT NULL,
        dt DATETIME NOT NULL DEFAULT NOW()
    );
    
    INSERT INTO supported_devices_cats(id, cid, cat_name) VALUES
    (1, NULL, "Disk Drives"), (2, NULL, "Monitors"),
    (3, 1, "SSD"), (4, 1, "HDD"), 
    (5, 2, "TN"), (6, 2, "IPS");
    
    INSERT INTO supported_devices(id, device) VALUES
    (4, "Seagate 2TB"), (4, "WD 2TB"),
    (3, "Corsair 256GB"), (3, "WD 256GB"),
    (5, "ASUS 27in"), (5, "ASUS 21in"),
    (6, "Viewsonic 27in"), (6, "LG 24in");
    

    接下来我要做的是 CTE 递归查询(在较新的 MySQL/MariaDB 服务器中可用 - 对于旧版本,需要与 SELF JOIN 等效):

    WITH RECURSIVE hier_query(id, cat_id, cat_name, dev_name, dt) AS(
        SELECT id, cid, cat_name, CAST(NULL AS VARCHAR(128)), dt
        FROM supported_devices_cats
        WHERE cid IS NULL
        UNION ALL
        SELECT supported_devices_cats.id, supported_devices_cats.cid, 
               supported_devices_cats.cat_name, supported_devices.device, supported_devices_cats.dt
        FROM supported_devices_cats
        JOIN hier_query
             ON hier_query.id = supported_devices_cats.cid
        LEFT JOIN supported_devices ON supported_devices.id = supported_devices_cats.id
    )
    SELECT id, cat_id, cat_name, dev_name, dt
    FROM hier_query;
    

    这将产生以下结果,其中包含所有必需的信息:

    +------+--------+-------------+----------------+---------------------+
    | id   | cat_id | cat_name    | dev_name       | dt                  |
    +------+--------+-------------+----------------+---------------------+
    |    1 |   NULL | Disk Drives | NULL           | 2020-06-25 07:52:39 |
    |    2 |   NULL | Monitors    | NULL           | 2020-06-25 07:52:39 |
    |    3 |      1 | SSD         | Corsair 256GB  | 2020-06-25 07:52:39 |
    |    3 |      1 | SSD         | WD 256GB       | 2020-06-25 07:52:39 |
    |    4 |      1 | HDD         | Seagate 2TB    | 2020-06-25 07:52:39 |
    |    4 |      1 | HDD         | WD 2TB         | 2020-06-25 07:52:39 |
    |    5 |      2 | TN          | ASUS 27in      | 2020-06-25 07:52:39 |
    |    5 |      2 | TN          | ASUS 21in      | 2020-06-25 07:52:39 |
    |    6 |      2 | IPS         | Viewsonic 27in | 2020-06-25 07:52:39 |
    |    6 |      2 | IPS         | LG 24in        | 2020-06-25 07:52:39 |
    +------+--------+-------------+----------------+---------------------+
    

    现在在这个结果表中,当 dev_name 为 NULL 时,它是一个父类别。您可以通过这种方式从中获取所有父类别。然后对于每个父类别,当您将 id 与 cat_id 进行比较时,您可以找到子类别。终于可以轻松搞定所有设备了。

    老实说,我试图使其成为“全 SQL”解决方案,但我很难按照您想要的方式对该表进行排序。我敢打赌,其他人会做得更好。

    附:这是排序问题的快速肮脏技巧。我用“->”分隔符连接子名称之前的主要类别。这样您以后可以轻松拆分它们。

    WITH RECURSIVE hier_query(id, cat_id, cat_name, dev_name, dt) AS(
        SELECT id, cid, cat_name, CAST(NULL AS VARCHAR(128)), dt
        FROM supported_devices_cats
        WHERE cid IS NULL
        UNION ALL
        SELECT supported_devices_cats.id, supported_devices_cats.cid, 
               CONCAT(hier_query.cat_name, "->", supported_devices_cats.cat_name), 
               supported_devices.device, supported_devices_cats.dt
        FROM supported_devices_cats
        JOIN hier_query
             ON hier_query.id = supported_devices_cats.cid
        LEFT JOIN supported_devices ON supported_devices.id = supported_devices_cats.id
    )
    SELECT id, cat_id, cat_name, dev_name, dt
    FROM hier_query
    ORDER BY cat_name;
    

    产生:

    +------+--------+------------------+----------------+---------------------+
    | id   | cat_id | cat_name         | dev_name       | dt                  |
    +------+--------+------------------+----------------+---------------------+
    |    1 |   NULL | Disk Drives      | NULL           | 2020-06-25 07:52:39 |
    |    4 |      1 | Disk Drives->HDD | Seagate 2TB    | 2020-06-25 07:52:39 |
    |    4 |      1 | Disk Drives->HDD | WD 2TB         | 2020-06-25 07:52:39 |
    |    3 |      1 | Disk Drives->SSD | Corsair 256GB  | 2020-06-25 07:52:39 |
    |    3 |      1 | Disk Drives->SSD | WD 256GB       | 2020-06-25 07:52:39 |
    |    2 |   NULL | Monitors         | NULL           | 2020-06-25 07:52:39 |
    |    6 |      2 | Monitors->IPS    | Viewsonic 27in | 2020-06-25 07:52:39 |
    |    6 |      2 | Monitors->IPS    | LG 24in        | 2020-06-25 07:52:39 |
    |    5 |      2 | Monitors->TN     | ASUS 27in      | 2020-06-25 07:52:39 |
    |    5 |      2 | Monitors->TN     | ASUS 21in      | 2020-06-25 07:52:39 |
    +------+--------+------------------+----------------+---------------------+
    

    您甚至可以添加“WHERE cat_id IS NOT NULL”条件来删除两个无用的行。所有其他行都包含您需要的内容。

    【讨论】:

    • 设备表中的cid是为了知道它属于哪个子类别。感谢您的建议,我将尝试此代码。但是是的,正如您提到自己的那样,仍然缺少正确的排序,我希望其他人可以就如何正确排序给出一点意见。我会玩弄你的代码,看看我能用它做什么。非常感谢朋友。
    • id 也是 cid,因为 cid 是 id 的 FK :) 但是,RDBMS 和 SQL 对这类事情并不友好。
    • 同意 :) 通常我的 SQL 和编码没有问题,但是当它需要多表和正确排序时,有时我会头疼。
    • 快速搞定它: WITH RECURSIVE hier_query(id, cat_id, cat_name, dev_name, dt) AS( SELECT id, cid, cat_name, CAST(NULL AS VARCHAR(128)), dt FROM supported_devices_cats其中 cid 为 NULL UNION ALL SELECT sdc.id, sdc.cid, CONCAT(hier_query.cat_name, "->", sdc.cat_name), supported_devices.device, sdc.dt FROM supported_devices_cats AS sdc JOIN hier_query ON hier_query.id = sdc .cid LEFT JOIN supported_devices ON supported_devices.id = sdc.id ) SELECT id, cat_id, cat_name, dev_name, dt FROM hier_query ORDER BY cat_name;
    • 啊,看起来很糟糕。我将编辑答案并将其添加为那里。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    相关资源
    最近更新 更多