我可以使用纯mysql解决这个问题吗?(加入';'列中的分隔值)

jwe*_*dev 4 mysql join

总而言之:我需要将几个表格中的数据放在一起,为了不必画一张大桌子,我简化了它们.

我需要在一个查询中这样做,我不能使用PHP或任何其他语言来处理结果.(如果我可以简单地解决这个问题,我会使用PHP)

如果我有一个将t1行连接到t2的链接表,但不幸的是我没有,也不能引入一个,这不会是一个问题.

User table: (alias t1)
user(varchar 150),resources(varchar 250)
+-------+-------+
| user1 | 1;2;4 |
+-------+-------+
| user2 | 2     |
+-------+-------+
| user3 | 3;4   |
+-------+-------+

Resources table: (alias t2)
id(int 11 AI), data(text)
+---+-------+
| 1 | data1 |
+---+-------+
| 2 | data2 |
+---+-------+
| 3 | data3 |
+---+-------+
| 4 | data4 |
+---+-------+
| 5 | data5 |
+---+-------+
Run Code Online (Sandbox Code Playgroud)

多个用户可以连接到相同的资源,用户可以访问一个或多个资源.

我希望结果接近:

user,data
+-------+-------+
| user1 | data1 |
+-------+-------+
| user1 | data2 |
+-------+-------+
| user1 | data4 |
+-------+-------+
| user2 | data2 |
+-------+-------+
Run Code Online (Sandbox Code Playgroud)

....等等

我有基本的mysql知识,但这个不在我的知识范围内.有什么方法可以加入t2吗?

我在发布这篇文章之前已经读过的帖子: 如何在join字段中使用逗号分隔列表连接两个表

mysql用逗号分隔的id连接两个表

Rya*_*ent 8

如果user_resources(t1)是一个'规范化表',每个user => resource组合有一行,那么得到答案的查询就像只有joining表一样简单.

唉,denormalizedresources列作为:''资源ID列表'用';'分隔 字符.

如果我们可以将'resources'列转换为行,那么随着表连接变得简单,很多困难都会消失.

生成输出的查询要求:

SELECT user_resource.user, 
       resource.data

FROM user_resource 
     JOIN integerseries AS isequence 
       ON isequence.id <= COUNT_IN_SET(user_resource.resources, ';') /* normalize */

     JOIN resource 
       ON resource.id = VALUE_IN_SET(user_resource.resources, ';', isequence.id)      
ORDER BY
       user_resource.user,  resource.data
Run Code Online (Sandbox Code Playgroud)

输出:

user        data    
----------  --------
sampleuser  abcde   
sampleuser  azerty  
sampleuser  qwerty  
stacky      qwerty  
testuser    abcde   
testuser    azerty  
Run Code Online (Sandbox Code Playgroud)

怎么样:

'技巧'是一个包含从1到某个限制的数字的表.我叫它integerseries.它可以用来转换'水平'的东西,如:';' delimited stringsinto rows.

这种方式的工作方式是,当你"加入"时integerseries,你正在做一个cross join,这就是"自然地"与"内部联接"发生的事情.

每行都integerseries使用表中不同的"序列号"进行复制,我们将其用作列表中"资源"的"索引",我们希望将其用于该表row.

这个想法是:

  • 计算列表中的项目数.
  • 根据列表中的位置提取每个项目.
  • 使用integerseries一个行转换成一组行提取个人"资源ID"的user.resources我们一起去.

我决定使用两个函数:

  • 给定"分隔字符串列表"和"索引"的函数将返回列表中位置的值.我称之为:VALUE_IN_SET.即,给定'A; B; C'和'索引'为2,则返回'B'.

  • 给定"分隔字符串列表"的函数将返回列表中项目数的计数.我称之为:COUNT_IN_SET.即给定'A; B; C'将返回3

它转而认为这两个功能integerseries应该提供一般解决方案delimited items list in a column.

它有用吗?

从a创建"规范化"表的查询';' delimited string in column.它显示所有列,包括由'cross_join'(isequence.idas resources_index)生成的值:

SELECT user_resource.user, 
       user_resource.resources,
       COUNT_IN_SET(user_resource.resources, ';')                AS resources_count, 
       isequence.id                                              AS resources_index,
       VALUE_IN_SET(user_resource.resources, ';', isequence.id)  AS resources_value
FROM 
     user_resource 
     JOIN  integerseries AS isequence 
       ON  isequence.id <= COUNT_IN_SET(user_resource.resources, ';')
ORDER BY
       user_resource.user, isequence.id
Run Code Online (Sandbox Code Playgroud)

'规范化'表输出:

user        resources  resources_count  resources_index  resources_value  
----------  ---------  ---------------  ---------------  -----------------
sampleuser  1;2;3                    3                1  1                
sampleuser  1;2;3                    3                2  2                
sampleuser  1;2;3                    3                3  3                
stacky      2                        1                1  2                
testuser    1;3                      2                1  1                
testuser    1;3                      2                2  3                
Run Code Online (Sandbox Code Playgroud)

使用上面的"规范化" user_resources表,它是一个简单的连接,提供所需的输出:

所需的功能(这些是可以在任何地方使用的通用功能)

注意:这些函数的名称与mysql FIND_IN_SET函数有关.即他们在字符串列表方面做了类似的事情?

COUNT_IN_SET函数:返回的数character delimited items列.

DELIMITER $$

DROP FUNCTION IF EXISTS `COUNT_IN_SET`$$

CREATE FUNCTION `COUNT_IN_SET`(haystack VARCHAR(1024), 
                               delim CHAR(1)
                               ) RETURNS INTEGER
BEGIN
      RETURN CHAR_LENGTH(haystack) - CHAR_LENGTH( REPLACE(haystack, delim, '')) + 1;
END$$

DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

VALUE_IN_SET功能:对待delimited list作为one based array并返回在给定的"索引"的值.

DELIMITER $$

DROP FUNCTION IF EXISTS `VALUE_IN_SET`$$

CREATE FUNCTION `VALUE_IN_SET`(haystack VARCHAR(1024), 
                               delim CHAR(1), 
                               which INTEGER
                               ) RETURNS VARCHAR(255) CHARSET utf8 COLLATE utf8_unicode_ci
BEGIN
      RETURN  SUBSTRING_INDEX(SUBSTRING_INDEX(haystack, delim, which),
                     delim,
                     -1);
END$$

DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

相关信息:

表(带数据):

CREATE TABLE `integerseries` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

/*Data for the table `integerseries` */

insert  into `integerseries`(`id`) values (1);
insert  into `integerseries`(`id`) values (2);
insert  into `integerseries`(`id`) values (3);
insert  into `integerseries`(`id`) values (4);
insert  into `integerseries`(`id`) values (5);
insert  into `integerseries`(`id`) values (6);
insert  into `integerseries`(`id`) values (7);
insert  into `integerseries`(`id`) values (8);
insert  into `integerseries`(`id`) values (9);
insert  into `integerseries`(`id`) values (10);
Run Code Online (Sandbox Code Playgroud)

资源:

CREATE TABLE `resource` (
  `id` int(11) NOT NULL,
  `data` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

/*Data for the table `resource` */

insert  into `resource`(`id`,`data`) values (1,'abcde');
insert  into `resource`(`id`,`data`) values (2,'qwerty');
insert  into `resource`(`id`,`data`) values (3,'azerty');
Run Code Online (Sandbox Code Playgroud)

User_resource:

CREATE TABLE `user_resource` (
  `user` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `resources` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`user`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

/*Data for the table `user_resource` */

insert  into `user_resource`(`user`,`resources`) values ('sampleuser','1;2;3');
insert  into `user_resource`(`user`,`resources`) values ('stacky','3');
insert  into `user_resource`(`user`,`resources`) values ('testuser','1;3');
Run Code Online (Sandbox Code Playgroud)