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
3条答案
按热度按时间3hvapo4f1#
主要问题是
你打算怎么处理这些数据?和
您如何筛选/排序/加入/操作这些数据?
json(如xml)非常适合于数据交换、小型存储和一般定义的结构,但它不能参与在rdbms中运行的典型操作。在大多数情况下,最好将json数据传输到普通表中,并在需要时重新创建json。
xml/json和1.nf
规范化的第一条规则规定,永远不要在一列中存储超过一位的信息。你看到“personname”列的值像“米老鼠”吗?你指着这个哭:马上换!
xml或json呢?这些类型是否破坏了1.nf?嗯,是和不是。。。
如果一个完整的结构实际上是一个信息位,那么将它存储为一个信息位是完全可以的。您得到一个soap响应并希望存储它,因为您可能需要它作为将来的参考(但是您不会将此数据用于您自己的进程)?按原样储存!
现在想象一个复杂的结构(xml或json)代表一个人(地址,更多细节…)。现在你把它放在一列中作为
PersonInCharge
. 这不对吗?这不应该存在于正确设计的相关表中,使用外键引用而不是xml/json吗?特别是如果同一个人可能出现在许多不同的行中,那么使用xml/json方法肯定是错误的。但是现在想象一下需要存储历史数据。您希望在给定的时间内保留该人员的数据。几天后那个人告诉你一个新地址?没问题!如果你需要的话,旧地址就存在于xml/json中。。。
结论:如果你存储数据只是为了保存它,那没关系。如果这些数据是唯一的,那没关系。。。
但如果你需要定期的内部零件,或者这意味着冗余的重复存储,这是不好的。。。
物理存储
以下是针对sql server的,在其他rdbms上可能有所不同。
xml不是以您看到的文本的形式存储的,而是以层次结构树的形式存储的。这是惊人的好表现!此结构未在字符串级别进行分析!
sql server(2016+)中的json存在于字符串中,必须对其进行解析。没有真正的原生json类型(就像有原生xml类型一样)。这可能会在以后出现,但目前我假设json在sql server上的性能不如xml(请参阅更新2部分)。任何从json中读取值的需要都需要大量的隐藏字符串方法调用。。。
这对你意味着什么?
你可爱的db艺术家:-d知道,按原样存储json是违反rdbms的一般原则的。他知道,
json很可能会破坏1.nf
json可能会随时间变化(相同的列,不同的内容)。
json不容易阅读,很难对其进行过滤/搜索/连接或排序。
这样的操作会将相当多的额外负载转移到可怜的小db服务器上
有一些解决方法(取决于您正在使用的rdbms),但大多数都不是您希望的工作方式。。。
简而言之,你问题的答案是什么
对
如果您不想使用存储在json中的数据进行昂贵的操作(filter/join/sort)。
您可以像存储任何其他仅存在的内容一样存储此内容。我们将许多图片存储为blob,但我们不会尝试用一朵花过滤所有图片。。。
如果你一点也不在意里面的东西(只要把它存储起来,然后把它当作一点信息读出来就行了)
如果结构是可变的,这将使创建物理表比处理json数据更困难。
如果结构嵌套得很深,那么物理表中的存储开销会很大
不
如果您想像使用关系表的数据一样使用内部数据(过滤器、索引、联接…)
如果要存储重复项(创建冗余)
一般来说:如果您面临性能问题(在许多典型的场景中,您肯定会遇到这些问题!)
您可以从字符串列或blob中的json开始,并在需要时将其更改为物理表。我的魔法水晶球告诉我,这可能是明天
更新
请在此处查找有关性能和光盘空间的一些想法:https://stackoverflow.com/a/47408528/5089204
更新2:有关性能的更多信息。。。
下面介绍sql server 2016中的json和xml支持
user@mike123指出,微软官方博客上的一篇文章似乎在实验中证明,在sqlserver中查询json比查询xml快10倍。
对此有一些想法:
与“实验”进行交叉核对:
这个“实验”衡量了很多,但不是xml与json的性能。重复对同一个(不变的)字符串执行相同的操作是不现实的
测试的例子对于一个一般的陈述来说是非常简单的!
读取的值总是相同的,甚至没有使用。优化器会看到这个。。。
一句话也没说
XQuery
支持!在数组中查找具有给定id的产品?json需要读取全部内容,然后使用一个过滤器WHERE
,而XML
将允许内部XQuery predicate
. 更不用说FLWOR
...我系统上的“实验”代码显示:json似乎快了3倍(但不是10倍)。
添加
/text()
到XPath
减少到2倍以下。在相关文章中,用户“mister magoo”已经指出了这一点,但点击诱饵标题仍然没有改变。。。对于“实验”中给出的这种简单的json,最快的纯t-sql方法是
SUBSTRING
以及CHARINDEX
:-d个下面的代码将展示一个更真实的实验
使用json和相同的xml与多个
Product
(json数组与同级节点)json和xml略有变化(10000个连续数字)并插入到表中。
两个表都有一个初始调用,以避免第一次调用的偏差
读取所有10000个条目,并将检索到的值插入到另一个表中。
使用
GO 10
将在该块中运行十次以避免第一次调用的偏差最后的结果清楚地表明,json比xml慢(不是很多,在一个仍然非常简单的示例中大约是1.5倍)。
最后声明:
对于一个过度简化的例子,在不适当的情况下json可以比xml更快
处理json是纯字符串操作,而xml是解析和转换的。这是相当昂贵的第一次行动,但会加快一切,一旦这样做。
json在一次性操作中可能更好(避免了创建xml的内部层次表示的开销)
通过一个仍然非常简单但更现实的示例,xml在简单的阅读中会更快
每当需要从数组中读取特定元素、筛选数组中包含给定productid的所有条目或在路径中上下导航时,json都无法支持。它必须完全从一个字符串中解析出来-每次你都必须抓住它。。。
测试代码
结果(acer aspire v17 nitro intel i7 8gb ram上的sql server 2016 express)
zdwk9cvp2#
我要挥动我的魔杖。噗!使用json的黄金法则:
如果mysql不需要查看json内部,而应用程序只需要一组内容,那么json就可以了,甚至可能更好。
如果您要搜索内部的数据,并且您有mariadb 10.0.1或mysql 5.7(带有json数据类型和函数),那么json可能很实用。mariadb 5.3的“动态”列是这方面的一个变体。
如果你正在做“实体属性值”的事情,那么json是不好的,但它是几个弊病中最小的一个。http://mysql.rjweb.org/doc.php/eav
对于按索引列进行搜索,不将值隐藏在json中是一大优点。
用于按索引列上的范围或
FULLTEXT
搜索或SPATIAL
,无法使用json。为了
WHERE a=1 AND b=2
“综合”指数INDEX(a,b)
是伟大的;可能无法接近json。json可以很好地处理“稀疏”数据;索引工作,但不一样,与这样的(对于许多行,我指的是“缺少”或null的值。)
json可以为您提供“数组”和“树”,而无需使用额外的表。但是,只在应用程序中挖掘这些数组/树,而不是在sql中。
json比xml更好((我的意见)
如果您不想从应用程序以外的地方进入json字符串,那么我建议(在客户端)将其压缩到
BLOB
. 把它想象成一个.jpg——里面有东西,但sql不在乎。陈述你的申请
am46iovg3#
这太长了,不能发表评论。
如果它是“绝对错误的”,那么大多数数据库将不支持它。好吧,大多数数据库都支持逗号
FROM
我认为这是“绝对错误的”。但是对json的支持是新的发展,而不是向后兼容的“特性”。一个明显的例子是,json结构只是一个传递回应用程序的blob。那么就没有争论了——除了存储json的开销之外,这对于每个记录中都有公共字段的结构化数据来说是不必要的冗长。
另一种情况是“稀疏”列情况。您的行中有许多可能的列,但每行的列数各不相同。
另一种情况是要在记录中存储“嵌套”记录。json功能强大。
如果json在要查询的记录中有公共字段,那么通常最好将这些字段放在适当的数据库列中。然而,数据是复杂的,对于json这样的格式是有位置的。