按枚举计数组,包括可能有0计数的枚举值

时间:2018-02-14 17:03:40

标签: mysql

我有一张物品表。其中一个字段是一个类别(由枚举表示)。有些类别没有项目。

所以我这样做了:

select category, count(*) as total from items group by category;
+------------+-------+
| category   | total |
+------------+-------+
| one        |  6675 |
+------------+-------+

我想生成一个这样的表(其中两个是另一个可能的枚举值):

+------------+-------+
| category   | total |
+------------+-------+
| one        |  6675 |
+------------+-------+
| two        |  0    |
+------------+-------+

如何使用mysql SQL查询执行此操作?

5 个答案:

答案 0 :(得分:5)

对于可能的选项(值)不太多(首选<= 10)并且以后您将不会添加新选项的情况,通常首选

Enum数据类型经常)。因此,Enum的一个好用例是性别:(m, f, n)。对于您而言,最好是拥有所有可能类别的主表,而不是对它们使用Enum,这通常会更好。然后,可以更容易地从主表中进行LEFT JOIN

但是,按照您的要求:

  

一个解决方案使用枚举类型来生成表,并且包括0   条目

适用于所有MySQL / MariaDB版本

我们需要从INFORMATION_SCHEMA.COLUMNS获取所有可能的Enum值的列表:

SELECT
   SUBSTRING(COLUMN_TYPE, 6, CHAR_LENGTH(COLUMN_TYPE) - 6) AS enum_values
FROM
    information_schema.COLUMNS
WHERE
    TABLE_NAME = 'items'        -- your table name
AND
    COLUMN_NAME = 'category'    -- name of the column
AND 
    TABLE_SCHEMA = 'your_db'    -- name of the database (schema)

但是,此查询将为您提供以逗号分隔的字符串中的所有枚举值,如下所示:

'one','two','three','four'

现在,我们需要将此字符串转换为多个行。为此,我们可以使用“序列(数字系列)”表。您可以在数据库中定义一个永久表,该表存储从1到100的整数(您可能在许多其他情况下也可以找到此表)(或者,另一种方法是使用{{3} }-选中此按钮可获得一个提示:Derived Table)。

CREATE TABLE seq (n tinyint(3) UNSIGNED NOT NULL, PRIMARY KEY(n));
INSERT INTO seq (n) VALUES (1), (2), ...... , (99), (100);

现在,我们将基于逗号的位置在“枚举值字符串”和seq表之间进行JOIN操作,以将枚举值提取到不同的行中。请注意,我们将使用,来代替枚举值,而不仅仅是使用','(逗号)(以避免在值字符串中出现逗号的情况)。利用https://stackoverflow.com/a/58052199/2469308Substring_Index()Trim()等函数的字符串操作可用于提取枚举值。您可以查看此Char_Length()以获得有关此技术的一般想法:

架构(answer

CREATE TABLE items 
(id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
 category ENUM('one','two','three','four'), 
 item_id INT UNSIGNED) ENGINE=InnoDB;

INSERT INTO items (category, item_id) 
VALUES ('one', 1), 
       ('two', 2), 
       ('one', 2), 
       ('one', 3);

CREATE TABLE seq (n tinyint(3) UNSIGNED NOT NULL, 
                  PRIMARY KEY(n));
INSERT INTO seq (n) VALUES (1),(2),(3),(4),(5);

查询#1

SELECT Trim(BOTH '\'' FROM Substring_index(Substring_index(e.enum_values,
                                           '\',\'',
                                                  seq.n),
                                  '\',\'', -1)) AS cat
FROM   (SELECT Substring(column_type, 6, Char_length(column_type) - 6) AS
               enum_values
        FROM   information_schema.columns
        WHERE  table_name = 'items'
               AND column_name = 'category'
               AND table_schema = 'test') AS e
       JOIN seq
         ON ( Char_length(e.enum_values) - Char_length(REPLACE(e.enum_values,
                                                       '\',\'',
                                                       ''))
            ) / 3 >= seq.n - 1

| cat   |
| ----- |
| one   |
| two   |
| three |
| four  |

现在,最困难的部分已经完成。我们要做的就是从此子查询(具有所有类别枚举值)到您的LEFT JOIN表中,items,以获取每个类别的计数。

最后的查询如下(View on DB Fiddle):

SELECT all_cat.cat                   AS category,
       Count(i.item_id) AS total
FROM   (SELECT Trim(BOTH '\'' FROM Substring_index(
                                   Substring_index(e.enum_values,
                                          '\',\'',
                                                  seq.n),
                                                  '\',\'', -1)) AS cat
        FROM   (SELECT Substring(column_type, 6, Char_length(column_type) - 6)
                       AS
                       enum_values
                FROM   information_schema.columns
                WHERE  table_name = 'items'
                       AND column_name = 'category'
                       AND table_schema = 'test') AS e
               JOIN seq
                 ON ( Char_length(e.enum_values) - Char_length(
                                                   REPLACE(e.enum_values,
                                                   '\',\'',
                                                   ''))
                    ) / 3 >= seq.n - 1) AS all_cat
       LEFT JOIN items AS i
              ON i.category = all_cat.cat
GROUP  BY all_cat.cat
ORDER  BY total DESC;

结果

| category | total |
| -------- | ----- |
| one      | 3     |
| two      | 1     |
| three    | 0     |
| four     | 0     |

答案 1 :(得分:2)

这对于MySQL 8.0和JSON_TABLE()来说很有趣:

select c.category, count(i.category) as total
from information_schema.COLUMNS s
join json_table(
  replace(replace(replace(trim('enum' from s.COLUMN_TYPE),'(','['),')',']'),'''','"'),
  '$[*]' columns (category varchar(50) path '$')
) c
left join items i on i.category = c.category
where s.TABLE_SCHEMA = 'test' -- replace with your db/schema name
  and s.TABLE_NAME   = 'items'
  and s.COLUMN_NAME  = 'category'
group by c.category

它将ENUM类型定义从information_schema转换为JSON数组,然后由JSON_TABLE()转换为表,然后可将其用于LEFT JOIN。

db-fiddle上观看演示

注意:类别中不得包含()[]'"中的任何字符。

但是要认真–仅创建categories表。这样做的理由更多。例如,您可能想要呈现一个包含所有可能类别的下拉菜单。

select category from categories

答案 2 :(得分:1)

我想说,将枚举编码到脚本中基本上是一种不好的做法。因此,使用存在的枚举(及其相对键)创建一个表,这是对左连接查询进行分组的简单情况...

SELECT 
    cat.enum_name, 
    COUNT(data.id) AS total 
FROM
    category_table cat
    LEFT JOIN
    data_table data
        ON  cat.cate_id = data.cat_id 
 GROUP BY 
    cat.enum_name 

答案 3 :(得分:0)

您可以制作不同类别的虚构数据集,并与原始表格进行左连接,如下所示。

 SELECT A.category, count(*) total FROM
 (SELECT 'one' as Category 
 UNION ALL
 SELECT 'two' as Category) A
 LEFT JOIN items B
 ON A.Category=B.Category
 GROUP BY B.Category;

如果您希望动态获取所有类别的列表,请将它们保存在另一个表格中(例如All_category_table),然后执行如下所示的连接:

 SELECT A.category, count(*) total FROM
 (SELECT Category FROM All_category_table) A
 LEFT JOIN items B
 ON A.Category=B.Category
 GROUP BY B.Category;

答案 4 :(得分:0)

使用选择中子查询

select  cat.categoryname
   (
      select count(*)  -- count total
      from items as i
      where i.category = cat.category  -- connect

   ) as totalcount
from cat
order by cat.categoryname