2016-10-06 83 views
8

MySQL 5.7では新しいJSON関数が大好きですが、JSONの値を通常のテーブル構造にマージしようとしているブロックで動作しています。MySQLのJSON配列を行に変換する

JSONを取得し、それから配列を操作して抽出するなどは簡単です。 JSON_EXTRACTのすべての方法。しかし、JSON配列から行に行く逆はどうでしょうか?おそらく、私は既存のMySQL JSONの機能性が密集しているかもしれませんが、私はそれを理解することができませんでした。

たとえば、JSON配列があり、値の配列の各要素に行を挿入したいとしますか?私が見つけた唯一の方法は、JSON_EXTRACT(... '$ [0]')JSON_EXTRACT(... '$ [1]')などをまとめてそれらを結合することです。

JSON配列があり、GROUP_CONCAT()をカンマ区切りの単一の文字列にしたいとしますか?言い換えれば

、私はこれを行うことができます知っている:

SET @j = '[1, 2, 3]'; 
SELECT GROUP_CONCAT(JSON_EXTRACT(@j, CONCAT('$[', x.n, ']'))) AS val 
    FROM 
    ( 
    SELECT 0 AS n  
    UNION  
    SELECT 1 AS n  
    UNION  
    SELECT 2 AS n  
    UNION  
    SELECT 3 AS n  
    UNION  
    SELECT 4 AS n  
    UNION  
    SELECT 5 AS n  
) x 
WHERE x.n < JSON_LENGTH(@j); 

しかし、それは私の目が痛いです。そして私の心。

にはどうすればいいような何かすることができます

SET @j = '[1, 2, 3]'; 
SELECT GROUP_CONCAT(JSON_EXTRACT(@j, '$[ * ]')) 

を...そしてそれがJSON配列自体対の配列で一緒に値を連結ありますか?

私は私がここに探していることの線に沿ってJSON_SPLITのいくつかの並べ替えです推測:

SET @j = '[1, 2, 3]'; 

SELECT GROUP_CONCAT(val) 
FROM 
    JSON_SPLIT(JSON_EXTRACT(@j, '$[ * ]'), '$') 

MySQLが適切STRING_SPLIT(ヴァル、「区切り」)テーブルを返す関数を持っていた場合、私はハック可能性それは(呪われて逃げる)、しかしそれはどちらも利用できません。

+0

私は 'あなたが行うことができないのと同じ理由のために、あなたがそれを行うことができるとは思いませんSPLIT_STRING() ':クエリは、同じ行から結合なしで入力テーブルを複数作成することはできません。 – Barmar

+0

ええ、あなたは正しいかもしれません。私はテーブルの値を持つ関数がサポートされていると仮定していました。明らかに、MySQLはそれを奇妙な人にしています。たとえば、SQL ServerにはSTRING_SPLIT:https://msdn.microsoft.com/en-us/library/mt684588.aspxという名前があります。 Postgressはregexp_split_to_tableの正規表現で分割さえしています。ああ、MySQL ... –

+0

右。 MySQLにはテーブル以外の配列のようなデータ構造はありません。 JSON関数は、スキーマを非正規化するためのブランケット・ライセンスと見なされるべきではありません。 – Barmar

答えて

6

JSONに非正規化するのは良い考えではありませんが、JSONデータを処理する必要があることもあります。また、JSON配列をクエリの行に抽出する方法もあります。

トリックは、インデックスのテンポラリ・テーブルまたはインライン・テーブルに対してジョインを実行することです。これにより、JSON配列内のヌル以外の各値の行が与えられます。つまり、値0、1、2のテーブルを2つのエントリを持つJSON配列「fish」に結合すると、fish [0]は0になり、1行、fish [1]は1にマッチし、 2行目になりますが、fish [2]はnullであるため、2と一致せず、結合で行が生成されません。インデックステーブルには、JSONデータの配列の最大長と同じ数の番号が必要です。これはちょっとしたハックだし、OPの例ほど苦痛だが、とても便利だ。

例(MySQLの5.7.8以降が必要):

CREATE TABLE t1 (rec_num INT, jdoc JSON); 
INSERT INTO t1 VALUES 
    (1, '{"fish": ["red", "blue"]}'), 
    (2, '{"fish": ["one", "two", "three"]}'); 

SELECT 
    rec_num, 
    idx, 
    JSON_EXTRACT(jdoc, CONCAT('$.fish[', idx, ']')) AS fishes 
FROM t1 
    -- Inline table of sequential values to index into JSON array 
JOIN ( 
    SELECT 0 AS idx UNION 
    SELECT 1 AS idx UNION 
    SELECT 2 AS idx UNION 
    -- ... continue as needed to max length of JSON array 
    SELECT 3 
) AS indexes 
WHERE JSON_EXTRACT(jdoc, CONCAT('$.fish[', idx, ']')) IS NOT NULL 
ORDER BY rec_num, idx; 

結果は次のとおりです。

+---------+-----+---------+ 
| rec_num | idx | fishes | 
+---------+-----+---------+ 
|  1 | 0 | "red" | 
|  1 | 1 | "blue" | 
|  2 | 0 | "one" | 
|  2 | 1 | "two" | 
|  2 | 2 | "three" | 
+---------+-----+---------+ 

MySQLのチームはすべて作るためにMySQLの8でJSON_TABLE機能を追加することのように見えますこれは簡単です。 (http://mysqlserverteam.com/mysql-8-0-labs-json-aggregation-functions/

+0

ええ、それは基本的に私の質問の最初の例として持っていたものと同じです。動作しますが、醜いので、UNIONを複数回複製する必要があります。ここでの基本的な問題は、MySQLはテーブル定義関数をサポートせず、ユーザ定義ではなく組み込み関数をサポートしていないことです。欲しいのは、JSON_TABLEとSTRING_SPLITをMySQL 8に追加し、追加のユーザ定義のテーブル値関数がギャップを埋めることができるようにすることです。 –

1

私の場合、JSON機能が利用できなかったので、ハックを使用しました。 Chris MYSQLが述べたようにSTRING_SPLITはありませんが、substring_indexがあります。

入力を使用でき

{ 
    "requestId":"BARBH17319901529", 
    "van":"0xxxxx91317508", 
    "source":"AxxxS", 
    "txnTime":"15-11-2017 14:08:22" 
} 

について:

trim(
    replace(
     substring_index(
      substring(input, 
       locate('requestid',input) 
        + length('requestid') 
        + 2), ',', 1), '"', '') 
) as Requestid` 

出力は次のようになります。

BARBH17319901529 

あなたはあなたの条件に応じて変更することができます。

0

大きな列のjson配列リストがあるレポートで作業していました。 1つの列にすべてを格納するのではなく、関係1を*に格納するようにデータモデルを変更しました。このプロセスを実行するために、私は最大サイズがわからないので、ストアドプロシージャでしばらく使用していた:

DROP PROCEDURE IF EXISTS `test`; 

DELIMITER # 

CREATE PROCEDURE `test`() 
PROC_MAIN:BEGIN 
DECLARE numNotes int; 
DECLARE c int; 
DECLARE pos varchar(10); 

SET c = 0; 
SET numNotes = (SELECT 
ROUND ( 
     (
      LENGTH(debtor_master_notes) 
      - LENGTH(REPLACE (debtor_master_notes, "Id", "")) 
     )/LENGTH("Id")   
    ) AS countt FROM debtor_master 
order by countt desc Limit 1); 

DROP TEMPORARY TABLE IF EXISTS debtorTable; 
CREATE TEMPORARY TABLE debtorTable(debtor_master_id int(11), json longtext, note int); 
WHILE(c <numNotes) DO 
SET pos = CONCAT('$[', c, ']'); 
INSERT INTO debtorTable(debtor_master_id, json, note) 
SELECT debtor_master_id, JSON_EXTRACT(debtor_master_notes, pos), c+1 
FROM debtor_master 
WHERE debtor_master_notes IS NOT NULL AND debtor_master_notes like '%[%' AND JSON_EXTRACT(debtor_master_notes, pos) IS NOT NULL AND JSON_EXTRACT(debtor_master_notes, pos) IS NOT NULL; 
SET c = c + 1; 
END WHILE; 
SELECT * FROM debtorTable; 
END proc_main # 

DELIMITER ; 
関連する問題