文字列としてJSONドキュメントを含む場合、MySQL(5.6)列から値を取得する方法
たとえば、テーブルがあり、id、name、およびeducationsの3つの列がある従業員があるとします。列の教育には、JSONドキュメントとしてのデータが含まれます
{"ug":"bsc","pg":"mca","ssc":"10th"}
教育コラムのugとpgの値が必要です
MySQL(5.6)クエリを使用してそれを実行できますか?
目的の操作を実行するには、MySQL 5.7.8以降が必要です。 5.7.8以降、JSON_EXTRACT
関数を使用してJSON文字列から値を抽出できます。
SELECT JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name');
+---------------------------------------------------------+
| JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name') |
+---------------------------------------------------------+
| "Aztalan" |
+---------------------------------------------------------+
here から取得。
MySQL 5.6では、JSONオブジェクトが何であるかについてMySQLは何も知らないため、必要な値を取得できません。オプションは次のとおりです。
MySQL 5.6では、デフォルトでJSON_EXTRACT
はデフォルトでは使用できません。
MySQL 5.6でjsonデータにアクセスする必要がある場合は、カスタム関数を記述する必要があります。
DELIMITER $$
DROP FUNCTION IF EXISTS `json_extract_c`$$
CREATE DEFINER=`root`@`%` FUNCTION `json_extract_c`(
details TEXT,
required_field VARCHAR (255)
) RETURNS TEXT CHARSET latin1
BEGIN
RETURN TRIM(
BOTH '"' FROM SUBSTRING_INDEX(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
details,
CONCAT(
'"',
SUBSTRING_INDEX(required_field,'$.', - 1),
'"'
),
- 1
),
'",',
1
),
':',
- 1
)
) ;
END$$
DELIMITER ;
これは役立ちます。作成してテストしました。
要素がJSONテキストで言及されていなかった場合、以前の両方の回答は機能しませんでした。私の改善された機能があります:
DELIMITER $$
DROP FUNCTION IF EXISTS `json_extract_c`$$
CREATE FUNCTION `json_extract_c`(
details TEXT,
required_field VARCHAR (255)
) RETURNS TEXT CHARSET latin1
BEGIN
DECLARE search_term TEXT;
SET details = SUBSTRING_INDEX(details, "{", -1);
SET details = SUBSTRING_INDEX(details, "}", 1);
SET search_term = CONCAT('"', SUBSTRING_INDEX(required_field,'$.', - 1), '"');
IF INSTR(details, search_term) > 0 THEN
RETURN TRIM(
BOTH '"' FROM SUBSTRING_INDEX(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
details,
search_term,
- 1
),
',"',
1
),
':',
-1
)
);
ELSE
RETURN NULL;
END IF;
END$$
DELIMITER ;
JSON抽出に使用する3つのSQLストアド関数を次に示します。ネストされたオブジェクトを処理しますが、キー名のみを考慮します。キーは文字列でなければならず、値は文字列、数値またはブール値にすることができます。配列は適切に処理されず、最初の値のみが選択されます。値が見つからない場合、NULL
を返します。
最初のjson_extract_1
は、同じ名前のキーが複数ある場合に最初の値のみを選択します。 LIMIT 1
句をそのままにしておくと、さらにキーが見つかった場合に「サブクエリは複数の行を返します」(スローモード)をスローします。
2番目のjson_extract_m
は、同じキーを持つすべての値をコンマ区切りリストで収集します。
3番目のjson_extract_c
は最も遅いものですが、コンマで値を正しく処理します。テキストの説明など、絶対に必要な場合に使用します。
3つすべてについて、制限は999キーです。 numbers
副選択用にテーブルを準備すると、速度を上げることができます。
DELIMITER $$
/*
* Single-value JSON extract - picks the first value
*/
DROP FUNCTION IF EXISTS `json_extract_1`$$
CREATE FUNCTION `json_extract_1`(json_txt TEXT, search_key VARCHAR (255))
RETURNS TEXT
BEGIN
RETURN (SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(txt,':',-1), '"', 2), '"', -1)) AS val
FROM (
SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX( SUBSTRING_INDEX(json_txt , ',', n), ',', -1 ), '}', 1), '{', -1)) AS txt
FROM (SELECT t1.v + t2.v*10 + t3.v*100 AS n
FROM (SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1,
(SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2,
(SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t3) numbers
WHERE CHAR_LENGTH(json_txt ) - CHAR_LENGTH(REPLACE(json_txt , ',', '')) >= n - 1
AND n>0 ) sp
WHERE TRIM(SUBSTRING_INDEX(txt,':',1)) = CONCAT('"',search_key,'"')
LIMIT 1 -- comment out for safe mode
);
END$$
/*
* Multi-value JSON extract - collects all values, group_concats them with comma
*/
DROP FUNCTION IF EXISTS `json_extract_m`$$
CREATE FUNCTION `json_extract_m`(json_txt TEXT, search_key VARCHAR (255))
RETURNS TEXT
BEGIN
RETURN (SELECT GROUP_CONCAT(TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(txt,':',-1), '"', 2), '"', -1))) AS val
FROM (
SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX( SUBSTRING_INDEX(json_txt , ',', n), ',', -1 ), '}', 1), '{', -1)) AS txt
FROM (SELECT t1.v + t2.v*10 + t3.v*100 AS n
FROM (SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1,
(SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2,
(SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t3) numbers
WHERE CHAR_LENGTH(json_txt ) - CHAR_LENGTH(REPLACE(json_txt , ',', '')) >= n - 1
AND n>0 ) sp
WHERE TRIM(SUBSTRING_INDEX(txt,':',1)) = CONCAT('"',search_key,'"'));
END$$
/*
* Comma-safe JSON extract - treats values with commas correctly, but slow like hell
*/
DROP FUNCTION IF EXISTS `json_extract_c`$$
CREATE FUNCTION `json_extract_c`(json_txt TEXT, search_key VARCHAR (255))
RETURNS TEXT
BEGIN
DROP TEMPORARY TABLE IF EXISTS json_parts;
DROP TEMPORARY TABLE IF EXISTS json_parts2;
DROP TEMPORARY TABLE IF EXISTS json_indexes;
CREATE TEMPORARY TABLE json_parts AS
SELECT n, IF(INSTR(txt,':')>0 AND (INSTR(txt,',')+INSTR(txt,'{')>0),1,0) AS this_val, IF(INSTR(txt,':')>0 AND (INSTR(txt,',')+INSTR(txt,'{')=0),1,0) AS next_val, IF(INSTR(txt,',')+INSTR(txt,'{')>0,1,0) AS next_key, txt
FROM (SELECT n, IF(n%2,txt,REPLACE(txt,',','|')) AS txt
FROM (SELECT n, TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(json_txt , '"', n), '"', -1 )) AS txt
FROM (SELECT t1.v + t2.v*10 + t3.v*100 AS n
FROM (SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1,
(SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2,
(SELECT 0 AS v UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t3) numbers
WHERE CHAR_LENGTH(json_txt ) - CHAR_LENGTH(REPLACE(json_txt , '"', '')) >= n - 1
AND n>0) v
) v2;
CREATE TEMPORARY TABLE json_parts2 AS SELECT * FROM json_parts;
CREATE TEMPORARY TABLE json_indexes AS
SELECT p1.n, p1.n+1 AS key_idx, MIN(GREATEST(IF(p2.this_val,p2.n,0), IF(p2.next_val,p2.n+1,0))) AS val_idx, p2.this_val AS trim_val
FROM json_parts p1
JOIN json_parts2 p2 ON (p1.n < p2.n AND (p2.this_val OR p2.next_val))
WHERE p1.next_key
GROUP BY p1.n;
RETURN (SELECT json_values.v
FROM (SELECT p1.txt AS k, REPLACE(IF(i.trim_val, regexp_replace(regexp_replace(p2.txt,'^[: {]+',''),'[, }]+$',''), p2.txt), '|', ',') AS v
FROM json_indexes i
JOIN json_parts p1 ON (i.key_idx = p1.n)
JOIN json_parts2 p2 ON (i.val_idx = p2.n)) json_values
WHERE json_values.k = search_key);
END$$
DELIMITER ;
ええ、もし機会があれば、MySQL 5.7にアップグレードしてみてください。組み込み関数はより効率的に機能します。
Rahulの答えは私にはあまりうまくいきませんでしたので、私はそれを編集し、これは私のために働きました:
DELIMITER $$
DROP FUNCTION IF EXISTS `json_extract_c`$$
CREATE FUNCTION `json_extract_c`(
details TEXT,
required_field VARCHAR (255)
) RETURNS TEXT CHARSET latin1
BEGIN
SET details = TRIM(LEADING '{' FROM TRIM(details));
SET details = TRIM(TRAILING '}' FROM TRIM(details));
RETURN TRIM(
BOTH '"' FROM SUBSTRING_INDEX(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
details,
CONCAT(
'"',
SUBSTRING_INDEX(required_field,'$.', - 1),
'":'
),
- 1
),
',"',
1
),
':',
-1
)
) ;
END$$
DELIMITER ;
以下の答えは私のために働く。値から二重引用符を削除します。
DELIMITER $$
DROP FUNCTION IF EXISTS `json_extract_values`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `json_extract_values`(
details TEXT,
required_field VARCHAR (255)
) RETURNS TEXT CHARSET latin1
BEGIN
SET details = SUBSTRING_INDEX(details, "{", -1);
SET details = SUBSTRING_INDEX(details, "}", 1);
RETURN
SUBSTRING_INDEX(
TRIM(
TRAILING '"' FROM
SUBSTRING_INDEX(
details,
CONCAT(
'"',
SUBSTRING_INDEX(required_field,'$.', - 1),
'":'
),
-1 )
),
'"',
-1);
END$$
DELIMITER ;
テーブルフィールドにJSONをネストしている場合、上記の機能は正常に動作しません。
Mysql 5.6でJSON_EXTRACTが必要だったので、mysql 5.7のネイティブ関数のような値を抽出できる元の関数のコピーを自分で作成しました
使用法:
SELECT JSON_EXTRACT_NESTED(table_field,"json_level1.json_level2.json_level3") FROM table;
1レベルのJSONがある場合は、次を使用します。
SELECT JSON_EXTRACT_NESTED(table_field,"json_level1") FROM table;
データベースには、次の2つの関数を追加する必要があります。
主な機能:
CREATE FUNCTION `json_extract_nested`(
_field TEXT,
_variable TEXT
) RETURNS TEXT CHARSET latin1
BEGIN
DECLARE X INT DEFAULT 0;
DECLARE fieldval1 TEXT;
DECLARE arrayName,arrayValue TEXT;
SET arrayName = SUBSTRING_INDEX(_variable, '.', 1);
IF(LOCATE('%',arrayName)> 0) THEN
SET _field = SUBSTRING_INDEX(_field, "{", -1);
SET _field = SUBSTRING_INDEX(_field, "}", 1);
RETURN TRIM(
BOTH '"' FROM SUBSTRING_INDEX(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
_field,
CONCAT(
'"',
SUBSTRING_INDEX(_variable,'$.', - 1),
'":'
),
- 1
),
',"',
1
),
':',
-1
)
) ;
ELSE
SET arrayValue = json_array_value(_field, arrayName);
WHILE X < (LENGTH(_variable) - LENGTH(REPLACE(_variable, '.', ""))) DO
IF(LENGTH(_variable) - LENGTH(REPLACE(_variable, '.', ""))>X) THEN
SET arrayName = SUBSTRING_INDEX(SUBSTRING_INDEX(_variable, '.', X+2),'.',-1);
END IF;
IF(arrayName<>'') THEN
SET arrayValue = json_array_value(arrayValue, arrayName);
END IF;
SET X = X + 1;
END WHILE;
END IF;
RETURN arrayValue;
END$$
DELIMITER ;
補助機能(メイン機能に必要):
CREATE FUNCTION `json_array_value`(
_field TEXT,
arrayName VARCHAR (255)
) RETURNS TEXT CHARSET latin1
BEGIN
DECLARE arrayValue, arrayValueTillDelimit TEXT;
DECLARE arrayStartDelimiter, arrayEndDelimiter VARCHAR(10);
DECLARE arrayCountDelimiter INT;
DECLARE countBracketLeft, countBracketRight INT DEFAULT 0;
DECLARE X INT DEFAULT 0;
DECLARE arrayNameQuoted VARCHAR(255);
SET arrayNameQuoted = CONCAT('"',arrayName,'"');
/*check arrayname exist*/
IF(LOCATE(arrayNameQuoted,_field)= 0) THEN
RETURN NULL;
ELSE
/*get value behind arrayName1*/
SET _field = SUBSTRING(_field,1,LENGTH(_field)-1);
SET arrayValue = SUBSTRING(_field, LOCATE(arrayNameQuoted,_field)+LENGTH(arrayNameQuoted)+1, LENGTH(_field));
/*get json delimiter*/
SET arrayStartDelimiter = LEFT(arrayValue, 1);
IF(arrayStartDelimiter='{') THEN
SET arrayEndDelimiter = '}';
loopBrackets: WHILE X < (LENGTH(arrayValue)) DO
SET countBracketLeft = countBracketLeft +IF(SUBSTRING(arrayValue,X,1)=arrayStartDelimiter,1,0);
SET countBracketRight = countBracketRight +IF(SUBSTRING(arrayValue,X,1)=arrayEndDelimiter,1,0);
IF(countBracketLeft<>0 AND countBracketLeft=countBracketRight) THEN
SET arrayCountDelimiter = X;
LEAVE loopBrackets;
ELSE
SET X = X + 1;
END IF;
END WHILE;
ELSEIF(arrayStartDelimiter='[') THEN
SET arrayEndDelimiter = ']';
SET arrayCountDelimiter = LENGTH(SUBSTRING_INDEX(arrayValue, arrayEndDelimiter, 0));
ELSEIF(arrayStartDelimiter='"') THEN
SET arrayEndDelimiter = '"';
SET arrayCountDelimiter = LENGTH(SUBSTRING_INDEX(arrayValue, arrayEndDelimiter, 0));
ELSE
SET arrayStartDelimiter = "";
IF((LOCATE(",",arrayValue)> LOCATE("}",arrayValue))) THEN
SET arrayEndDelimiter = ",";
ELSE
SET arrayEndDelimiter = "}";
END IF;
SET arrayCountDelimiter = LENGTH(SUBSTRING_INDEX(arrayValue, arrayEndDelimiter, 0));
END IF;
SET arrayValueTillDelimit = SUBSTRING(arrayValue, 1, arrayCountDelimiter);
SET arrayCountDelimiter = LENGTH(arrayValueTillDelimit) - LENGTH(REPLACE(arrayValueTillDelimit, arrayStartDelimiter, ""));
SET arrayValue = SUBSTR(arrayValue,LENGTH(arrayStartDelimiter)+1);
IF(arrayStartDelimiter='{') THEN
SET arrayValue = SUBSTRING_INDEX(arrayValue, arrayEndDelimiter, arrayCountDelimiter);
ELSE
SET arrayValue = SUBSTRING_INDEX(arrayValue, arrayEndDelimiter, arrayCountDelimiter+1);
END IF;
RETURN (arrayValue);
END IF;
END$$
DELIMITER ;
SQL関数内の文字列操作を直観的に見ていないかもしれない人(私のような人)に、ここで与えられた回答の代替ビューを提供します。このバージョンでは、テキスト解析の各ステップを明示的に確認できます。これはMySQL 5.6で機能し、もちろん組み合わせて元に戻すことができ、変数を使用しません。
DELIMITER $$
DROP FUNCTION IF EXISTS `json_extract_c`$$
CREATE FUNCTION `json_extract_c`(
details TEXT,
required_field VARCHAR (255)
) RETURNS TEXT CHARSET latin1
BEGIN
/* get key from function passed required field value */
set @JSON_key = SUBSTRING_INDEX(required_field,'$.', -1);
/* get everything to the right of the 'key = <required_field>' */
set @JSON_entry = SUBSTRING_INDEX(details,CONCAT('"', @JSON_key, '"'), -1 );
/* get everything to the left of the trailing comma */
set @JSON_entry_no_trailing_comma = SUBSTRING_INDEX(@JSON_entry, ",", 1);
/* get everything to the right of the leading colon after trimming trailing and leading whitespace */
set @JSON_entry_no_leading_colon = TRIM(LEADING ':' FROM TRIM(@JSON_entry_no_trailing_comma));
/* trim off the leading and trailing double quotes after trimming trailing and leading whitespace*/
set @JSON_extracted_entry = TRIM(BOTH '"' FROM TRIM(@JSON_entry_no_leading_colon));
RETURN @JSON_extracted_entry;
END$$
DELIMITER ;