当使用SQL或MySQL(或与此相关的任何关系数据库)时-我知道将数据保存在常规列中对于索引和其他目的更好。
SQL
MySQL
JSON有时,加载和保存数据要简单得多-并使开发更容易。
JSON
是否有用于JSON在数据库中保存原始数据的“黄金法则” ?
这样做绝对是不好的做法吗?
主要问题是
JSON(如XML)非常适合数据交换,小型存储和通用定义的结构,但它不能参与在RDBMS中运行的典型操作。在大多数情况下,将JSON数据传输到 普通表中 并在需要时重新创建JSON 会更好。
规范化的第一条规则规定,决不要将多于一位的信息存储到一列中。您看到带有“ Mickey Mouse”之类的值的“ PersonName”列吗?您指向此并哭了: 立即更改!
XML或JSON呢?这些类型是否会破坏1.NF?好吧,是的,不是…
如果实际上只存储 一小部分信息, 则可以将一个完整的结构存储为 一小部分信息 。您会得到一个SOAP响应并想要存储它,因为您可能需要它作为以后的参考(但您 不会将这些数据用于自己的进程 )?只需 按原样 存储它即可!
现在,想象一个 代表人 的 复杂结构(XML或JSON) (及其地址,更多详细信息…)。现在,将其 放在的一列中PersonInCharge。错了吗 难道这不应该存在于具有外键引用而不是XML / JSON的经过适当设计的相关表中吗?特别是如果同一个人可能出现在许多不同的行中,那么使用XML / JSON方法肯定是错误的。
PersonInCharge
但是现在想象一下需要存储历史数据。您要在给定的时间段内 保留 该人的数据。几天后,对方告诉您新地址?没问题!如果您需要,旧地址将保存在XML / JSON中…
结论: 如果存储数据只是为了保留它,就可以了。如果这些数据是 唯一的 部分,那没关系。 但是如果您需要定期使用 内部零件 ,或者这意味着多余的重复存储,那就不好了。
以下内容适用于SQL Server,在其他RDBM上可能有所不同。
XML不是存储为您看到的文本,而是存储为层次结构树。查询这是惊人的好表现!不会在字符串级别解析此结构! SQL Server(2016+)中的JSON存在一个字符串中,必须进行解析。没有真正的本机JSON类型(例如,有本机XML类型)。这可能会在以后出现,但是现在我假设JSON在SQL Server上的性能不如XML(请参阅 UPDATE 2 一节)。任何需要从JSON读取值的操作都将需要大量隐藏的字符串方法调用…
您 可爱的数据库艺术家 :-D知道, _ 按原样*_ 存储 _JSON *_违反RDBM的通用原则。 他知道,
有一些解决方法(取决于您所使用的RDBMS),但是大多数方法都无法按照您希望的方式工作…
是
如果您 不想使用数据,该数据存储 在 JSON中以进行昂贵的操作(过滤器/联接/排序)。 您可以像存储任何其他 仅存在的 内容一样存储它。我们将许多图片存储为BLOB,但是我们不会尝试过滤所有带有花朵的图片…
如果您完全不打扰里面的内容(只需将其存储并读取为一点信息即可)
没有
您可以在字符串列中以JSON开头或以BLOB开头,并在需要时将其更改为物理表。我的魔幻水晶球告诉我,这可能是明天:-D
以下内容解决了SQL Server 2016中的JSON和XML支持
用户@ mike123指向微软官方博客上的一篇文章,该文章似乎在实验中得到了证明,与在SQL Server中 查询XML相比 , 查询JSON的 速度快10倍 。
关于此的一些想法:
与“实验”进行一些核对:
XQuery
WHERE
XML
XQuery predicate
FLWOR
/text()
XPath
SUBSTRING
CHARINDEX
以下代码将显示更实际的实验
Product
GO 10
最终结果清楚地表明,JSON比XML慢 (不是那么多,在一个非常简单的示例中约为1.5倍)。
最后的声明:
测试代码
USE master; GO --create a clean database CREATE DATABASE TestJsonXml; GO USE TestJsonXml; GO --create tables CREATE TABLE TestTbl1(ID INT IDENTITY,SomeXml XML); CREATE TABLE TestTbl2(ID INT IDENTITY,SomeJson NVARCHAR(MAX)); CREATE TABLE Target1(SomeString NVARCHAR(MAX)); CREATE TABLE Target2(SomeString NVARCHAR(MAX)); CREATE TABLE Times(Test VARCHAR(10),Diff INT) GO --insert 10000 XMLs into TestTbl1 WITH Tally AS(SELECT TOP 10000 ROW_NUMBER() OVER(ORDER BY (SELECT NULL))*2 AS Nmbr FROM master..spt_values AS v1 CROSS APPLY master..spt_values AS v2) INSERT INTO TestTbl1(SomeXml) SELECT N'<Root> <Products> <ProductDescription> <Features> <Maintenance>' + CAST(Nmbr AS NVARCHAR(10)) + ' year parts and labor extended maintenance is available</Maintenance> <Warranty>1 year parts and labor</Warranty> </Features> <ProductID>' + CAST(Nmbr AS NVARCHAR(10)) + '</ProductID> <ProductName>Road Bike</ProductName> </ProductDescription> <ProductDescription> <Features> <Maintenance>' + CAST(Nmbr + 1 AS NVARCHAR(10)) + ' blah</Maintenance> <Warranty>1 year parts and labor</Warranty> </Features> <ProductID>' + CAST(Nmbr + 1 AS NVARCHAR(10)) + '</ProductID> <ProductName>Cross Bike</ProductName> </ProductDescription> </Products> </Root>' FROM Tally; --insert 10000 JSONs into TestTbl2 WITH Tally AS(SELECT TOP 10000 ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Nmbr FROM master..spt_values AS v1 CROSS APPLY master..spt_values AS v2) INSERT INTO TestTbl2(SomeJson) SELECT N'{ "Root": { "Products": { "ProductDescription": [ { "Features": { "Maintenance": "' + CAST(Nmbr AS NVARCHAR(10)) + ' year parts and labor extended maintenance is available", "Warranty": "1 year parts and labor" }, "ProductID": "' + CAST(Nmbr AS NVARCHAR(10)) + '", "ProductName": "Road Bike" }, { "Features": { "Maintenance": "' + CAST(Nmbr + 1 AS NVARCHAR(10)) + ' blah", "Warranty": "1 year parts and labor" }, "ProductID": "' + CAST(Nmbr + 1 AS NVARCHAR(10)) + '", "ProductName": "Cross Bike" } ] } } }' FROM Tally; GO --Do some initial action to avoid first-call-bias INSERT INTO Target1(SomeString) SELECT SomeXml.value('(/Root/Products/ProductDescription/Features/Maintenance/text())[1]', 'nvarchar(4000)') FROM TestTbl1; INSERT INTO Target2(SomeString) SELECT JSON_VALUE(SomeJson, N'$.Root.Products.ProductDescription[0].Features.Maintenance') FROM TestTbl2; GO --Start the test DECLARE @StartDt DATETIME2(7), @EndXml DATETIME2(7), @EndJson DATETIME2(7); --Read all ProductNames of the second product and insert them to Target1 SET @StartDt = SYSDATETIME(); INSERT INTO Target1(SomeString) SELECT SomeXml.value('(/Root/Products/ProductDescription/ProductName/text())[2]', 'nvarchar(4000)') FROM TestTbl1 ORDER BY NEWID(); --remember the time spent INSERT INTO Times(Test,Diff) SELECT 'xml',DATEDIFF(millisecond,@StartDt,SYSDATETIME()); --Same with JSON into Target2 SET @StartDt = SYSDATETIME(); INSERT INTO Target2(SomeString) SELECT JSON_VALUE(SomeJson, N'$.Root.Products.ProductDescription[1].ProductName') FROM TestTbl2 ORDER BY NEWID(); --remember the time spent INSERT INTO Times(Test,Diff) SELECT 'json',DATEDIFF(millisecond,@StartDt,SYSDATETIME()); GO 10 --do the block above 10 times --Show the result SELECT Test,SUM(Diff) AS SumTime, COUNT(Diff) AS CountTime FROM Times GROUP BY Test; GO --clean up USE master; GO DROP DATABASE TestJsonXml; GO
结果(Acer Aspire v17 Nitro Intel i7、8GB Ram上的SQL Server 2016 Express)
Test SumTime ------------------ json 2706 xml 1604