2010-09-04 37 views
1

我一直在与一些SQL进行对抗,似乎无法摆脱困境。在MySQL中计算属于某个类别的记录

我有两张表,一张带有类别列表,另一张带有我的所有文章。

我想要做的是找到每个类别有多少篇文章。

这里是SQL我到目前为止

SELECT DISTINCT COUNT(po.post_Cat_ID) AS Occurances, ca.cat_Title 
FROM Posts po, Categories ca 
WHERE ca.cat_ID = LEFT(po.post_Cat_ID, 2) 

我用剩下的就是只得到的主要类别为我列出类别披露如下...例如

Science = 01 
Medicine = 0101 
Sport = 02 
原因

说asprin的帖子因此将有一个cat_ID作为0101.(然后,LEFT会将0101,0102,0103等修整为01)。基本上我不感兴趣的子类别。

在此先感谢


结果

SELECT DISTINCT COUNT(po.post_Cat_ID) AS Occurances, ca.cat_Title 
FROM Posts po, Categories ca 
WHERE ca.cat_ID = LEFT(po.post_Cat_ID, 2) 
GROUP BY LEFT(po.post_Cat_ID, 2) 

附:感谢@nullpointer,它的工作原理就目前而言,我会考虑重组 其他读者继承人的链接再次

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

+1

您是否尝试过使用post_Cat_ID分组? – DrColossos 2010-09-04 13:40:56

+0

顺便说一下,子类别的处理看起来相当复杂和低效... – DrColossos 2010-09-04 13:41:35

+0

是工作感谢 – Stevanicus 2010-09-04 14:17:07

回答

0

让我建议你到重组架构来代替。你在这里想要表示一个层次结构(类别),这对于关系数据库来说并不简单。两种常见的解决方案是邻接列表和嵌套集合。

邻接列表更直接的树状结构。您将有一个categories表所示:

id | name  | parent 
------------------------ 
1 | Science | null 
2 | Sports | null 
3 | Medicine | 1 

不幸的是这种模式是很难使用SQL工作。相反,我们可以使用嵌套集合方法。这里每个节点具有lftrgt值节点,其将在父节点的值lftrgt之间。在您的例子中,你将有:

id | name  | lft | rgt 
------------------------------- 
1 | Science | 1 | 4  
2 | Sports | 5 | 6 
3 | Medicine | 2 | 3 

因此,为了检索某一类别的计数,你可以简单地查询有你想要的类别之间的lftrgt价值节点的数量。例如:

SELECT COUNT(*) 
    FROM articles a 
LEFT JOIN categories c ON a.category_id = c.id 
    WHERE lft BETWEEN 1 AND 4 
     AND rgt BETWEEN 1 AND 4 

假设你article表如下所示:

id | ... | category_id 

这更详细讨论在:
http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/


我会提出另一种解决方案:使用标签而不是类别。您可以为给定的文章使用多个标签,并只需获取与某个标签匹配的所有文章的计数。这将更容易处理,并且给你更多的灵活性。

要做到这一点,你需要的文章和标签之间的许多一对多的关系,通常用接线表来实现:

tags 
id | name 

articles_tags # the junction table 
article_id | tag_id 

要标记的文章,你只需INSERT多个条目进入带有正确的article_idtag_idarticles_tags表。然后,您可以像往常一样使用JOIN来得到您想要的。

+0

嵌套集合聪明和酷,但一个痛苦的工作。我建议OP确信他需要在改变任何事情之前给他的东西。 – 2010-09-04 13:54:27

0

给类别添加一列,给出每个类别所在的主类别(主类别给予自己)。因此:

cat_id | main_cat_id | title 
-------+-------------+--------- 
01  | 01   | Science 
0101 | 01   | Medicine 
02  | 02   | Sport 

请从cat_id = main_cat_id中选择以查找主要类别;在left.cat_id = right.main_cat_id上回到自身上以查找子类别,然后在cat_id = cat_id上的帖子上。通过left.cat_id分组,并通过cat_id和count(*)进行项目。

我在PostgreSQL 8.4中试过这个,我不明白为什么这在MySQL中不起作用,因为查询是非常基本的。我的表:

create table categories(
    cat_id varchar(40) primary key, 
    main_cat_id varchar(40) not null references categories, 
    title varchar(40) not null 
) 

create table posts (
    post_id integer primary key, 
    cat_id varchar(40) not null references categories, 
    title varchar(40) not null 
) 

我的查询(按标题,而不是ID分组):

select m.title, count(*) 
from categories m, categories c, posts p 
where m.cat_id = c.main_cat_id 
    and c.cat_id = p.cat_id 
group by m.title 

更新:我也有在做一个字符串操作这项工作了一枪,作为OP尝试。查询(在PostgreSQL接受的标准兼容的SQL中,而不是MySQL的方言)是:

select m.title, count(*) 
from categories m, posts p 
where m.cat_id = substring(p.cat_id from 1 for 2) 
group by m.title; 

哪个工作正常。我无法就速度提供有意义的比较,但查询计划看起来比双向连接看起来简单一些。

+0

这是我在我的答案中解释的邻接表方法的一种变体,当您拥有多个(子)类别的级别时,这会变得非常麻烦。 – NullUserException 2010-09-04 14:26:01

+0

@NullUserException:在某种程度上。如果您的数据库可以执行递归查询(Oracle,PostgreSQL,Firebird和SQL Server都可以 - 不确定MySQL),那么它会变得稍微棘手,而不是非常麻烦,并且比嵌套更容易(并且AIUI更快)。但是由于OP没有表现出对多个子类别的兴趣,这似乎没有意义。 – 2010-09-04 16:18:59