Как оптимизировать синтаксический анализ Json массива внутри функции PostgreSQL - PullRequest
0 голосов
/ 14 июля 2020

Я написал PostgreSQL функцию , в которой JSON массив разбирается внутри L oop. Проблема в том, что это занимает слишком много времени, если JSON размер массива превышает 1000 данных. Вдобавок к этому я использовал 2 оператора IF внутри l oop. Есть способы сделать это быстрее? .

Мой код

    WHILE incr < json_size LOOP
        jur_name2 := (SELECT my_json -> 'data' ->incr ->> 'JUR_NAME');
        filial_code2 := (SELECT my_json -> 'data' ->incr ->> 'FILIAL_CODE');
        activity_name2 := (SELECT my_json -> 'data' ->incr ->> 'ACTIVITY_NAME');
        client_code2 := (SELECT my_json -> 'data' ->incr ->> 'CLIENT_CODE');
        inn2 := (SELECT my_json -> 'data' ->incr ->> 'INN');
        account2 := (SELECT my_json -> 'data' ->incr ->> 'ACCOUNT');
        director_name2 := (SELECT my_json -> 'data' ->incr ->> 'DIRECTOR_NAME');
        director_passport2 := (SELECT my_json -> 'data' ->incr ->> 'DIRECTOR_PASSPORT');
        ----------------------------------
        phone_number2 := (SELECT my_json -> 'data' ->incr ->> 'PHONE_NUMBER');
        reg_date2 := (SELECT my_json -> 'data' ->incr ->> 'REG_DATE');
        owner_name2 := (SELECT my_json -> 'data' ->incr ->> 'OWNER_NAME');
        accountant_name2 := (SELECT my_json -> 'data' ->incr ->> 'ACCOUNTANT_NAME');
        terminal2 := (SELECT my_json -> 'data' ->incr ->> 'TERMINAL');
        region_id2 := (SELECT my_json -> 'data' ->incr ->> 'REGION_ID');
        credit_remainder := (SELECT my_json -> 'data' ->incr ->> 'CREDIT_REMAINDER');
        credit_cnt   := (SELECT my_json -> 'data' ->incr ->> 'CRED_CNT');
        end_date     := (SELECT CAST(my_json -> 'data' ->incr ->> 'END_DATE' AS DATE));

        IF(NOT EXISTS(SELECT * FROM sp_clients WHERE client_code = client_code2)) THEN

            INSERT INTO sp_clients(jur_name,filial_code,activity_name,client_code,inn,account,director_name,director_passport,phone_number,reg_date,owner_name,accountant_name,num_of_employees,plastic_cards,terminal,region_id,client_type) 
                VALUES(jur_name2,filial_code2,activity_name2,client_code2,inn2,account2,director_name2,director_passport2,phone_number2,reg_date2,owner_name2,accountant_name2,num_of_employees2,plastic_cards2,terminal2,region_id2,cl_type);
            
            new_clients_incr := new_clients_incr + 1;
            
        END IF;

        INSERT INTO loan_portfolio(client_code,summa,cnt,oper_month) 
            SELECT client_code2,credit_remainder,credit_cnt,now()::date 
            WHERE 
                NOT EXISTS (
                  SELECT 1 FROM loan_portfolio WHERE oper_month = now()::date AND client_code = client_code2
             );

        incr := incr + 1;

    END LOOP;

json

{
    "data": [
        {
            "ACTIVITY_NAME": "Gardening",
            "REGION_ID": 24,
            "FILIAL_CODE": "00760",
            "TERMINAL": 0,
            "DIRECTOR_PASSPORT": "DD12453",
            "INN": "233245",
            "REG_DATE": "2020-07-01",
            "END_DATE": "2020-06-30",
            "CRED_CNT": 6,
            "CREDIT_REMAINDER": 1.443828556508E10,
            "ACCOUNT": "24546000200199297001",
            "CLIENT_CODE": "65434234",
            "JUR_NAME": "ggg",
            "OWNER_NAME": "John Wick",
            "DIRECTOR_NAME": "Iron Man",
            "ACCOUNTANT_NAME": "Spider Man",
            "PHONE_NUMBER": "000123321"
        }
    ]
}

Как вы можете видеть в приведенном выше коде, внутри массива json есть множество атрибутов, и их просмотр занимает много времени. Вдобавок я создал индекс для столбцов, которые проверяю с помощью операторов If

1 Ответ

1 голос
/ 14 июля 2020

Вы можете сделать это с помощью одного оператора SQL, без l oop или PL / pg SQL требуется

with input (data) as (
  -- your full JSON goes here
  values ('{"data": [ { ...}, {....} ]}'::jsonb)
), new_clients as (
  INSERT INTO sp_clients (jur_name,filial_code,activity_name,client_code,inn,account,director_name,director_passport,phone_number,reg_date,owner_name,accountant_name,num_of_employees,plastic_cards,terminal,region_id,client_type) 
  SELECT x.e ->> 'JUR_NAME', x.e ->> 'FILIAL_CODE', ... and so on 
  from input 
    cross join jsonb_array_elements (data -> 'data') as x(e)
  on conflict (client_code) do nothing
)
INSERT INTO loan_portfolio(client_code,summa,cnt,oper_month) 
SELECT x.e ->> 'CLIENT_CODE', x.e ->> 'CREDIT_REMAINDER', x.e ->> 'CRED_CNT', current_date
from input 
  cross join jsonb_array_elements (data -> 'data') as x(e)
on conflict (oper_month, client_code) do nothing;

Если вы все же хотите поместить это в функцию и передать JSON в качестве параметра, вы можете немного упростить его, вызвав jsonb_array_elements() один раз для параметра:

with input as (
  select x.e ->> 'CLIENT_CODE' as client_code, 
         x.e ->> 'JUR_NAME' as jur_name, 
         x.e ->> 'FILIAL_CODE' as filial_code,
         x.e ->> 'CREDIT_REMAINDER' as credit_remainder,
         x.e ->> 'CRED_CNT' as cred_cnt,
         ... repeat for all keys
  from jsonb_array_elements(p_jsonb_parameter -> 'data') as x(e)
), new_clients as (
  INSERT INTO sp_clients (jur_name,filial_code,activity_name,client_code,inn,account,director_name,director_passport,phone_number,reg_date,owner_name,accountant_name,num_of_employees,plastic_cards,terminal,region_id,client_type) 
  SELECT jur_name, filial_code, .... other columns
  from input 
  on conflict (client_code) do nothing
)
INSERT INTO loan_portfolio(client_code,summa,cnt,oper_month) 
SELECT client_code, credit_remainder, cred_cnt, current_date
from input 
on conflict (oper_month, client_code) do nothing;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...