【问题标题】:Counting SQLite rows that might match multiple times in a single query计算单个查询中可能匹配多次的 SQLite 行
【发布时间】:2018-05-21 08:49:10
【问题描述】:

我有一个 SQLite 表,其中有一列包含每行可能属于的类别。每行都有一个唯一的 ID,但可能分为零个、一个或多个类别,例如:

|-------+-------|
| name  | cats  |
|-------+-------|
| xyzzy | a b c |
| plugh | b     |
| quux  |       |
| quuux | a c   |
|-------+-------|

我想获得每个类别中有多少项目的计数。换句话说,输出如下:

|------------+-------|
| categories | total |
|------------+-------|
| a          | 2     |
| b          | 2     |
| c          | 2     |
| none       | 1     |
|------------+-------|

我尝试像这样使用case 语句:

select case
    when cats like "%a%" then 'a'
    when cats like "%b%" then 'b'
    when cats like "%c%" then 'c'
    else 'none'
end as categories,
count(*)
from test
group by categories

但问题是这只计算每一行一次,所以它不能处理多个类别。然后你会得到这个输出:

|------------+-------|
| categories | total |
|------------+-------|
| a          | 2     |
| b          | 1     |
| none       | 1     |
|------------+-------|

一种可能性是使用与类别一样多的union 语句:

select case
    when cats like "%a%" then 'a'
end as categories, count(*)
from test
group by categories
union
select case
    when cats like "%b%" then 'b'
end as categories, count(*)
from test
group by categories
union
...

但这看起来真的很丑,与 DRY 正好相反。

有没有更好的办法?

【问题讨论】:

    标签: sql sqlite


    【解决方案1】:

    修复您的数据结构!您应该有一个表格,每个 name 和每个 category 都有一行:

    create table nameCategories (
        name varchar(255),
        category varchar(255)
    );
    

    那么你的查询就很简单了:

    select category, count(*)
    from namecategories
    group by category;
    

    为什么你的数据结构不好?以下是一些原因:

    • 一列应包含一个值。
    • SQL 的字符串功能非常糟糕。
    • 无法优化执行所需操作的 SQL 查询。
    • SQL 具有用于存储列表的出色数据结构。它被称为 table,而不是 string

    考虑到这一点,这里有一种蛮力方法来做你想做的事:

    with categories as (
          select 'a' as category union all
          select 'b' union all
          . . .
         )
    select c.category, count(t.category)
    from categories c left join
         test t
         on ' ' || t.categories || ' ' like '% ' || c.category || ' %' 
    group by c.category;
    

    如果您已有有效类别表,则不需要 CTE。

    【讨论】:

    • 谢谢!同意应该是这样的,但我们并不总是能够选择我们使用的数据集:) 你能解释一下左连接中发生了什么吗?我正在尝试了解为什么会这样,但没有看到您在 on 部分使用的语法。
    • @KyleBarbour 。 . . LEFT JOIN 只是将所有类别保留在您的列表中,即使 test 中没有匹配项。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-27
    • 1970-01-01
    相关资源
    最近更新 更多