Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Bisakah saya menyelesaikan ini dengan mysql murni? (bergabung pada '' nilai yang dipisahkan dalam kolom)

Jika user_resources (t1) adalah 'tabel yang dinormalisasi' dengan satu baris untuk setiap user => resource kombinasi maka kueri untuk mendapatkan jawabannya akan sesederhana hanya joining tabel bersama.

Sayangnya, ini denormalized dengan memiliki resources kolom sebagai:'daftar id sumber daya' dipisahkan oleh ';' karakter.

Jika kita dapat mengubah kolom 'sumber daya' menjadi baris, maka banyak kesulitan akan hilang saat tabel bergabung menjadi sederhana.

Kueri untuk menghasilkan keluaran yang diminta:

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

Keluaran:

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

Bagaimana:

'Triknya' adalah memiliki tabel yang berisi angka dari 1 hingga batas tertentu. Saya menyebutnya integerseries . Ini dapat digunakan untuk mengonversi hal-hal 'horizontal' seperti:';' delimited strings ke dalam rows .

Cara kerjanya adalah ketika Anda 'bergabung' dengan integerseries , Anda melakukan cross join , itulah yang terjadi 'secara alami' dengan 'penggabungan batin'.

Setiap baris diduplikasi dengan 'nomor urut' yang berbeda dari integerseries tabel yang kita gunakan sebagai 'indeks' dari 'sumber daya' dalam daftar yang ingin kita gunakan untuk rows itu .

Idenya adalah untuk:

  • hitung jumlah item dalam daftar.
  • ekstrak setiap item berdasarkan posisinya dalam daftar.
  • Gunakan integerseries untuk mengonversi satu baris menjadi satu set baris yang mengekstrak 'id sumber daya' individu dari user .resources seiring berjalannya waktu.

Saya memutuskan untuk menggunakan dua fungsi:

  • fungsi yang diberi 'daftar string yang dibatasi' dan 'indeks' akan mengembalikan nilai pada posisi dalam daftar. Saya menyebutnya:VALUE_IN_SET . yaitu diberikan 'A;B;C' dan 'indeks' dari 2 kemudian mengembalikan 'B'.

  • fungsi yang diberi 'daftar string yang dibatasi' akan mengembalikan hitungan jumlah item dalam daftar. Saya menyebutnya:COUNT_IN_SET . yaitu diberikan 'A;B;C' akan mengembalikan 3

Ternyata kedua fungsi tersebut dan integerseries harus memberikan solusi umum untuk delimited items list in a column .

Berhasil?

Kueri untuk membuat tabel 'dinormalisasi' dari ';' delimited string in column . Ini menunjukkan semua kolom, termasuk nilai yang dihasilkan karena 'cross_join' (isequence.id sebagai 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

Output tabel 'dinormalisasi':

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                

Menggunakan user_resources yang 'dinormalisasi' di atas tabel, ini adalah gabungan sederhana untuk memberikan output yang diperlukan:

Fungsi yang dibutuhkan (ini adalah fungsi umum yang dapat digunakan di mana saja )

catatan:Nama-nama fungsi ini terkait dengan mysql Fungsi FIND_IN_SET . yaitu mereka melakukan hal serupa terkait daftar string?

COUNT_IN_SET fungsi:mengembalikan jumlah character delimited items di kolom.

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 fungsi:memperlakukan delimited list sebagai one based array dan mengembalikan nilai pada 'indeks' yang diberikan.

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 ;

Informasi Terkait:

Tabel (dengan data):

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

Sumber:

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

Sumber_pengguna:

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. MySQL sekarang() +1 hari

  2. Bagaimana cara kerja KASUS MySQL?

  3. Membuat dan Menggunakan Prosedur Tersimpan MySQL - Tutorial

  4. Perbarui kolom di MySQL

  5. Cara Mengekspor Database Menggunakan Baris Perintah