什么时候可以将json或xml数据保存在sql表中

y4ekin9u  于 2021-06-23  发布在  Mysql
关注(0)|答案(3)|浏览(202)

使用时 SQL 或者 MySQL (或者任何关系数据库)-我知道为了索引和其他目的,将数据保存在正则列中会更好。。。
东西正在加载和保存 JSON 数据有时要简单得多,并且使开发更容易。
保存生肉有什么“黄金法则”吗 JSON 数据库中的数据?
这样做绝对是不好的做法吗?

3hvapo4f

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都无法支持。它必须完全从一个字符串中解析出来-每次你都必须抓住它。。。
测试代码

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
zdwk9cvp

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不在乎。
陈述你的申请

am46iovg

am46iovg3#

这太长了,不能发表评论。
如果它是“绝对错误的”,那么大多数数据库将不支持它。好吧,大多数数据库都支持逗号 FROM 我认为这是“绝对错误的”。但是对json的支持是新的发展,而不是向后兼容的“特性”。
一个明显的例子是,json结构只是一个传递回应用程序的blob。那么就没有争论了——除了存储json的开销之外,这对于每个记录中都有公共字段的结构化数据来说是不必要的冗长。
另一种情况是“稀疏”列情况。您的行中有许多可能的列,但每行的列数各不相同。
另一种情况是要在记录中存储“嵌套”记录。json功能强大。
如果json在要查询的记录中有公共字段,那么通常最好将这些字段放在适当的数据库列中。然而,数据是复杂的,对于json这样的格式是有位置的。

相关问题