Navigation Bar

Saturday, March 1, 2025

A JSON generator using SQL and PLSQL

 Sample JSON file I want to create from table data

Suppose I have the account details of persons in the following tables

account, transactions and tdsinfo tables. For each account I want to create the json file format of data as per below using PLSQL. 

With this information we can build a suitable query to generate our JSON using some common oracle json functions like json_arrayagg and json_object.

{
    "Version": {
        "Ver": "1.1"
    },
    "acctdtls": {
        "acctname": "John Smith",
        "accttype": "S",
        "accno": 101,
        "transactions": [
            {
                "transID": 1,
                "transDate": "2025-01-01T12:32:37",
                "transAmount": 1000
            },
            {
                "transID": 2,
                "transDate": "2025-02-01T12:33:13",
                "transAmount": 2000
            },
            {
                "transID": 3,
                "transDate": "2025-03-01T12:33:35",
                "transAmount": 1500
            }
        ]
    },
    "tdsdtls": {
        "accno": 101,
        "tds_info": [
            {
                "tdsdate": "2025-01-01T12:32:37",
                "tdsamt": 100
            },
            {
                "tdsdate": "2025-03-01T12:33:35",
                "tdsamt": 110
            },
            {
                "tdsdate": "2025-02-01T12:33:13",
                "tdsamt": 105
            }
        ]
    }
}

We first create the tables and dummy data for running our JSON queries.

For a single tag say, acctInfo, below is the query and the output for the same.
SELECT json_object ('acctInfo' IS (json_object ('transactions' IS
  (SELECT json_arrayagg (json_object ('transID' IS trans_id, 'transDate' IS trans_dt, 'transAmount' IS trans_amt))
  FROM transactions
  WHERE 1            =1
  AND accounts.accno = transactions.accno
  ),'accountName' IS accname, 'accountType' IS acctype, 'accountNumber' IS accno)))
FROM accounts
WHERE accno = 101;
For this query you will get an output as below
{
    "acctInfo": {
        "transactions": [
            {
                "transID": 1,
                "transDate": "2024-09-28T18:49:57",
                "transAmount": 1000
            },
            {
                "transID": 2,
                "transDate": "2024-08-09T18:49:57",
                "transAmount": 2000
            },
            {
                "transID": 3,
                "transDate": "2024-10-08T18:49:57",
                "transAmount": 1500
            }
        ],
        "accountName": "John Smith",
        "accountType": "S",
        "accountNumber": 101
    }
}


The same query can be written in a a different style using JSON_OBJECT and JSON_ARRAYAGG as below and you will have the same output. 
SELECT json_object ('acctInfo' IS ( json_object (
  'transactions' VALUE 
  json_arrayagg 
      (json_object 
		 ('transID' VALUE t.trans_id, 
		 'transDate' VALUE t.trans_dt, 
		 'transAmount' VALUE t.trans_amt
	  )
  ),
  'accountName' VALUE a.accname, 
  'accountType' VALUE a.acctype, 
  'accountNumber' VALUE a.accno
  )))
  FROM transactions t, accounts a
  WHERE a.accno = t.accno
  AND a.accno = 101
  GROUP BY a.accname, a.acctype, a.accno;

Now if I want to show the tds information for this account in another node. I will have to add a new table to the select query to fetch the tds information and show this data in a new node. 
The advantage of below query is you have the flexibility to add more nodes to the array. 
I will do it as follows
SELECT json_object ('acctInfo' IS ( json_object (
  'transactions' VALUE 
  json_arrayagg 
      (json_object 
		 ('transID' VALUE t.trans_id, 
		 'transDate' VALUE t.trans_dt, 
		 'transAmount' VALUE t.trans_amt
	  )
  ),
  'tdsInfo' VALUE 
  json_arrayagg 
      (json_object 
		 ('accNo' VALUE ti.accno, 
		 'tdsDate' VALUE ti.tds_date, 
		 'tdsAmount' VALUE ti.tds_amount
	  )
  ),
  'accountName' VALUE a.accname, 
  'accountType' VALUE a.acctype, 
  'accountNumber' VALUE a.accno
  )))
  FROM transactions t, accounts a, tds_info ti
  WHERE a.accno = t.accno
    AND a.accno = ti.accno
    AND t.trans_dt = ti.tds_date
  AND a.accno = 101
  GROUP BY a.accname, a.acctype, a.accno;
The output is as below
{
    "acctInfo": {
        "transactions": [
            {
                "transID": 1,
                "transDate": "2025-01-01T12:32:37",
                "transAmount": 1000
            },
            {
                "transID": 3,
                "transDate": "2025-03-01T12:33:35",
                "transAmount": 1500
            },
            {
                "transID": 2,
                "transDate": "2025-02-01T12:33:13",
                "transAmount": 2000
            }
        ],
        "tdsInfo": [
            {
                "accNo": 101,
                "tdsDate": "2025-01-01T12:32:37",
                "tdsAmount": 100
            },
            {
                "accNo": 101,
                "tdsDate": "2025-03-01T12:33:35",
                "tdsAmount": 110
            },
            {
                "accNo": 101,
                "tdsDate": "2025-02-01T12:33:13",
                "tdsAmount": 105
            }
        ],
        "accountName": "John Smith",
        "accountType": "S",
        "accountNumber": 101
    }
}
The output of this query still does not give me the json in the format that is require. 
Below is the query which will give the desired output.
SELECT json_object(
          'Version' VALUE json_object('Ver' VALUE '1.1'),
          'acctdtls' VALUE treat(
            (SELECT   JSON_OBJECT(
		   KEY 'acctname' VALUE a.ACCNAME,
			 KEY 'accttype'   VALUE a.ACCTYPE,
			 KEY 'accno' VALUE a.ACCNO,
             KEY 'transactions' VALUE JSON_ARRAYAGG(
               JSON_OBJECT(
                 KEY 'transID' VALUE t.trans_id,
                 KEY 'transDate' VALUE t.trans_dt,
                 KEY 'transAmount' VALUE t.trans_amt
               )
               ORDER BY trans_id
             )
           RETURNING CLOB
         ) AS json
--  INTO   p_json_acct
  FROM   transactions t, accounts a
  WHERE a.accno = t.accno
  AND a.ACCNO = 101
  GROUP BY a.ACCTYPE, a.ACCNAME, a.ACCNO) as json 
          ),
     'tdsdtls' VALUE treat(     
    (SELECT   JSON_OBJECT(
             KEY 'accno' VALUE ACCNO,
             KEY 'tds_info' VALUE JSON_ARRAYAGG(
               JSON_OBJECT(
                 KEY 'tdsdate' VALUE TDS_DATE,
                 KEY 'tdsamt'   VALUE TDS_AMOUNT
               )
               ORDER BY ACCNO
             )
           RETURNING CLOB
         ) AS json
--  INTO   p_json_tdsinfo
  FROM   TDS_INFO
  WHERE ACCNO = 101
  GROUP BY ACCNO
  ) as json     
)
) 
FROM DUAL;
You can write the same query in a PLSQL block to make each json tag more readable.

Below is the sample block for the same.
DECLARE
  p_json_versn CLOB;
  p_json_acct CLOB;
  p_json_tdsinfo CLOB;
  p_json_all CLOB;
BEGIN
  p_json_versn := '{
        "Ver": "1.1"
    }';

  SELECT   JSON_OBJECT(
		   KEY 'acctname' VALUE a.ACCNAME,
			 KEY 'accttype'   VALUE a.ACCTYPE,
			 KEY 'accno' VALUE a.ACCNO,
             KEY 'transactions' VALUE JSON_ARRAYAGG(
               JSON_OBJECT(
                 KEY 'transID' VALUE t.trans_id,
                 KEY 'transDate' VALUE t.trans_dt,
                 KEY 'transAmount' VALUE t.trans_amt
               )
               ORDER BY trans_id
             )
           RETURNING CLOB
         ) AS json
  INTO   p_json_acct
  FROM   transactions t, accounts a
  WHERE a.accno = t.accno
  AND a.ACCNO = 101
  GROUP BY a.ACCTYPE, a.ACCNAME, a.ACCNO;
  
  SELECT   JSON_OBJECT(
             KEY 'accno' VALUE ACCNO,
             KEY 'tds_info' VALUE JSON_ARRAYAGG(
               JSON_OBJECT(
                 KEY 'tdsdate' VALUE TDS_DATE,
                 KEY 'tdsamt'   VALUE TDS_AMOUNT
               )
               ORDER BY ACCNO
             )
           RETURNING CLOB
         ) AS json
  INTO   p_json_tdsinfo
  FROM   TDS_INFO
  WHERE ACCNO = 101
  GROUP BY ACCNO;
  
  select json_object ( 
      'Version' value treat ( p_json_versn as json ),
      'acctdtls' value treat ( p_json_acct as json ),  
      'tdsdtls' value treat ( p_json_tdsinfo as json )
    ) 
    into p_json_all 
    from dual;
  
--  DBMS_OUTPUT.PUT_LINE(p_json_versn);
--  DBMS_OUTPUT.PUT_LINE(p_json_emp);
--  DBMS_OUTPUT.PUT_LINE(p_json_dept);
  DBMS_OUTPUT.PUT_LINE(p_json_all);
END;
/
The output of the PLSQL is similar to the output desired and from that achieved writing a single query.

References 


Thought for the day. 
  Blessings of Wisdom (On seeking wisdom)
Search out and seek, and she 
  will become known to you
and when you get hold of her,
  do not let her go.
For at last you will find the rest she gives.
  And she will be changed into joy for you.
Then her fetters will become for you a strong defense,
  And her collar a glorious robe.
Her yoke is a golden ornament,
  And her bonds a purple cord.
You will wear her like a glorious robe
  And put her on like a splendid crown.

Sirach 6:27:31

No comments:

Post a Comment