我有一张物品表。其中一个字段是一个类别(由枚举表示)。有些类别没有项目。
所以我这样做了:
select category, count(*) as total from items group by category;
+------------+-------+
| category | total |
+------------+-------+
| one | 6675 |
+------------+-------+
我想生成一个这样的表(其中两个是另一个可能的枚举值):
+------------+-------+
| category | total |
+------------+-------+
| one | 6675 |
+------------+-------+
| two | 0 |
+------------+-------+
如何使用mysql SQL查询执行此操作?
答案 0 :(得分:5)
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/2469308,Substring_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