MySQL 查询以将位域扩展为单独的命名列?

MySQL query to expand a bitfield into individual named columns?

我有以下问题 我有两个 table 第一个 table 包含一个字符串,如 01001101110 每个数字代表 table 两个中提到的技能。

因此第一个数字显示为零,这意味着该人不具备 id 1 的技能。第二个数字表明该人确实具有 id 2 的技能

table 1:

|-----------|-----------------------------|
|  name  |             skillset            |
|-----------|-----------------------------|
|   John  | 01001101110             |
|-----------|-----------------------------|

table 2:

|-----------|-----------------------------|
|    id       |              skill              |
|-----------|-----------------------------|
|       1    | polite                         |
|-----------|-----------------------------|
|      2     | easy going                 |
|-----------|-----------------------------|

现在我需要创建带有结果的查询

|-----------|-------------------|------------------|
|  name  |      polite        |   easy going  |
|-----------|-------------------|------------------|
|  John   |          0            |            1        |
|-----------|-------------------|------------------|

我看过很多这样的故事,"a former coder implemented this and now we're stuck with it." 我想只有一个独立的编码员,从一家公司跳槽到另一家公司,以尽可能最聪明和最不易维护的方式实施事情,然后迅速继续前进到他的下一个受害公司。

我还觉得讽刺的是,有人会在 varchar 中实现位域,因为他们最终使用一个完整的字节(8 位)来存储每个 1 或 0。:facepalm:

无论如何,要解决您的任务,您必须使用动态 SQL 查询。

事实上,any pivot table 查询需要动态 SQL,因为在检查 table 列出了您的技能,但是您不能 运行 在不知道列数的情况下进行查询。所以你需要运行至少两次查询。

测试数据如下:

create table table1 (name varchar(20), skillset varchar(200));

insert into table1 values ('John', '01001101110110');

create table table2 (id int, skill varchar(20));

insert into table2 values
    (1, 'polite'),
    (2, 'easy going'),
    (3, 'trustworthy'),
    (4, 'loyal'),
    (5, 'helpful'),
    (6, 'friendly'),
    (7, 'courteous'),
    (8, 'kind'),
    (9, 'obedient'),
    (10, 'cheerful'),
    (11, 'thrifty'),
    (12, 'brave'),
    (13, 'clean'),
    (14, 'reverent');

Now 是一个聪明的查询,通过在 select-list 中为您的技能 table 中的每个条目附加一个字段,为动态查询生成 SQL。关键是MySQL的GROUP_CONCAT()函数。

select concat(
    'select name,',
    group_concat(concat(' mid(skillset,',id,',1) as `',skill,'`')),
    ' from table1;') as _sql
from table2;

上述查询的输出如下:

select name, 
 mid(skillset,1,1) as `polite`, 
 mid(skillset,2,1) as `easy going`, 
 mid(skillset,3,1) as `trustworthy`, 
 mid(skillset,4,1) as `loyal`, 
 mid(skillset,5,1) as `helpful`, 
 mid(skillset,6,1) as `friendly`, 
 mid(skillset,7,1) as `courteous`, 
 mid(skillset,8,1) as `kind`, 
 mid(skillset,9,1) as `obedient`, 
 mid(skillset,10,1) as `cheerful`, 
 mid(skillset,11,1) as `thrifty`, 
 mid(skillset,12,1) as `brave`, 
 mid(skillset,13,1) as `clean`, 
 mid(skillset,14,1) as `reverent` 
from table1;

我确保用 back-ticks 分隔列别名,以防其中一个技能名称包含特殊字符或空格或与 SQL 保留字冲突。

那么这可以运行作为第二个查询,结果如下:

+------+--------+------------+-------------+-------+---------+----------+-----------+------+----------+----------+---------+-------+-------+----------+
| name | polite | easy going | trustworthy | loyal | helpful | friendly | courteous | kind | obedient | cheerful | thrifty | brave | clean | reverent |
+------+--------+------------+-------------+-------+---------+----------+-----------+------+----------+----------+---------+-------+-------+----------+
| John | 0      | 1          | 0           | 0     | 1       | 1        | 0         | 1    | 1        | 1        | 0       | 1     | 1     | 0        |
+------+--------+------------+-------------+-------+---------+----------+-----------+------+----------+----------+---------+-------+-------+----------+

使其成为SET数据类型;这为您提供了两全其美的方式——以位形式存储最多 64 个 pre-defined 选项的任意组合,但 read/write 作为字符串。

简单的 SELECT skillset FROM tbl WHERE name = 'John' 会作为字符串

返回
"easy going,helpful,friendly,kind"  (etc)

Reference