将JSON字符串转换为SQL Server中的规范化架构

问题描述 投票:5回答:1

我在Azure SQL Server数据库中有一个表,它只是一个带有JSON字符串的nvarchar(max)列,如下所示,每行一个扁平化为一行(约200,000行)

{
    "username": "George84",
    "items": [{
            "type": 8625,
            "score": "90"
        }, {
            "type": 8418,
            "score": "84"
        }, {
            "type": 7818,
            "score": "90"
        }, {
            "type": 23,
            "score": "q"
        }
    ]
}

每条记录都有一个用户名和一个包含一个或多个条目的项目数组。 items数组中的每个条目都包含一个类型字段(整数)和一个得分字段(字符串)。用户名是唯一的。多个用户可能具有相同类型的项目,并且这些重复项可能具有相同的分数,也可能不具有相同的分数。

我想将其转换为一组新的正确规范化的表,其架构如下所示:

Schema Diagram

我知道我可以使用外部应用程序来完成它,但我希望利用SQL Server 2016的new JSON processing capability完全在数据库/ TSQL中完成它。

完成此转换的最佳方法是什么?

sql-server json tsql sql-server-2016 database-normalization
1个回答
3
投票

这是我第一次使用Json和T-sql,但我觉得它很有趣,所以我认为可能有很多更好的解决方案,无论如何让我们开始吧。

首先我需要一些数据开始,所以我将生成一些临时数据如下: -

declare @jsonData Table (jsonText nvarchar(max))

insert into @jsonData(jsonText)values(N'{
    "username": "George84",
    "items": [{
            "type": 8625,
            "score": "90"
        }, {
            "type": 8418,
            "score": "84"
        }, {
            "type": 7818,
            "score": "90"
        }, {
            "type": 23,
            "score": "q"
        }
    ]
}'),(N'{
    "username": "George85",
    "items": [{
            "type": 8625,
            "score": "80"
        }, {
            "type": 8418,
            "score": "90"
        }, {
            "type": 7818,
            "score": "70"
        }, {
            "type": 232,
            "score": "q"
        }
    ]
}'),(N'{
    "username": "George86",
    "items": [{
            "type": 8626,
            "score": "80"
        }, {
            "type": 8418,
            "score": "70"
        }, {
            "type": 7818,
            "score": "90"
        }, {
            "type": 23,
            "score": "q"
        }
    ]
}'),(N'{
    "username": "George87",
    "items": [{
            "type": 8625,
            "score": "90"
        }, {
            "type": 8418,
            "score": "70"
        }, {
            "type": 7818,
            "score": "60"
        }, {
            "type": 23,
            "score": "q"
        }
    ]
}')

这会给我一个模拟主数据表的表。

让我们创建一些内存表来存储数据,如下所示:

declare @Users Table (ID int not null IDENTITY(1, 1),username nvarchar(50))
declare @Types Table (ID int not null IDENTITY(1, 1),[type] int)
declare @Scores Table (ID int not null IDENTITY(1, 1),score nvarchar(50))
declare @Items Table (ID int not null IDENTITY(1, 1),UserId int,TypeId int,ScoreId int)

您可能已经创建了上面的表,因此您可以替换它们。所以让我们跳转到将填充表格的t-sql

declare @RowsCount int=(select count(*) from @jsonData)
declare @index int=1
declare @jsonRowData NVARCHAR(MAX)
declare @username NVARCHAR(50)

while(@index<=@RowsCount)
begin
    ;with JsonDataWithSeq as (
        select ROW_NUMBER() over(order by jsonText) [seq],* from @jsonData
    ) select top(1) @jsonRowData=JsonDataWithSeq.jsonText from JsonDataWithSeq where seq=@index

    --select @jsonRowData [jsonRowData],ISJSON(@jsonRowData)  [ISJSON]

    SELECT @username=JSON_VALUE(@jsonRowData, '$.username') 

    if not exists (select * from @Users where username=@username)--no need for this check if names are unique in the list
    insert into @Users (username) values(@username)

    insert into @Types([type])
    SELECT xx.[type] from OPENJSON(@jsonRowData, 'lax $.items') with ([type] int) xx where xx.[type] not in (select t.[type] From @Types t)

    insert into @Scores([score])
    SELECT xx.[score] from OPENJSON(@jsonRowData, 'lax $.items') with ([score] nvarchar(50)) xx where xx.[score] not in (select t.[score] From @Scores t)

    insert into @Items(UserId,TypeId,ScoreId)
    SELECT u.ID [UserID],t.ID [TypeID],s.ID [ScoreID]  from OPENJSON(@jsonRowData, 'lax $.items') with ([type] int,[score] nvarchar(50)) xx
    inner join @Users u on u.username=@username
    inner join @Types t on t.[type]=xx.[type]
    inner join @Scores s on s.score=xx.score

    set @index=@index+1
end

select * from @Users
select * from @Types
select * from @Scores
select * from @Items

就是这样,希望这会有所帮助。

对于我提供的数据,我得到以下结果: -

用户表: -

ID  username
==  ========
1   George84
2   George85
3   George86
4   George87

类型表: -

ID  type
==  =====
1   8625
2   8418
3   7818
4   23
5   232
6   8626

分数表: -

ID  score
==  ======
1   90
2   84
3   90
4   q
5   80
6   70
7   60

物品表: -

ID      UserId  TypeId  ScoreId
==      ====== =======  ========
1       1       1       1
2       1       1       3
3       1       2       2
4       1       3       1
5       1       3       3
6       1       4       4
7       2       1       5
8       2       2       1
9       2       2       3
10      2       3       6
11      2       5       4
12      3       6       5
13      3       2       6
14      3       3       1
15      3       3       3
16      3       4       4
17      4       1       1
18      4       1       3
19      4       2       6
20      4       3       7
21      4       4       4
© www.soinside.com 2019 - 2024. All rights reserved.