Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Tôi có thể giải quyết vấn đề này bằng mysql thuần túy không? (tham gia vào '' các giá trị được phân tách trong một cột)

Nếu user_resources (t1) là một 'bảng chuẩn hóa' với một hàng cho mỗi user => resource kết hợp thì truy vấn để nhận câu trả lời sẽ đơn giản như chỉ cần joining các bảng với nhau.

Than ôi, nó denormalized bằng cách có resources cột dưới dạng:'danh sách id tài nguyên' được phân tách bằng dấu ';' nhân vật.

Nếu chúng ta có thể chuyển đổi cột 'tài nguyên' thành các hàng thì rất nhiều khó khăn sẽ biến mất vì việc nối bảng trở nên đơn giản.

Truy vấn để tạo đầu ra được yêu cầu cho:

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

Đầu ra:

user        data    
----------  --------
sampleuser  abcde   
sampleuser  azerty  
sampleuser  qwerty  
stacky      qwerty  
testuser    abcde   
testuser    azerty  

Cách thức:

'Bí quyết' là có một bảng chứa các số từ 1 đến một số giới hạn. Tôi gọi nó là integerseries . Nó có thể được sử dụng để chuyển đổi những thứ 'ngang' như:';' delimited strings thành rows .

Cách này hoạt động là khi bạn 'nối' với integerseries , bạn đang thực hiện cross join , đó là điều xảy ra 'tự nhiên' với 'liên kết bên trong'.

Mỗi hàng được sao chép với một 'số thứ tự' khác từ integerseries bảng mà chúng tôi sử dụng làm 'chỉ mục' của 'tài nguyên' trong danh sách mà chúng tôi muốn sử dụng cho rows đó .

Ý tưởng là:

  • đếm số lượng các mục trong danh sách.
  • trích xuất từng mục dựa trên vị trí của nó trong danh sách.
  • Sử dụng integerseries để chuyển đổi một hàng thành một tập hợp các hàng trích xuất 'id tài nguyên' riêng lẻ từ user . resources khi chúng ta đồng hành.

Tôi quyết định sử dụng hai chức năng:

  • hàm đưa ra 'danh sách chuỗi được phân tách' và 'chỉ mục' sẽ trả về giá trị tại vị trí trong danh sách. Tôi gọi nó là:VALUE_IN_SET . tức là đã cho 'A; B; C' và 'chỉ số' là 2 thì nó trả về 'B'.

  • hàm đã cho một 'danh sách chuỗi được phân tách' sẽ trả về số lượng các mục trong danh sách. Tôi gọi nó là:COUNT_IN_SET . tức là đã cho 'A; B; C' sẽ trả về 3

Hóa ra hai hàm đó và integerseries nên cung cấp một giải pháp chung cho delimited items list in a column .

Nó có hoạt động không?

Truy vấn để tạo bảng 'chuẩn hóa' từ ';' delimited string in column . Nó hiển thị tất cả các cột, bao gồm cả các giá trị được tạo do 'cross_join' (isequence.id dưới dạng 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

Đầu ra của bảng 'chuẩn hóa':

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                

Sử dụng user_resources 'chuẩn hóa' ở trên bảng, nó là một phép nối đơn giản để cung cấp đầu ra được yêu cầu:

Các chức năng cần thiết ( đây là những chức năng chung có thể được sử dụng ở mọi nơi )

lưu ý:Tên của những hàm này có liên quan đến mysql hàm FIND_IN_SET . tức là họ làm những việc tương tự liên quan đến danh sách chuỗi?

COUNT_IN_SET function:trả về số lượng các mục được phân tách bằng ký tự character delimited items trong cột.

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 ;

VALUE_IN_SET function:xử lý delimited list dưới dạng one based array và trả về giá trị tại 'chỉ mục' đã cho.

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 ;

Thông tin liên quan:

Các bảng (có dữ liệu):

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);

Nguồn:

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');

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');


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tìm khoảng cách giữa hai điểm bằng cách sử dụng vĩ độ và kinh độ trong mysql

  2. Docker:Kết hợp nhiều hình ảnh

  3. MySQL Giống như nhiều giá trị

  4. Sao chép MySQL và chuyển đổi dự phòng dựa trên GTID - Đi sâu vào các giao dịch có lỗi

  5. Cách thoát Trích dẫn Đơn, Ký tự Đặc biệt trong MySQL