UPDATE:これは、JSON_TABLE関数を介してMySQL 8で可能になりました: https://dev.mysql.com/doc/refman/ 8.0/en/json-table-functions.html
私はMySQL 5.7の新しいJSON関数が大好きですが、JSONの値を通常のテーブル構造にマージしようとするブロックにぶつかります。
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(val、 'separator')テーブルを返す関数があれば、それをハックすることもできます(エスケープされることはありません)が、それも利用できません。
MySQL 8+で JSON_TABLE を使用してこれを行う方法は次のとおりです。
SELECT *
FROM
JSON_TABLE(
'[5, 6, 7]',
"$[*]"
COLUMNS(
Value INT PATH "$"
)
) data;
また、区切り文字列を取得してJSON文字列に変換することで、MySQLにはない一般的な文字列分割関数としても使用できます(PGのregexp_split_to_tableまたはMSSQLのSTRING_SPLITと同様)。
set @delimited = 'a,b,c';
SELECT *
FROM
JSON_TABLE(
CONCAT('["', REPLACE(@delimited, ',', '", "'), '"]'),
"$[*]"
COLUMNS(
Value varchar(50) PATH "$"
)
) data;
JSONに非正規化するのは得策ではありませんが、JSONデータを処理する必要がある場合があり、クエリの行にJSON配列を抽出する方法があります。
トリックは、インデックスの一時テーブルまたはインラインテーブルで結合を実行することです。これにより、JSON配列のnull以外の各値に対して行が提供されます。つまり、2つのエントリを持つJSON配列「fish」に結合する値0、1、および2のテーブルがある場合、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/ )
2018年。このケースで私がしたこと。
行の数が連続的に続くテーブルを準備します。
CREATE TABLE `t_list_row` (
`_row` int(10) unsigned NOT NULL,
PRIMARY KEY (`_row`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT t_list_row VALUES (0), (1), (2) .... (65535) big enough;
将来、行への簡単なJSON配列をお楽しみください。
SET @j = '[1, 2, 3]';
SELECT
JSON_EXTRACT(@j, CONCAT('$[', B._row, ']'))
FROM (SELECT @j AS B) AS A
INNER JOIN t_list_row AS B ON B._row < JSON_LENGTH(@j);
このように。 「クリス・ハインズ」のようなものです。ただし、配列のサイズを知る必要はありません。
良い:明確で、短く、簡単なコード、配列サイズを知る必要がない、ループがない、他の関数を呼び出す必要がない、高速。
悪い:十分な行があるテーブルがもう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
要件に応じて変更できます。
1つの列に大きな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 ;