2016-10-06 6 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 어떤 종류의 추측 : MySQL은 적절한 STRING_SPLIT (발, '분리') 테이블 반환 함수가 있다면

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

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

것은, 내가 해킹 수 그것 (저주받을 수 있음)을 피할 수는 있지만, 그것은 사용할 수 없습니다.

+0

나는 당신이 할 수 있다고 생각하지 않습니다. SPLIT_STRING()': 질의는 조인없이 동일한 행의 입력 테이블에서 여러 행을 작성할 수 없습니다. – Barmar

+0

네, 맞을지도 모릅니다. 나는 테이블 값을 가진 함수가 지원되었다고 생각 했었습니다. 왜냐하면 태양 아래서 다른 DBMS가 있기 때문입니다. 분명히 MySQL은 이상한 사람입니다. 예를 들어, SQL Server는 STRING_SPLIT : https://msdn.microsoft.com/en-us/library/mt684588.aspx를 완벽하게 준수합니다. 심지어 postgegress는 regexp_split_to_table에서 정규식으로 분할됩니다. 아, MySQL ... –

+0

오른쪽. MySQL은 테이블이 아닌 배열과 같은 데이터 구조를 가지고 있지 않다. JSON 함수는 스키마를 비정규 화하기위한 포괄적 인 라이센스로 간주되어서는 안됩니다. – Barmar

답변

6

JSON으로 비정규 화하는 것은 좋지 않지만 때로는 JSON 데이터를 처리해야 할 필요가 있으며 쿼리의 행에 JSON 배열을 추출하는 방법이 있습니다.

트릭은 인덱스의 임시 또는 인라인 테이블에서 조인을 수행하는 것으로, JSON 배열의 널이 아닌 각 값에 대한 행을 제공합니다. 즉, 값이 0, 1 및 2 인 테이블을 두 항목이 포함 된 JSON 배열 "fish"에 조인하면 fish [0]은 0과 일치하고 한 행이 생기고 [1]은 1과 일치하고, 두 번째 행이 생기지 만 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; 

결과는 다음과 같습니다 그것은 MySQL의 팀처럼 보인다

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

모든 만들기 위해 MySQL의 팔에 JSON_TABLE 기능을 추가 할 수 있습니다 이거 더 쉬워. (http://mysqlserverteam.com/mysql-8-0-labs-json-aggregation-functions/)

+0

네, 기본적으로 제 질문의 첫 번째 예와 같습니다. 작동하지만, 못 생기고 UNION을 여러 번 복제해야합니다. 여기서 기본적인 문제는 MySQL이 사용자 정의가 아닌 내장 함수가 아닌 테이블 값 함수를 지원한다는 것입니다. Hoping을 사용하면 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을 저장하도록 데이터 모델을 수정했습니다. 이 프로세스를 수행 할 때 나는 최대 크기를 모르기 때문에 스토어드 프로 시저에서 잠시 동안 사용해야했습니다 :

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 ; 
관련 문제