在JSON列上创建外键约束

时间:2018-08-24 04:51:58

标签: json sql-server tsql

SQL Server中的JSON支持是相当新的。有关更多信息,请参见here

我们想用它来存储我们的可翻译字段。这意味着在数据库中,我们将不再拥有带有翻译的单独表,而是将它们存储在列本身中。

我们的列值如下所示:

"Name" : {
    "en-US" : "Conditioning",
    "nl-NL" : "Behandeling",
    "fr-FR" : "Traitement",
    "de-DE" : "Konditionierung"
}

由于我们仍然有一个包含所有不同区域性的表,所以我仍然想在Name.Key和Culture表之间应用外键关系。

我找不到有关如何将外键约束应用于JSON中的数据的任何信息。有人知道怎么做吗?

我已经尝试了下面的约束进行测试,但这不起作用。 JSON_VALUE似乎不适用于JSON密钥。而JSON_QUERY返回的不仅仅是键。

ADD CONSTRAINT
    [FK_ItemCulture]
CHECK (JSON_VALUE([Name], '$.Name') IN ('en-US', 'nl-NL', 'fr-FR', 'de-DE'))

1 个答案:

答案 0 :(得分:1)

您可以定义将检查单个json的标量函数:

CREATE FUNCTION [dbo].[svf_CheckJsonNames](@json nvarchar(max))
RETURNS bit AS
BEGIN
    declare @ok bit
    declare @validNames table([name] nvarchar(50))

    insert into @validNames values ('en-US'),('nl-NL'),('fr-FR'),('de-DE')

    if exists (
            select x.[key] , vn.name  from OPENJSON (@json)  
            with (
                [Name] nvarchar(max) as json
            ) t
            cross apply 
            (
            select [key] from openjson(t.[Name])
            ) x
            left join  @validNames vn
            on x.[key] COLLATE DATABASE_DEFAULT = vn.[name] COLLATE DATABASE_DEFAULT
            where vn.[name] is null
        )
        set @ok = 0
    else set @ok = 1

    RETURN @ok
END

如果所有名称均有效,则该函数返回1,如果一个或多个名称无效,则返回0。

现在您可以在约束中使用此功能:

create table tmp(myId int, [Name] nvarchar(max))

alter table tmp ADD CONSTRAINT [FK_ItemCulture]
    CHECK ([dbo].[svf_CheckJsonNames]([Name]) = 1)  

如果运行以下插入语句:

insert into tmp values(1, '{"Name" : { "en-US" : "Conditioning", "nl-NL" : "Behandeling", "fr-FR" : "Traitement", "de-DE" : "Konditionierung" }}')
insert into tmp values(2, '{"Name" : { "en-EN" : "Conditioning", "nl-NL" : "Behandeling", "fr-FR" : "Traitement", "de-DE" : "Konditionierung" }}')

第一个将成功,因为所有名称均正确:

enter image description here

,但是第二个将失败,因为名字(“ en-EN”)无效。这是错误:

enter image description here