web-dev-qa-db-ja.com

MySQL:列の値を分割して複数の行を取得する

I have some data in a table like so:
product_id      categories
10              9,12
11              8
12              11,18,5

I want a select statement that would produce this output:

product_id      category_id
10              9
10              12
11              8
12              11
12              18
12              5

私はそれをググることができるようにこのシナリオをフレーズする方法がわかりません。

5
Sparctus

探しているのは、GROUP_CONCATを使用したGROUP BY集計クエリの逆です。結果を一時テーブルに格納することをいとわないなら、私はちょうどそれを得ました。

まず、prodというテーブルとprodcatという一時テーブルのサンプルデータを使用して、探している結果を保持するコードを次に示します。

use test
drop table if exists prod;
drop table if exists prodcat;
create table prod
(
  product_id int not null,
  categories varchar(255)
) engine=MyISAM;
create table prodcat
(
  product_id int not null,
  cat  int not null
) engine=MyISAM;
insert into prod values
(10,'9,12'),(11,'8'),(12,'11,18,5');
select * from prod;

ここにロードされます

mysql> use test
Database changed
mysql> drop table if exists prod;
Query OK, 0 rows affected (0.00 sec)

mysql> drop table if exists prodcat;
Query OK, 0 rows affected (0.00 sec)

mysql> create table prod
    -> (
    ->   product_id int not null,
    ->   categories varchar(255)
    -> ) engine=MyISAM;
Query OK, 0 rows affected (0.07 sec)

mysql> create table prodcat
    -> (
    ->   product_id int not null,
    ->   cat  int not null
    -> ) engine=MyISAM;
Query OK, 0 rows affected (0.06 sec)

mysql> insert into prod values
    -> (10,'9,12'),(11,'8'),(12,'11,18,5');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from prod;
+------------+------------+
| product_id | categories |
+------------+------------+
|         10 | 9,12       |
|         11 | 8          |
|         12 | 11,18,5    |
+------------+------------+
3 rows in set (0.00 sec)

mysql>

OK、各product_idと各カテゴリを組み合わせるクエリが必要です。ここにあります:

select concat('insert into prodcat select ',product_id,',cat from (select NULL cat union select ',
replace(categories,',',' union select '),') A where cat IS NOT NULL;') ProdCatQueries from prod;

ここで実行されます

mysql> select concat('insert into prodcat select ',product_id,',cat from (select NULL cat union select ',
    -> replace(categories,',',' union select '),') A where cat IS NOT NULL;') ProdCatQueries from prod;
+----------------------------------------------------------------------------------------------------------------------------------+
| ProdCatQueries                                                                                                                   |
+----------------------------------------------------------------------------------------------------------------------------------+
| insert into prodcat select 10,cat from (select NULL cat union select 9 union select 12) A where cat IS NOT NULL;                 |
| insert into prodcat select 11,cat from (select NULL cat union select 8) A where cat IS NOT NULL;                                 |
| insert into prodcat select 12,cat from (select NULL cat union select 11 union select 18 union select 5) A where cat IS NOT NULL; |
+----------------------------------------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)

mysql>

各行を手動で実行させます

mysql> insert into prodcat select 10,cat from (select NULL cat union select 9 union select 12) A where cat IS NOT NULL;
Query OK, 2 rows affected (0.07 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into prodcat select 11,cat from (select NULL cat union select 8) A where cat IS NOT NULL;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into prodcat select 12,cat from (select NULL cat union select 11 union select 18 union select 5) A where cat IS NOT NULL;
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql>

いいよ。クエリは機能します。 prodcatテーブルは適切に入力されましたか?

mysql> select * from prodcat;
+------------+-----+
| product_id | cat |
+------------+-----+
|         10 |   9 |
|         10 |  12 |
|         11 |   8 |
|         12 |  11 |
|         12 |  18 |
|         12 |   5 |
+------------+-----+
6 rows in set (0.00 sec)

mysql>

わかりました。データがあります。

正直なところ、SQL Serverでは、手作りの一時テーブルがなくても、これらすべてを1つのピボットクエリで実行できると思います。

それを別のレベルにして、すべてのクエリを1つのクエリに連結することもできましたが、SQLは非常に長くなっていたでしょう。実際のクエリが数千行ある場合、単一のMySQLは実用的ではありませんでした。

3つのINSERTクエリを手動で実行する代わりに、3つのINSERTクエリをテキストファイルにエコーして、スクリプトとして実行できます。次に、製品とカテゴリの組み合わせを個別に記述したテーブルがあります。

5
RolandoMySQLDBA

組み込みのMySQLトリックス自体はありませんが、巧妙な方法で目標を達成するカスタムプロシージャを保存できます。 productsテーブルに列product_idcategories、および新しいcategory_idがあると仮定します。

DELIMITER $$
    CREATE FUNCTION SPLIT_STRING(val TEXT, delim VARCHAR(12), pos INT) RETURNS TEXT
    BEGIN
        DECLARE output TEXT;
        SET output = REPLACE(SUBSTRING(SUBSTRING_INDEX(val, delim, pos), CHAR_LENGTH(SUBSTRING_INDEX(val, delim, pos - 1)) + 1), delim, '');
        IF output = '' THEN
            SET output = null;
        END IF;
        RETURN output;
    END $$

    CREATE PROCEDURE TRANSFER_CELL()
    BEGIN
        DECLARE i INTEGER;
        SET i = 1;
        REPEAT
            INSERT INTO products (product_id, category_id)
            SELECT product_id, SPLIT_STRING(categories, ',', i)
            FROM products
            WHERE SPLIT_STRING(categories, ',', i) IS NOT NULL;
            SET i = i + 1;
        UNTIL ROW_COUNT() = 0
        END REPEAT;
    END $$
DELIMITER ;

CALL TRANSFER_CELL() ;

その後、すべての行を削除しますWHERE categories NOT NULL

2
Matt Hagemann