选择类别树中某处具有category_id的记录

时间:2018-12-01 21:20:38

标签: mysql sql

我正在显示一棵类别树,并且在每个分支旁边,我希望统计相同级别或更低级别的列表数量。

所有列表的catid处于最低类别级别。也就是说,如果可用的类别级别较低,则不能将特定的catid应用于列表。

例如,在下面的“列表”表中,您找不到具有catid = 24的列表,因为这还不是该分支中树的最低级别。

在我的类别数据库中,最多有4个级别(0-3)。

以下是表格:

所有类别(表)

record_id  parent_category_id   parent_id  title        level
--------------------------------------------------------------------- 
24         NULL                 NULL       Real Estate  0
5915       24                   24         Residential  1
7569       5915                 24         For sale     2

列表(表格)

record_id    cat_id
--------------------
1            7569
2            8847

因此,我的HTML类别树应如下所示:

HTML

Categories              Listing count
-------------------------------------
24                      1
  5915                  1
    7569                1

因此,在我的html和jQuery代码中,我将任意级别的特定catid传递给查询,并且该目录应查找该级别或更低级别的列表。

我已经尝试了几个小时,到目前为止我的努力还不值得一试。但是我还是会...

编辑:到目前为止,我的努力(别笑了,经过数小时的尝试,事情变得混乱了):

select l.record_id 
from listings l
where catid in (
    select record_id 
    from all_categories 
    where record_id = 5915)
or catid in (
    select parent_category_id 
    from all_categories 
    where parent_category_id = 5915)
or catid in (
    select parent_id 
    from all_categories 
    where parent_id = 5915)

6 个答案:

答案 0 :(得分:1)

孩子的0级parent_id保留在“ all_categories”表中。

因此,一个孩子可以通过其共同的parent_id与其他孩子链接。
然后同时添加级别0。

困难在于给定的孩子ID不在“列表”表中。
因此,要检索该列表record_id,必须通过类别。

可以在here上找到关于妊娠酯的测试

SELECT 
cat.record_id as catId, 
pl.listId AS ListingCount,
cat.level
FROM 
(
    SELECT 
     cat1.parent_id, 
     MAX(list.record_id) AS listId
    FROM all_categories AS cat1
    JOIN all_categories AS cat2 ON cat2.parent_id = cat1.parent_id
    JOIN listings list ON list.cat_id = cat2.record_id
    WHERE cat1.record_id = 5915
    GROUP BY cat1.parent_id
) AS pl
LEFT JOIN all_categories AS cat ON (cat.parent_id = pl.parent_id OR cat.record_id = pl.parent_id)
ORDER BY cat.record_id, cat.level;

结果:

catId   ListingCount    level
24      1               0
5915    1               1
7569    1               2

查询中还包括“级别”,因为它可用于生成HTML中的类别树。

请注意,如果“列表”表仅包含级别0的cat_id,则可以大大简化查询

答案 1 :(得分:1)

假设树的最大深度为四级,则可以使用多个LEFT JOIN检索完整的树或子树。但是,这不会是超级高效的。考虑以下查询:

SET @subtree_id = 1;

SELECT
    c0.category_id AS c0_id, c0.name AS c0_name,
    c1.category_id AS c1_id, c1.name AS c1_name,
    c2.category_id AS c2_id, c2.name AS c2_name,
    c3.category_id AS c3_id, c3.name AS c3_name,
    l.listing_id
FROM category AS c0
LEFT JOIN category AS c1 ON c1.parent_id = c0.category_id
LEFT JOIN category AS c2 ON c2.parent_id = c1.category_id
LEFT JOIN category AS c3 ON c3.parent_id = c2.category_id
LEFT JOIN listing AS l ON l.category_id = c0.category_id
                       OR l.category_id = c1.category_id
                       OR l.category_id = c2.category_id
                       OR l.category_id = c3.category_id
WHERE c0.category_id = @subtree_id;

它将产生如下结果:

| c0_id | c0_name     | c1_id | c1_name     | c2_id | c2_name   | c3_id | c3_name    | listing_id |
|-------|-------------|-------|-------------|-------|-----------|-------|------------|------------|
| 1     | Real Estate | 2     | Residential | 3     | House     | NULL  | NULL       | NULL       |
| 1     | Real Estate | 2     | Residential | 4     | Apartment | NULL  | NULL       | 1          |
| 1     | Real Estate | 2     | Residential | 4     | Apartment | NULL  | NULL       | 2          |
| 1     | Real Estate | 2     | Residential | 4     | Apartment | NULL  | NULL       | 3          |
| 1     | Real Estate | 2     | Residential | 5     | Condo     | NULL  | NULL       | NULL       |
| 1     | Real Estate | 6     | Commercial  | 7     | Office    | NULL  | NULL       | 4          |
| 1     | Real Estate | 6     | Commercial  | 8     | Retail    | NULL  | NULL       | 5          |
| 1     | Real Estate | 6     | Commercial  | 9     | Other     | 10    | Industrial | 6          |

不幸的是,它仅包含完整路径。要达到预期的结果,只需将每行分成4行:

SET @subtree_id = 1;

SELECT
    CASE WHEN level >= 0 THEN c0_id END AS c0_id, CASE WHEN level >= 0 THEN c0_name END AS c0_name,
    CASE WHEN level >= 1 THEN c1_id END AS c1_id, CASE WHEN level >= 1 THEN c1_name END AS c1_name,
    CASE WHEN level >= 2 THEN c2_id END AS c2_id, CASE WHEN level >= 2 THEN c2_name END AS c2_name,
    CASE WHEN level >= 3 THEN c3_id END AS c3_id, CASE WHEN level >= 3 THEN c3_name END AS c3_name,
    COUNT(listing_id) AS lc
FROM (
    SELECT
        c0.category_id AS c0_id, c0.name AS c0_name,
        c1.category_id AS c1_id, c1.name AS c1_name,
        c2.category_id AS c2_id, c2.name AS c2_name,
        c3.category_id AS c3_id, c3.name AS c3_name,
        l.listing_id
    FROM category AS c0
    LEFT JOIN category AS c1 ON c1.parent_id = c0.category_id
    LEFT JOIN category AS c2 ON c2.parent_id = c1.category_id
    LEFT JOIN category AS c3 ON c3.parent_id = c2.category_id
    LEFT JOIN listing AS l ON l.category_id = c0.category_id
                           OR l.category_id = c1.category_id
                           OR l.category_id = c2.category_id
                           OR l.category_id = c3.category_id
    WHERE c0.category_id = @subtree_id
) AS paths
INNER JOIN (
    SELECT 0 AS level UNION ALL
    SELECT 1 UNION ALL
    SELECT 2 UNION ALL
    SELECT 3
) AS levels ON level = 0 AND c0_id IS NOT NULL
            OR level = 1 AND c1_id IS NOT NULL
            OR level = 2 AND c2_id IS NOT NULL
            OR level = 3 AND c3_id IS NOT NULL
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8
ORDER BY 2, 1, 4, 3, 6, 5, 8, 7

Demo on DB<>Fiddle

答案 2 :(得分:1)

您有一个神秘的数据结构,但是问题似乎并不那么困难。这似乎可以满足您的要求:

select c2.record_id, count(*)
from listings l join
         all_categories c
         on l.cat_id = c.record_id join
         all_categories c2
         on c2.record_id in (c.record_id, c.parent_category_id, c.parent_id)
group by c2.record_id, l.cat_id
order by l.cat_id, c2.record_id;

这个想法很简单。表all_categories具有完整的层次结构。基本上,您需要将层次结构的所有级别移动到单独的中,以便可以对其进行汇总。

joinc2就是这样做的。剩下的只是聚合。

答案 3 :(得分:0)

在mysql 8中,有一个(with)函数可以递归运行,您可以获取catId的级别

  

结构->(id cat parent_id)   ->(1,24,NULL),(2,5915,1),(3,7569,2),(4,3,1)

WITH recursive parent(id,parent_id,Levels) AS
(
    select id,parent_id, 0 as Levels from table where id =1 //condition
    union all
    select c.id,c.parent_id,(Levels+1) as Levels from table as c  inner join parent as p1 on p1.id = c.parent_id
)
SELECT *
FROM  parent

唯一的限制是它在我的sql版本8.x中,其余代码可以正常运行,您可以问这种方法的任何问题称为cet希望对您有所帮助

答案 4 :(得分:0)

对于每个record_id,左键联接子代,然后计算每个级别的唯一值并将它们加在一起。

select n.record_id
count(distinct n.record_id)+count(distinct n2.record_id)+count(distinct 
n3.record_id)+count(distinct n4.record_id) listingcount
from all_categories n
left join all_categories n2 on n.record_id=n2.parent_category_id
left join all_categories n3 on n2.record_id=n3.parent_category_id
left join all_categories n4 on n3.record_id=n4.parent_category_id
group by n.record_id

答案 5 :(得分:0)

我相信这适合于递归sql。

CREATE FUNCTION f_total_listings(@root_id INT) RETURNS INT AS
BEGIN
    DECLARE @listing_count INT;
    WITH cat (record_id, parent_category_id) AS
    (
        SELECT root.record_id, root.parent_category_id
        FROM all_categories AS root
        WHERE root.parent_category_id = @root_id
        UNION ALL
        SELECT child.record_id, child.parent_category_id
        FROM cat AS parent, all_categories AS child
        WHERE parent.record_id = child.parent_category_id
    )
    SELECT @listing_count = count(*)
    FROM listings l
    JOIN cat ON l.cat_id = cat.record_id;
    RETURN @listing_count;
END;

SELECT record_id, f_total_listings(record_id) FROM all_categories