在JSON列上创建外键约束

在JSON列上创建外键约束,json,sql-server,tsql,Json,Sql Server,Tsql,SQL Server中的JSON支持是非常新的。有关更多信息,请参阅 我们想用它来存储我们的可翻译字段。这意味着在数据库中,我们不再有一个单独的带有翻译的表,而是将它们存储在列本身中 我们的列值如下所示: "Name" : { "en-US" : "Conditioning", "nl-NL" : "Behandeling", "fr-FR" : "Traitement", "de-DE" : "Konditionierung" } 因为我们仍然有一个包含所有不

SQL Server中的JSON支持是非常新的。有关更多信息,请参阅

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

我们的列值如下所示:

"Name" : {
    "en-US" : "Conditioning",
    "nl-NL" : "Behandeling",
    "fr-FR" : "Traitement",
    "de-DE" : "Konditionierung"
}
因为我们仍然有一个包含所有不同区域性的表,所以我仍然希望在Name.key和区域性表之间应用外键关系

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

我尝试了下面的约束进行测试,但这不起作用。
JSON_值
似乎对JSON键不起作用。而
JSON\u QUERY
返回的不仅仅是键

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

您可以定义一个标量函数来检查单个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语句:

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" }}')
第一个将成功,因为所有名称都正确:

但第二个将失败,因为第一个名称(“en-en”)无效。以下是错误:


因此,对原始问题的严格回答是否定的。事实上,SQL供应商对该功能缺乏支持是一致的,也是可以理解的。作为一种解决方法,正如Andrea所建议的,您可以使用检查约束,但它不是FK约束,因为在某种程度上,它对关系的父端没有帮助:如果存在引用,我不能让父键的删除失败,除非我在被引用端上设置了类似的检查约束,即使这样,我也肯定不能用cascade选项删除。