Query nested arrays in PostgreSQL JSON data

The following is an example showing how to query multiple nested arrays in JSON data in a PostgreSQL database.

Given

CREATE TABLE sample_json (
  id serial PRIMARY KEY,
  name varchar(64),
  json_data json
);


INSERT INTO sample_json (name, json_data)
VALUES
	(
	'NA',
	'
	{
	    "location": "US",
	    "topLevelArray": [
	        {
	            "id": 1,
	            "secondLevelArray": [
	                {
	                    "key": "someKey",
	                    "operator": "=",
	                    "value": 10
	                },
	                {
	                    "key": "foo",
	                    "operator": ">=",
	                    "value": 5
	                },
	                {
	                    "key": "someOtherKey",
	                    "operator": ">",
	                    "value": 647
	                }
	            ]
	        },
	        {
	            "id": 2,
	            "secondLevelArray": [
	                {
	                    "key": "blah",
	                    "operator": "<",
	                    "value": 7
	                }
	            ]
	        }
	    ]
	}
	'
	),
	(
	'EU',
	'
	{
	    "location": "poland",
	    "topLevelArray": [
	        {
	            "id": 2,
	            "secondLevelArray": [
	                {
	                    "key": "bar",
	                    "operator": "<",
	                    "value": 10
	                },
	                {
	                    "key": "moo",
	                    "operator": ">=",
	                    "value": 16
	                },
	                {
	                    "key": "baz",
	                    "operator": "!=",
	                    "value": 9
	                }
	            ]
	        }
	    ]
	}
	'	
	)
;

With the aforementioned data, let’s say we want to know the id of the rows that have an object in the secondLevelArray with operator equal to >= and the value of the key field.

The concept to understand to be able to search for all of the keys in the secondLevelArray where the operator is >= is the lateral join. The TLDR; is that a subquery appearing in the FROM clause can reference columns provided by preceding items. Or, you can write clauses in the FROM clause that read from the result of previous FROM clauses.

Let’s run some queries and go through them, line-by-line. First we will just select everything in the table.

SELECT * FROM sample_json;

|id |name|json_data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
|---|----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|1  |NA  |"
	{
	    \"location\": \"US\",
	    \"topLevelArray\": [
	        {
	            \"id\": 1,
	            \"secondLevelArray\": [
	                {
	                    \"key\": \"someKey\",
	                    \"operator\": \"=\",
	                    \"value\": 10
	                },
	                {
	                    \"key\": \"foo\",
	                    \"operator\": \">=\",
	                    \"value\": 5
	                },
	                {
	                    \"key\": \"someOtherKey\",
	                    \"operator\": \">\",
	                    \"value\": 647
	                }
	            ]
	        },
	        {
	            \"id\": 2,
	            \"secondLevelArray\": [
	                {
	                    \"key\": \"blah\",
	                    \"operator\": \"<\",
	                    \"value\": 7
	                }
	            ]
	        }
	    ]
	}
	"|
|2  |EU  |"
	{
	    \"location\": \"poland\",
	    \"topLevelArray\": [
	        {
	            \"id\": 2,
	            \"secondLevelArray\": [
	                {
	                    \"key\": \"bar\",
	                    \"operator\": \"<\",
	                    \"value\": 10
	                },
	                {
	                    \"key\": \"moo\",
	                    \"operator\": \">=\",
	                    \"value\": 16
	                },
	                {
	                    \"key\": \"baz\",
	                    \"operator\": \"!=\",
	                    \"value\": 9
	                }
	            ]
	        }
	    ]
	}
	"                                                                                                                                                                                                                                                                      |

As expected, we just get back everything.

Now, let’s start to drill down into the JSON object. First we will select the row id and just the data from the topLevelArray.

SELECT
	sj.id
	topLevelArray
FROM
	sample_json sj,
	json_array_elements(json_data -> 'topLevelArray') topLevelArray
;

|id |toplevelarray                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|1  |"{
	            \"id\": 1,
	            \"secondLevelArray\": [
	                {
	                    \"key\": \"someKey\",
	                    \"operator\": \"=\",
	                    \"value\": 10
	                },
	                {
	                    \"key\": \"foo\",
	                    \"operator\": \">=\",
	                    \"value\": 5
	                },
	                {
	                    \"key\": \"someOtherKey\",
	                    \"operator\": \">\",
	                    \"value\": 647
	                }
	            ]
	        }"|
|1  |"{
	            \"id\": 2,
	            \"secondLevelArray\": [
	                {
	                    \"key\": \"blah\",
	                    \"operator\": \"<\",
	                    \"value\": 7
	                }
	            ]
	        }"                                                                                                                                                                                                                                                                                                                                    |
|2  |"{
	            \"id\": 2,
	            \"secondLevelArray\": [
	                {
	                    \"key\": \"bar\",
	                    \"operator\": \"<\",
	                    \"value\": 10
	                },
	                {
	                    \"key\": \"moo\",
	                    \"operator\": \">=\",
	                    \"value\": 16
	                },
	                {
	                    \"key\": \"baz\",
	                    \"operator\": \"!=\",
	                    \"value\": 9
	                }
	            ]
	        }"             |

The “columns” selected are the id and topLevelArray. The id is straightforward. The topLevelArray is a lateral join clause statement and we use the json_array_elements() function to select the contents of the json_data.topLevelArray key.

We can continue to traverse deeper into the JSON object with another lateral join clause that accesses the next nested array from the topLevelArray key.

SELECT
	sj.id,
	secondLevelElements
FROM
	sample_json sj,
	json_array_elements(json_data -> 'topLevelArray') topLevelArray,
	json_array_elements(topLevelArray -> 'secondLevelArray') secondLevelElements
;

|id |secondlevelelements                                                                                                                                   |
|---|----------------------------------------------------------------------------------------------------------------------------------------------------|
|1  |"{
	                    \"key\": \"someKey\",
	                    \"operator\": \"=\",
	                    \"value\": 10
	                }"      |
|1  |"{
	                    \"key\": \"foo\",
	                    \"operator\": \">=\",
	                    \"value\": 5
	                }"          |
|1  |"{
	                    \"key\": \"someOtherKey\",
	                    \"operator\": \">\",
	                    \"value\": 647
	                }"|
|1  |"{
	                    \"key\": \"blah\",
	                    \"operator\": \"<\",
	                    \"value\": 7
	                }"          |
|2  |"{
	                    \"key\": \"bar\",
	                    \"operator\": \"<\",
	                    \"value\": 10
	                }"          |
|2  |"{
	                    \"key\": \"moo\",
	                    \"operator\": \">=\",
	                    \"value\": 16
	                }"         |
|2  |"{
	                    \"key\": \"baz\",
	                    \"operator\": \"!=\",
	                    \"value\": 9
	                }"          |

We add an additional FROM clause. This one referencing the result of the previous FROM clause.

json_array_elements(json_data -> 'topLevelArray') topLevelArray,
json_array_elements(topLevelArray -> 'secondLevelArray') secondLevelElements

Now we have access to the elements in the secondLevelArray and we can add a WHERE clause to select only what we want from that nested array.

select
	sj.id,
	secondLevelElements ->> 'key'
from
	sample_json sj,
	json_array_elements(json_data -> 'topLevelArray') topLevelArray,
	json_array_elements(topLevelArray -> 'secondLevelArray') secondLevelElements
where
	secondLevelElements ->> 'operator' = '>='
;

|id |?column?|
|---|--------|
|1  |foo     |
|2  |moo     |

The result being the row id, and the value of the key field in inner most nested object.

PostgreSQL Query to Find Tables With Name LIKE

Sometimes you are working with a PostgreSQL database with A LOT of tables and looking for tables that contain a sub-string in their name. Following is a query that you can run that will return all of the tables that have the string in their name:

SELECT
  table_schema,
  table_name
FROM
  information_schema.tables
WHERE
      table_name LIKE '%<string>%'
  AND table_schema not in ('information_schema', 'pg_catalog')
  AND table_type = 'BASE TABLE'
ORDER BY
  table_name, table_schema
;

Unable to Set the Path To java.exe When Running Oracle SQL Developer Under Windows 7

I was trying to run Oracle SQL Developer for the first time on a new machine.  When firing it up, it presented me with a dialog box asking me to “Enter the full pathname for the java.exe file”.

OK, no problem.  So I find the path to the java.exe binary that was just installed with the SDK.  Hit submit . . . and nothing happens.  It blanks out the text field and the dialog box stares back at me.

I tried pointing it to the java.exe that was in the jre dir.  No joy.

After a lot of futzing around and doing some searches it turns out that there are a few things that you have to do to get it to run for the first time.

First, right-click on the Oracle SQL Developer short-cut and select “Run as Administrator”.

Then, in the dialog box, click Browse and navigate to the JDK that comes with the Oracle install.  For me it was in C:\Oracle11G\11.2.0.3\jdk\bin\java.exe

Once I did that it fired right up.  I then quit, and ran it as my user and it seemed to start up just fine.

Executing Dynamically Generated SQL Queries in a Shell Script and Saving the Output to a Variable

If you would like to, in a shell script, dynamically generate SQL queries for MySQL and save the output of those queries to a variable that you can then use in the script, here is an example:

#!/bin/bash

for i in `cat tables_list.txt`
do

   # Build the query
   QUERY="SELECT count(*) FROM ${i}"

   # Run the query from the command-line and save the
   # output into the $ROW_COUNT variable
   ROW_COUNT=$(echo $QUERY | mysql -u${USER_NAME} -p${PASSWORD} -h ${HOST} -P ${PORT} --skip-column-names ${DBASE})

   # Do something with the var...
   echo -n -e "$ROW_COUNT\t" >> $OUT_FILE
   echo "$i" >> $OUT_FILE

done;