JSON Function in SQL

Overview

Ever consider how much more beneficial it would be for you to be able to simply arrange all your SQL result sets into JSON objects? Whether you have considered it or not, you can achieve desired set of results with SQL JSON Functions. In the article, we shall try to help ourselves understand its concept.

Fortunately, I recently had the chance to work with SQL JSON Functions which offers a fantastic alternative to traditional approach of getting data in JSON format from SQL tables. It enables you to combine relational and NoSQL ideas within a single database.

The JSON functions first added to SQL Server 2016 (13.x), which requires database compatibility level 130 or higher.

PreRequisites

We created a StudentManagement database with the 4 tables listed below, all of which include default data, to help illustrate the concept.

  • [dbo].[Students] (Storing basic details)
  • [dbo].[Students_Address] (Storing address details)
  • [dbo].[Students_Emails] (Storing multiple emails)
  • [dbo].[Students_Json] (Later to store JSON string of every student)
Click here, to copy the scripts used to create tables and databases with default values. 👈🏾
1
2/****** Object: Database [StudentManagement] Script Date: 5/8/2024 6:24:51 PM ******/
3CREATE DATABASE [StudentManagement]
4 CONTAINMENT = NONE
5 ON PRIMARY
6( NAME = N'StudentManagement', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL16.SQLEXPRESS\MSSQL\DATA\StudentManagement.mdf' , SIZE = 8192KB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB )
7 LOG ON
8( NAME = N'StudentManagement_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL16.SQLEXPRESS\MSSQL\DATA\StudentManagement_log.ldf' , SIZE = 8192KB , MAXSIZE = 2048GB , FILEGROWTH = 65536KB )
9 WITH CATALOG_COLLATION = DATABASE_DEFAULT, LEDGER = OFF
10GO
11ALTER DATABASE [StudentManagement] SET COMPATIBILITY_LEVEL = 160
12GO
13IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
14begin
15EXEC [StudentManagement].[dbo].[sp_fulltext_database] @action = 'enable'
16end
17GO
18ALTER DATABASE [StudentManagement] SET ANSI_NULL_DEFAULT OFF
19GO
20ALTER DATABASE [StudentManagement] SET ANSI_NULLS OFF
21GO
22ALTER DATABASE [StudentManagement] SET ANSI_PADDING OFF
23GO
24ALTER DATABASE [StudentManagement] SET ANSI_WARNINGS OFF
25GO
26ALTER DATABASE [StudentManagement] SET ARITHABORT OFF
27GO
28ALTER DATABASE [StudentManagement] SET AUTO_CLOSE OFF
29GO
30ALTER DATABASE [StudentManagement] SET AUTO_SHRINK OFF
31GO
32ALTER DATABASE [StudentManagement] SET AUTO_UPDATE_STATISTICS ON
33GO
34ALTER DATABASE [StudentManagement] SET CURSOR_CLOSE_ON_COMMIT OFF
35GO
36ALTER DATABASE [StudentManagement] SET CURSOR_DEFAULT GLOBAL
37GO
38ALTER DATABASE [StudentManagement] SET CONCAT_NULL_YIELDS_NULL OFF
39GO
40ALTER DATABASE [StudentManagement] SET NUMERIC_ROUNDABORT OFF
41GO
42ALTER DATABASE [StudentManagement] SET QUOTED_IDENTIFIER OFF
43GO
44ALTER DATABASE [StudentManagement] SET RECURSIVE_TRIGGERS OFF
45GO
46ALTER DATABASE [StudentManagement] SET DISABLE_BROKER
47GO
48ALTER DATABASE [StudentManagement] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
49GO
50ALTER DATABASE [StudentManagement] SET DATE_CORRELATION_OPTIMIZATION OFF
51GO
52ALTER DATABASE [StudentManagement] SET TRUSTWORTHY OFF
53GO
54ALTER DATABASE [StudentManagement] SET ALLOW_SNAPSHOT_ISOLATION OFF
55GO
56ALTER DATABASE [StudentManagement] SET PARAMETERIZATION SIMPLE
57GO
58ALTER DATABASE [StudentManagement] SET READ_COMMITTED_SNAPSHOT OFF
59GO
60ALTER DATABASE [StudentManagement] SET HONOR_BROKER_PRIORITY OFF
61GO
62ALTER DATABASE [StudentManagement] SET RECOVERY SIMPLE
63GO
64ALTER DATABASE [StudentManagement] SET MULTI_USER
65GO
66ALTER DATABASE [StudentManagement] SET PAGE_VERIFY CHECKSUM
67GO
68ALTER DATABASE [StudentManagement] SET DB_CHAINING OFF
69GO
70ALTER DATABASE [StudentManagement] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF )
71GO
72ALTER DATABASE [StudentManagement] SET TARGET_RECOVERY_TIME = 60 SECONDS
73GO
74ALTER DATABASE [StudentManagement] SET DELAYED_DURABILITY = DISABLED
75GO
76ALTER DATABASE [StudentManagement] SET ACCELERATED_DATABASE_RECOVERY = OFF
77GO
78ALTER DATABASE [StudentManagement] SET QUERY_STORE = ON
79GO
80ALTER DATABASE [StudentManagement] SET QUERY_STORE (OPERATION_MODE = READ_WRITE, CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30), DATA_FLUSH_INTERVAL_SECONDS = 900, INTERVAL_LENGTH_MINUTES = 60, MAX_STORAGE_SIZE_MB = 1000, QUERY_CAPTURE_MODE = AUTO, SIZE_BASED_CLEANUP_MODE = AUTO, MAX_PLANS_PER_QUERY = 200, WAIT_STATS_CAPTURE_MODE = ON)
81GO
82USE [StudentManagement]
83GO
84/****** Object: Table [dbo].[Students] Script Date: 5/8/2024 6:24:51 PM ******/
85SET ANSI_NULLS ON
86GO
87SET QUOTED_IDENTIFIER ON
88GO
89CREATE TABLE [dbo].[Students](
90 [Id] [int] IDENTITY(1,1) NOT NULL,
91 [FirstName] [nvarchar](50) NULL,
92 [LastName] [nvarchar](50) NULL,
93 [Age] [int] NULL,
94 CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED
95(
96 [Id] ASC
97)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
98) ON [PRIMARY]
99GO
100/****** Object: Table [dbo].[Students_Address] Script Date: 5/8/2024 6:24:51 PM ******/
101SET ANSI_NULLS ON
102GO
103SET QUOTED_IDENTIFIER ON
104GO
105CREATE TABLE [dbo].[Students_Address](
106 [Id] [int] IDENTITY(1,1) NOT NULL,
107 [Student_Id] [int] NOT NULL,
108 [Street] [nvarchar](500) NULL,
109 [City] [nvarchar](50) NULL,
110 [State] [nvarchar](50) NULL,
111 CONSTRAINT [PK_StudentAddress] PRIMARY KEY CLUSTERED
112(
113 [Id] ASC
114)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
115) ON [PRIMARY]
116GO
117/****** Object: Table [dbo].[Students_Emails] Script Date: 5/8/2024 6:24:51 PM ******/
118SET ANSI_NULLS ON
119GO
120SET QUOTED_IDENTIFIER ON
121GO
122CREATE TABLE [dbo].[Students_Emails](
123 [Id] [int] IDENTITY(1,1) NOT NULL,
124 [Student_Id] [int] NOT NULL,
125 [Email_Id] [nvarchar](200) NULL,
126 CONSTRAINT [PK_Student_Emails] PRIMARY KEY CLUSTERED
127(
128 [Id] ASC
129)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
130) ON [PRIMARY]
131GO
132/****** Object: Table [dbo].[Students_Json] Script Date: 5/8/2024 6:24:51 PM ******/
133SET ANSI_NULLS ON
134GO
135SET QUOTED_IDENTIFIER ON
136GO
137CREATE TABLE [dbo].[Students_Json](
138 [Student_Id] [int] NOT NULL,
139 [Json] [nvarchar](max) NOT NULL,
140 CONSTRAINT [PK_Students_Json] PRIMARY KEY CLUSTERED
141(
142 [Student_Id] ASC
143)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
144) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
145GO
146SET IDENTITY_INSERT [dbo].[Students] ON
147GO
148INSERT [dbo].[Students] ([Id], [FirstName], [LastName], [Age]) VALUES (1, N'Edwin', N'Hallwood', 33)
149GO
150INSERT [dbo].[Students] ([Id], [FirstName], [LastName], [Age]) VALUES (2, N'Marie', N'Allen', 34)
151GO
152INSERT [dbo].[Students] ([Id], [FirstName], [LastName], [Age]) VALUES (3, N'Keith', N'Umbridge', 35)
153GO
154INSERT [dbo].[Students] ([Id], [FirstName], [LastName], [Age]) VALUES (4, N'Richard', N'Johnston', 45)
155GO
156INSERT [dbo].[Students] ([Id], [FirstName], [LastName], [Age]) VALUES (5, N'Ivan', N'Tetley', 56)
157GO
158SET IDENTITY_INSERT [dbo].[Students] OFF
159GO
160SET IDENTITY_INSERT [dbo].[Students_Address] ON
161GO
162INSERT [dbo].[Students_Address] ([Id], [Student_Id], [Street], [City], [State]) VALUES (1, 1, N'Destiny Canyon Suite 869', N'Lulabury', N'Colorado')
163GO
164INSERT [dbo].[Students_Address] ([Id], [Student_Id], [Street], [City], [State]) VALUES (5, 2, N'Lillie Ferry', N'Satterfieldchester', N'Arizona')
165GO
166INSERT [dbo].[Students_Address] ([Id], [Student_Id], [Street], [City], [State]) VALUES (6, 3, N'White Key Suite 122', N'Odaborough', N'Tennessee')
167GO
168INSERT [dbo].[Students_Address] ([Id], [Student_Id], [Street], [City], [State]) VALUES (7, 4, N'75230 Rau Roads Suite 497', N'South Hopestad', N'Illinois')
169GO
170INSERT [dbo].[Students_Address] ([Id], [Student_Id], [Street], [City], [State]) VALUES (8, 5, N'Candice Village Apt. 059', N'Elisaburgh', N'Montana')
171GO
172SET IDENTITY_INSERT [dbo].[Students_Address] OFF
173GO
174SET IDENTITY_INSERT [dbo].[Students_Emails] ON
175GO
176INSERT [dbo].[Students_Emails] ([Id], [Student_Id], [Email_Id]) VALUES (1, 1, N'Edwin1@xyz.com')
177GO
178INSERT [dbo].[Students_Emails] ([Id], [Student_Id], [Email_Id]) VALUES (2, 1, N'Edwin2@xyz.com')
179GO
180INSERT [dbo].[Students_Emails] ([Id], [Student_Id], [Email_Id]) VALUES (3, 2, N'Marie@abc.com')
181GO
182INSERT [dbo].[Students_Emails] ([Id], [Student_Id], [Email_Id]) VALUES (4, 3, N'Keith1@xyz.com')
183GO
184INSERT [dbo].[Students_Emails] ([Id], [Student_Id], [Email_Id]) VALUES (6, 3, N'Keith2@pqr.com')
185GO
186INSERT [dbo].[Students_Emails] ([Id], [Student_Id], [Email_Id]) VALUES (7, 3, N'Keith3@abc.com')
187GO
188INSERT [dbo].[Students_Emails] ([Id], [Student_Id], [Email_Id]) VALUES (8, 4, N'Richard@xyz.com')
189GO
190INSERT [dbo].[Students_Emails] ([Id], [Student_Id], [Email_Id]) VALUES (9, 5, N'Ivan@xyz.com')
191GO
192SET IDENTITY_INSERT [dbo].[Students_Emails] OFF
193GO
194/****** Object: Index [UQ__Students__A2F4E98D31AD1952] Script Date: 5/8/2024 6:24:52 PM ******/
195ALTER TABLE [dbo].[Students_Address] ADD UNIQUE NONCLUSTERED
196(
197 [Student_Id] ASC
198)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
199GO
200ALTER TABLE [dbo].[Students_Address] WITH CHECK ADD CONSTRAINT [FK_StudentAddress_Student] FOREIGN KEY([Student_Id])
201REFERENCES [dbo].[Students] ([Id])
202GO
203ALTER TABLE [dbo].[Students_Address] CHECK CONSTRAINT [FK_StudentAddress_Student]
204GO
205ALTER TABLE [dbo].[Students_Emails] WITH CHECK ADD CONSTRAINT [FK_Student_Email] FOREIGN KEY([Student_Id])
206REFERENCES [dbo].[Students] ([Id])
207GO
208ALTER TABLE [dbo].[Students_Emails] CHECK CONSTRAINT [FK_Student_Email]
209GO
210USE [master]
211GO
212ALTER DATABASE [StudentManagement] SET READ_WRITE
213GO
214

Facts

❌ If we wanted to read student data as JSON, the traditional approach would have looked something like this 👇

1select '{"FirstName":' + s.FirstName + ',"LastName":' + s.LastName + ',"Age":' + CAST(s.Age AS VARCHAR(30)) + '}'
2from [dbo].[Students] s
3where id = 1

Output - {"FirstName":Edwin,"LastName":Hallwood,"Age":33}

✔️ Now that JSON functions have been introduced, it is remarkably simple to write the query below and obtain the same results.

1select *
2from [dbo].[Students]
3where id = 1
4FOR JSON Auto, Without_Array_Wrapper;

💡 JSON Auto, Without_Array_Wrapper: What is it?

  • To automatically format the JSON output according to the SELECT statement's structure, utilize the JSON Auto function.
  • Here, the results are always return as an array of objects enclosed in [] brackets. Therefore, to have just object as the return type, all we must do is use the keyword Without_Array_Wrapper to eliminate the [] from the output.

Examples

1. How can the address information be made to appear as a child of the root object under the Address object?

1select s.Id,
2 s.FirstName,
3 s.LastName,
4 s.Age,
5 sa.Street as 'Address.Street',
6 sa.City as 'Address.City',
7 sa.State as 'Address.State'
8from [dbo].[Students] s
9 inner join [dbo].[Students_Address] sa
10 on s.Id = sa.student_id
11Where s.id = 1
12FOR JSON Path, Without_Array_Wrapper;
To see the json output, click me 🙋
1{
2 "Id": 1,
3 "FirstName": "Edwin",
4 "LastName": "Hallwood",
5 "Age": 33,
6 "Address": {
7 "Street": "Destiny Canyon Suite 869",
8 "City": "Lulabury",
9 "State": "Colorado"
10 }
11}

We have utilized JSON Path instead of JSON Auto in this instance, as the Auto will produce results based on the select statement. PATH mode allows you to have complete control over the output of the FOR JSON clause.

2. Let's look at an example where you want multiple EmailIds of the user to appear as a child of the root object under the Emails object?

1select s.Id,
2 s.FirstName,
3 s.LastName,
4 s.Age,
5 sa.Street as 'Address.Street',
6 sa.City as 'Address.City',
7 sa.State as 'Address.State',
8 (
9 select Email_Id as EmailId
10 from [dbo].[Students_Emails] se
11 where se.student_id = s.id
12 For Json Path
13 ) as Emails
14from [dbo].[Students] s
15 inner join [dbo].[Students_Address] sa
16 on s.Id = sa.student_id
17where s.id = 3
18For Json Path, Without_Array_Wrapper, include_null_values
To see the json output, click me 🙋
1{
2 "Id":3,
3 "FirstName":"Keith",
4 "LastName":"Umbridge",
5 "Age":35,
6 "Address":{
7 "Street":"White Key Suite 122",
8 "City":"Odaborough",
9 "State":"Tennessee"
10 },
11 "Emails":[
12 {
13 "EmailId":"Keith1@xyz.com"
14 },
15 {
16 "EmailId":"Keith2@pqr.com"
17 },
18 {
19 "EmailId":"Keith3@abc.com"
20 }
21 ]
22}
23

Note: If you want null values to show up in the JSON output, you must use the INCLUDE_NULL_VALUES option. If this option is not utilized, the output for query results excludes JSON properties for NULL values.

3. Let's say that you want the list of all students under the Root Students.

1select s.Id,
2 s.FirstName,
3 s.LastName,
4 s.Age,
5 sa.Street as 'Address.Street',
6 sa.City as 'Address.City',
7 sa.State as 'Address.State',
8 (
9 select Email_Id as EmailId
10 from [dbo].[Students_Emails] se
11 where se.student_id = s.id
12 For Json Path
13 ) as Emails
14from [dbo].[Students] s
15 inner join [dbo].[Students_Address] sa
16 on s.Id = sa.student_id
17For Json Path, Root('Students'), include_null_values
To see the json output, click me 🙋
1{
2 "Students": [
3 {
4 "Id": 1,
5 "FirstName": "Edwin",
6 "LastName": "Hallwood",
7 "Age": 33,
8 "Address": {
9 "Street": "Destiny Canyon Suite 869",
10 "City": "Lulabury",
11 "State": "Colorado"
12 },
13 "Emails": [
14 {
15 "EmailId": "Edwin1@xyz.com"
16 },
17 {
18 "EmailId": "Edwin2@xyz.com"
19 }
20 ]
21 },
22 {
23 "Id": 2,
24 "FirstName": "Marie",
25 "LastName": "Allen",
26 "Age": 34,
27 "Address": {
28 "Street": "Lillie Ferry",
29 "City": "Satterfieldchester",
30 "State": "Arizona"
31 },
32 "Emails": [
33 {
34 "EmailId": "Marie@abc.com"
35 }
36 ]
37 },
38 {
39 "Id": 3,
40 "FirstName": "Keith",
41 "LastName": "Umbridge",
42 "Age": 35,
43 "Address": {
44 "Street": "White Key Suite 122",
45 "City": "Odaborough",
46 "State": "Tennessee"
47 },
48 "Emails": [
49 {
50 "EmailId": "Keith1@xyz.com"
51 },
52 {
53 "EmailId": "Keith2@pqr.com"
54 },
55 {
56 "EmailId": "Keith3@abc.com"
57 }
58 ]
59 },
60 {
61 "Id": 4,
62 "FirstName": "Richard",
63 "LastName": "Johnston",
64 "Age": 45,
65 "Address": {
66 "Street": "75230 Rau Roads Suite 497",
67 "City": "South Hopestad",
68 "State": "Illinois"
69 },
70 "Emails": [
71 {
72 "EmailId": "Richard@xyz.com"
73 }
74 ]
75 },
76 {
77 "Id": 5,
78 "FirstName": "Ivan",
79 "LastName": "Tetley",
80 "Age": 56,
81 "Address": {
82 "Street": "Candice Village Apt. 059",
83 "City": "Elisaburgh",
84 "State": "Montana"
85 },
86 "Emails": [
87 {
88 "EmailId": "Ivan@xyz.com"
89 }
90 ]
91 }
92 ]
93}

💡Now, having seen the fundamentals, let us drill a little further and explore JSON functions and their broader implications.

Up till now, we have learned how to produce a JSON string. Now, we will use the Student_Json Table to dump the JSON for every student.

Currently, the For JSON clause returns, by default, a single JSON string containing array of objects (i.e., Rows). However, in order to record it against student_Id, we require a unique JSON string object for each student. We would create the query below to manage it.

1INSERT INTO [DBO].[Students_Json]
2(
3 [Student_Id],
4 [Json]
5)
6SELECT b.id,
7 (
8 select s.FirstName,
9 s.LastName,
10 s.Age,
11 sa.Street as 'Address.Street',
12 sa.City as 'Address.City',
13 sa.State as 'Address.State',
14 (
15 select Email_Id as EmailId
16 from [dbo].[Students_Emails] se
17 where se.student_id = s.id
18 For Json Path
19 ) as Emails
20 from [dbo].[Students] s
21 inner join [dbo].[Students_Address] sa
22 on s.Id = sa.student_id
23 WHERE s.Id = b.Id
24 For Json Path, Without_Array_Wrapper, include_null_values
25 ) AS jsondata
26FROM Students b;

When above query got executed, The JSON string for every student got dumped into the Students_Json database as shown in the figure below.👇

Student_Json Dump Result

Now, let us play with the Students_Json Table. To get the desired outcomes, follow to the following examples that pertain to Student_Id = 1.

4. How to read the Address object from a JSON string?

1select s.student_Id,
2 JSON_QUERY(Json, '$.Address') as Address
3from [DBO].[Students_Json] s
4where student_id = 1

Json_Function Example 4

JSON_QUERY extracts an object or an array from a JSON string.
JSON_VALUE extract a scalar value from a JSON string instead of an object or an array.

5. How to read the FirstName from a JSON string?

1select s.student_Id,
2 JSON_Value(Json, '$.FirstName') as FirstName
3from [DBO].[Students_Json] s
4where student_id = 1

Json_Function Example 5

Note the difference: Attempting to read the FirstName attribute with Json_Query would have returned null because it is not an array or object type. It was substituted with Json_Value. Try it out!

6. In a JSON string, change Edwin's first name to Thomas's.

1UPDATE [DBO].[Students_Json]
2SET Json = JSON_MODIFY(Json, '$.FirstName', 'Thomas')
3from [DBO].[Students_Json] s
4where student_id = 1
To see the json output, click me 🙋
1{
2 "FirstName": "Thomas",
3 "LastName": "Hallwood",
4 "Age": 33,
5 "Address": {
6 "Street": "Destiny Canyon Suite 869",
7 "City": "Lulabury",
8 "State": "Colorado"
9 },
10 "Emails": [
11 {
12 "EmailId": "Edwin1@xyz.com"
13 },
14 {
15 "EmailId": "Edwin2@xyz.com"
16 }
17 ]
18}

7. In a JSON string, changing the State to Chicago and adding a new Email address.

1UPDATE [DBO].[Students_Json]
2SET Json = JSON_MODIFY(
3 JSON_MODIFY(Json, '$.Address.State', 'Chicago'),
4 'append $.Emails',
5 JSON_QUERY('{"EmailId":"Edwin3@xyz.com"}')
6 )
7from [DBO].[Students_Json] s
8where student_id = 1
To see the json output, click me 🙋
1{
2 "FirstName": "Thomas",
3 "LastName": "Hallwood",
4 "Age": 33,
5 "Address": {
6 "Street": "Destiny Canyon Suite 869",
7 "City": "Lulabury",
8 "State": "Chicago"
9 },
10 "Emails": [
11 {
12 "EmailId": "Edwin1@xyz.com"
13 },
14 {
15 "EmailId": "Edwin2@xyz.com"
16 },
17 {
18 "EmailId": "Edwin3@xyz.com"
19 }
20 ]
21}

8. How to Convert Json String to Sql RowSet?.

1select c.*,
2 d.emailid
3from
4(
5 SELECT a.student_Id,
6 b.*
7 FROM students_json a
8 cross apply
9 OPENJSON(a.json)
10 WITH
11 (
12 FirstName nVARCHAR(200) '$.FirstName',
13 LastName nVARCHAR(200) '$.LastName',
14 Age int '$.Age',
15 Street nVARCHAR(200) '$.Address.Street',
16 City nVARCHAR(200) '$.Address.City',
17 State nVARCHAR(200) '$.Address.State',
18 Emails nVARCHAR(max) as json
19 ) as b
20 where a.student_id = 1
21) c
22 cross apply
23 OPENJSON(c.emails)
24 WITH
25 (
26 EmailId nVARCHAR(max) '$.EmailId'
27 ) as d

Json_Function Example 8

OPENJSON is a table-valued function that parses JSON text and returns objects and properties from the JSON input as rows and columns. In other words, OPENJSON provides a rowset view over a JSON document

I hope you found this article enjoyable to read,If you would like, you can practice understanding the concept by going through each example one at a time. This will be very useful when working with JSON in SQL.