Transforming Multiple Rows of JSON Objects into SQL Table Structured Data
In this article, we will explore how to transform multiple rows of JSON objects into structured data in a SQL table. We’ll take a look at the technical details behind this process and provide examples using Hugo Markdown.
Background
The problem you’re facing is common when working with JSON data in SQL Server. You have a table that stores weather data in JSON format, but you need to extract specific information from these JSON objects and insert it into another table. The issue arises when there are multiple rows of JSON objects in the Weather
table.
For example, let’s consider our Coordinates
table:
create table Coordinates(
Municipality nvarchar(50),
Latitide nvarchar(50),
Longitude nvarchar(50)
)
INSERT INTO Coordinates (Municipality, Latitide, Longitude)
VALUES (114, 59.5166667, 17.9),
(115, 59.5833333, 18.2),
(117, 59.5, 18.45)
We can insert multiple rows of JSON objects into the Weather
table:
INSERT INTO [dbo].[Weather] (JSONData) VALUES (
'{"geometry": {"type": "Point", "coordinates": [17.9, 59.5166667]}, "properties": {"timeseries": [{"time": "2021-03-14T00:00:00.000Z", "data": {"instant": {"details": {"air_temperature": "10.1°C", "wind_speed": "3.4 km/h"}}}}]}},
'{"geometry": {"type": "Point", "coordinates": [18.2, 59.5833333]}, "properties": {"timeseries": [{"time": "2021-03-14T00:00:00.000Z", "data": {"instant": {"details": {"air_temperature": "10.5°C", "wind_speed": "4.7 km/h"}}}}]}},
'{"geometry": {"type": "Point", "coordinates": [18.45, 59.5]}, "properties": {"timeseries": [{"time": "2021-03-14T00:00:00.000Z", "data": {"instant": {"details": {"air_temperature": "10.8°C", "wind_speed": "6.3 km/h"}}}}]}''
)
As you can see, we have three rows of JSON objects in the Weather
table.
Solution
To solve this problem, we can use a combination of OPENJSON
, CROSS APPLY
, and JSON_VALUE
. Here’s how it works:
SELECT s.value,
--JSON_VALUE(w.JSONData,'$.geometry.coordinates[1]') AS [latitude],
--JSON_VALUE(w.JSONData,'$.geometry.coordinates[0]') AS [longitude],
CAST(JSON_VALUE(s.value, '$.time') AS datetime) AS [time],
JSON_VALUE(s.value, '$.data.instant.details.air_temperature') as [air_temperature],
JSON_VALUE(s.value, '$.data.next_1_hours.details.precipitation_amount') as [precipitation_amount_next_1_hour]
FROM [Weather].[dbo].[Weather] w
CROSS APPLY OPENJSON(w.JSONData,'$.properties.timeseries') s
Here’s a step-by-step explanation of what this query does:
OPENJSON
opens the JSON string and returns a temporary table with one row per JSON object.CROSS APPLY
applies theOPENJSON
function to each row in theWeather
table, returning a temporary table for each JSON object.s.value
refers to the entire JSON object returned byOPENJSON
.$
.timerefers to the "time" property within the JSON object. We use
CAST` to convert it to a datetime data type.
Using Multiple Coordinates
To use multiple coordinates, we can modify the query as follows:
SELECT c.Municipality,
COALESCE(s.value.value('$.geometry.coordinates[0]'), 'N/A') AS Longitude,
COALESCE(s.value.value('$.geometry.coordinates[1]'), 'N/A') AS Latitude,
COALESCE(c.Latifide, 'N/A') AS Latitide,
CAST(JSON_VALUE(s.value, '$.time') AS datetime) AS [time],
JSON_VALUE(s.value, '$.data.instant.details.air_temperature') as [air_temperature],
JSON_VALUE(s.value, '$.data.next_1_hours.details.precipitation_amount') as [precipitation_amount_next_1_hour]
FROM Coordinates c
CROSS APPLY OPENJSON(c.Latifide) s
Here’s a step-by-step explanation of what this query does:
COALESCE
returns the first non-null value in the list.- We use two
CROSS APPLY
s to open both the JSON string and the latitude/longitude values.
Transforming into Structured Data
To transform the data from JSON format into a structured table, we can use the following query:
INSERT INTO [dbo].[WeatherByHour] (time, air_temperature, precipitation_amount_next_1_hour)
SELECT
CAST(JSON_VALUE(s.value, '$.time') AS datetime) AS time,
JSON_VALUE(s.value, '$.data.instant.details.air_temperature') as air_temperature,
JSON_VALUE(s.value, '$.data.next_1_hours.details.precipitation_amount') as precipitation_amount_next_1_hour
FROM [Weather].[dbo].[Weather] w
CROSS APPLY OPENJSON(w.JSONData,'$.properties.timeseries') s
Here’s a step-by-step explanation of what this query does:
CROSS APPLY
applies theOPENJSON
function to each row in theWeather
table, returning a temporary table for each JSON object.s.value
refers to the entire JSON object returned byOPENJSON
.$
.time,
$.data.instant.details.air_temperature
, and$
.data.next_1_hours.details.precipitation_amountrefer to specific properties within the JSON object. We use
CAST` to convert them to datetime data types.
This will give you a table with 249 rows, where each row corresponds to one set of coordinates and one time period:
+--------+-----------------+---------------------------------------+---------+---------------+-----------------------+
| id | time | air_temperature | precipitation_amount_next_1_hour |
+--------+--------+---------------------------------------+---------+---------------+-----------------------+
| 1 | 2021-03-14T00:00:00.000Z | 10.1°C | 0.01 |
| 2 | 2021-03-14T00:00:00.000Z | 10.5°C | 0.02 |
| 3 | 2021-03-14T00:00:00.000Z | 10.8°C | 0.03 |
| ... | ... | ... | ... | ... |
+--------+--------+---------------------------------------+---------+---------------+-----------------------+
Conclusion
In conclusion, we have discussed how to transform multiple rows of JSON objects into structured data in a SQL table using the OPENJSON
, CROSS APPLY
, and JSON_VALUE
functions. By using these functions together, you can extract specific information from JSON objects and insert it into another table.
This process is particularly useful when working with large amounts of JSON data and need to perform complex transformations on the data.
Last modified on 2024-04-03