db:Postgresql-14. Aceasta va fi o rare de transformare, și eu sunt în căutarea pentru recomandări / îmbunătățiri care pot fi făcute astfel încât să pot învăța/perfecționa mea postgres/json abilitățile (și viteza/optimiza acest foarte lent interogare).
Vom primi variabila dimensiunea/structura obiecte json de la un api extern.
Fiecare obiect json este un sondaj de răspuns. Fiecare imbricate "întrebare/răspuns" obiect poate avea o structură destul de diferită. În total sunt aproximativ ~5 cunoscute structuri.
Răspuns obiecte sunt stocate într-un jsonb coloana care are o jsonb_ops gin index.
Masa a aproximativ 500.000 de rânduri. Fiecare rând e jsonb coloana obiect are aproximativ 200 de valori imbricate.
Scopul nostru este de a extrage toate imbricate întrebare/răspuns răspunsuri într-un alt tabel de identitate,întrebare,răspuns. Pe de destinație tabel vom face extinsă interogarea cu FTS și trigrama, și sunt menite pentru schema simplitate. De aceea extrag la un tabel simplu în loc de a face ceva mai exotic cu jsonb interogarea. Există, de asemenea, o mulțime de metadate cruft în acele obiecte de care nu am nevoie. Deci, eu sunt, de asemenea, speranța de a salva un spațiu de arhivare originea masă (e 5GB + indici).
În mod special mi-ar plăcea să învețe un mod mai elegant de traversare și extragerea json la destinație masă.
Și am fost în imposibilitatea de a figura o modalitate de a arunca rezultatele efective sql text în loc de citat jsontext (în mod normal, aș folosi ->>, ::text, sau _text versiune a jsonb funcția)
Aceasta este o versiune foarte simplificată a obiect json pentru a ușura doar rulează acest lucru.
Va multumesc anticipat!
create table test_survey_processing(
id integer generated always as identity constraint test_survey_processing_pkey primary key,
json_data jsonb
);
insert into test_survey_processing (json_data)
values ('{"survey_data": {"2": {"answer": "Option 1", "question": "radiobuttonquesiton"}, "3": {"options": {"10003": {"answer": "Option 1"}, "10004": {"answer": "Option 2"}}, "question": "checkboxquestion"}, "5": {"answer": "Column 2", "question": "Row 1"}, "6": {"answer": "Column 2", "question": "Row 2"}, "7": {"question": "checkboxGRIDquesiton", "subquestions": {"8": {"10007": {"answer": "Column 1", "question": "Row 1 : Column 1"}, "10008": {"answer": "Column 2", "question": "Row 1 : Column 2"}}, "9": {"10007": {"answer": "Column 1", "question": "Row 2 : Column 1"}, "10008": {"answer": "Column 2", "question": "Row 2 : Column 2"}}}}, "11": {"answer": "Option 1", "question": "Row 1"}, "12": {"answer": "Option 2", "question": "Row 2"}, "13": {"options": {"10011": {"answer": "Et molestias est opt", "option": "Option 1"}, "10012": {"answer": "Similique magnam min", "option": "Option 2"}}, "question": "textboxlist"}, "14": {"question": "textboxgridquesiton", "subquestions": {"15": {"10013": {"answer": "Qui error magna omni", "question": "Row 1 : Column 1"}, "10014": {"answer": "Est qui dolore dele", "question": "Row 1 : Column 2"}}, "16": {"10013": {"answer": "vident mol", "question": "Row 2 : Column 1"}, "10014": {"answer": "Consectetur dolor co", "question": "Row 2 : Column 2"}}}}, "17": {"question": "contactformquestion", "subquestions": {"18": {"answer": "Rafael", "question": "First Name"}, "19": {"answer": "Adams", "question": "Last Name"}}}, "33": {"question": "customgroupquestion", "subquestions": {"34": {"answer": "Sed magnam enim non", "question": "customgroupTEXTbox"}, "36": {"answer": "Option 2", "question": "customgroupradiobutton"}, "37": {"options": {"10021": {"answer": "Option 1", "option": "customgroupCHEC KBOX question : Option 1"}, "10022": {"answer": "Option 2", "option": "customgroupCHEC KBOX question : Option 2"}}, "question": "customgroupCHEC KBOX question"}}}, "38": {"question": "customTABLEquestion", "subquestions": {"10001": {"answer": "Option 1", "question": "customTABLEquestioncolumnRADIO"}, "10002": {"answer": "Option 2", "question": "customTABLEquestioncolumnRADIO"}, "10003": {"options": {"10029": {"answer": "OPTION1"}, "10030": {"answer": "OPTION2"}}, "question": "customTABLEquestioncolumnCHECKBOX"}, "10004": {"options": {"10029": {"answer": "OPTION1"}, "10030": {"answer": "OPTION2"}}, "question": "customTABLEquestioncolumnCHECKBOX"}, "10005": {"answer": "Aperiam itaque dolor", "question": "customTABLEquestioncolumnTEXTBOX"}, "10006": {"answer": "Hic qui numquam inci", "question": "customTABLEquestioncolumnTEXTBOX"}}}}}');
create index test_survey_processing_gin_index on test_survey_processing using gin (json_data);
-- the query I'm using (it works, but it is unmanageably slow)
-- EXPLAIN (ANALYZE, VERBOSE, BUFFERS, FORMAT JSON)
select level1.value['question'] question, level1.value['answer'] as answer ,tgsr.json_data['survey_data']
from test_survey_processing tgsr,
jsonb_each(tgsr.json_data['survey_data']::jsonb) level1
-- where survey_id = 6633968 and id = 4
union
select level1.value['question'] question, jsonb_path_query(level1.value, '$.answer')::jsonb as answer ,tgsr.json_data['survey_data']
from test_survey_processing tgsr,
jsonb_each(tgsr.json_data['survey_data']::jsonb) level1
-- where survey_id = 6633968 and id = 4
union
select level1.value['question'] question, jsonb_path_query(level1.value, '$.options.*.answer')::jsonb as answer ,tgsr.json_data['survey_data']
from test_survey_processing tgsr,
jsonb_each(tgsr.json_data['survey_data']::jsonb) level1
-- where survey_id = 6633968 and id = 4
union
select level1.value['question'] question, jsonb_path_query(level1.value, '$.subquestions.*.*.answer')::jsonb as answer ,tgsr.json_data['survey_data']
from test_survey_processing tgsr,
jsonb_each(tgsr.json_data['survey_data']::jsonb) level1
-- where survey_id = 6633968 and id = 4
URMARIRE EDITA DUPĂ RAFINARE ȘI DE A OBȚINE REZULTATUL AVEAM NEVOIE
Acest lucru este interogarea am ajuns de funcționare. A luat 11min pentru a procesa și de a introduce 34million înregistrări. Ceea ce este bine, deoarece este un timp de funcționare.
Câteva comentarii cu privire la modificările le-am făcut
-Am folosit -> si>> în loc de [subscripting] când am citit că, chiar și în pg14, subscripting nu utilizați indici (nu sunt sigur dacă asta contează în DE)
-la "to_json(...) #>> '{}'"
este cum m-am convertit json string la un necotate șir bazat pe acest lucru: stack overflow răspunde
create table respondent_questions_answers as
select tgsr.id,tgsr.survey_id,level1.value ->> 'question' question, '' as sub_question,
to_json(jsonb_path_query(level1.value, '$.answer')) #>> '{}' as answer
from test_survey_processing tgsr, jsonb_each(tgsr.json -> 'survey_data') level1
union
select tgsr.id,tgsr.survey_id,level1.value ->> 'question' question,
to_json(jsonb_path_query(level1.value, '$.options.*.option')) #>> '{}' as sub_question,
to_json(jsonb_path_query(level1.value, '$.options.*.answer')) #>> '{}' as answer
from test_survey_processing tgsr, jsonb_each(tgsr.json -> 'survey_data') level1
union
select tgsr.id,tgsr.survey_id,level1.value ->> 'question' question,
to_json(jsonb_path_query(level1.value, '$.subquestions.*.*.question')) #>> '{}' as sub_question,
to_json(jsonb_path_query(level1.value, '$.subquestions.*.*.answer')) #>> '{}' as answer
from test_survey_processing tgsr, jsonb_each(tgsr.json -> 'survey_data') level1
union
select tgsr.id,tgsr.survey_id,level1.value ->> 'question' question,
to_json(jsonb_path_query(level1.value, '$.subquestions.*.question')) #>> '{}' as sub_question,
to_json(jsonb_path_query(level1.value, '$.subquestions.*.answer')) #>> '{}' as answer
from test_survey_processing tgsr, jsonb_each(tgsr.json -> 'survey_data') level1;
Editarea finală după acceptarea de mai jos răspunsul soluție
Vă mulțumim pentru @Edouard H. răspuns și cu o mai bună înțelegere a modului de a utiliza în mod corect jsonb_path_query, am fost capabil de a elimina toate UNION SELECT
, descoperi unele valori care au fost lipsă, și a elimina nevoia pentru to_json hack. Chiar dacă CROSS JOIN LATERAL
este implicită cu json funcții, este o formă mai bună pentru a include JOIN
în loc de virgule ca ele sunt mult mai strâns legate, și mai ușor de citit. Mai jos este ultima interogare-am folosit.
SELECT concat_ws(' ',
qu.value::jsonb->>'question'
, an.answer::jsonb->>'question'
, an.answer::jsonb->>'option') AS question
, an.answer::jsonb->>'answer' AS answer
-- , tgsr.json_data->>'survey_data'
FROM test_survey_processing tgsr
CROSS JOIN LATERAL jsonb_each(tgsr.json_data->'survey_data') AS qu
CROSS JOIN LATERAL jsonb_path_query(qu.value::jsonb, '$.** ? (exists(@.answer))') AS an(answer)