Bei Verwendung von SQL
oder MySQL
(oder einer anderen relationalen Datenbank) - Ich verstehe, dass das Speichern der Daten in regulären Spalten für die Indizierung und andere Zwecke besser ist ...
Das Laden und Speichern von JSON
Daten ist manchmal viel einfacher - und erleichtert die Entwicklung.
Gibt es "goldene Regeln" zum Speichern von Rohdaten JSON
in der DB?
Ist es absolut eine schlechte Praxis, dies zu tun?
sql
json
xml
normalization
structured-data
animuson
quelle
quelle
Antworten:
Die Hauptfragen sind
JSON (wie XML) eignet sich hervorragend für den Datenaustausch, kleinen Speicher und allgemein definierte Strukturen, kann jedoch nicht an typischen Aktionen teilnehmen, die Sie in Ihrem RDBMS ausführen. In den meisten Fällen ist es besser, Ihre JSON-Daten in normale Tabellen zu übertragen und den JSON bei Bedarf neu zu erstellen.
XML / JSON und 1.NF
Die erste Regel der Normalisierung schreibt vor, niemals mehr als ein Informationsbit in einer Spalte zu speichern. Sie sehen eine Spalte "PersonName" mit einem Wert wie "Mickey Mouse"? Sie zeigen darauf und rufen: Ändern Sie das sofort!
Was ist mit XML oder JSON? Brechen diese Typen 1.NF? Nun ja und nein ...
Es ist vollkommen in Ordnung, eine vollständige Struktur als ein Informationsbit zu speichern, wenn es sich tatsächlich um ein Informationsbit handelt . Sie erhalten eine SOAP-Antwort und möchten diese speichern, da Sie diese möglicherweise zum späteren Nachschlagen benötigen (Sie werden diese Daten jedoch nicht für Ihre eigenen Prozesse verwenden ). Bewahren Sie es einfach so auf, wie es ist !
Stellen Sie sich nun eine komplexe Struktur (XML oder JSON) vor, die eine Person darstellt (mit ihrer Adresse, weiteren Details ...). Jetzt setzen Sie dies in eine Spalte als
PersonInCharge
. Ist das falsch? Sollte dies nicht eher in ordnungsgemäß gestalteten verwandten Tabellen mit einer Fremdschlüsselreferenz anstelle von XML / JSON erfolgen? Insbesondere wenn dieselbe Person in vielen verschiedenen Zeilen vorkommt, ist es definitiv falsch, einen XML / JSON-Ansatz zu verwenden.Stellen Sie sich nun die Notwendigkeit vor, historische Daten zu speichern. Sie wollen beharren die Person , die Daten für einen bestimmten Zeitpunkt. Einige Tage später teilt Ihnen die Person eine neue Adresse mit? Kein Problem! Die alte Adresse befindet sich in einem XML / JSON, falls Sie sie jemals benötigen ...
Fazit: Wenn Sie die Daten nur speichern, um sie zu behalten, ist das in Ordnung. Wenn diese Daten ein eindeutiger Teil sind, ist es in Ordnung ...
Aber wenn Sie die internen Teile regelmäßig benötigen oder wenn dies redundanten doppelten Speicher bedeuten würde, ist es nicht in Ordnung ...
Physische Speicherung
Das Folgende gilt für SQL Server und kann bei anderen RDBMs unterschiedlich sein.
XML wird nicht als angezeigter Text gespeichert, sondern als Hierarchiebaum. Dies abzufragen ist erstaunlich gut! Diese Struktur wird nicht auf String-Ebene analysiert!
JSON in SQL Server (2016+) lebt in einer Zeichenfolge und muss analysiert werden. Es gibt keinen echten nativen JSON-Typ (wie es einen nativen XML-Typ gibt). Dies könnte später kommen, aber im Moment würde ich davon ausgehen, dass JSON unter SQL Server nicht so leistungsfähig ist wie XML (siehe Abschnitt UPDATE 2 ). Jede Notwendigkeit, einen Wert aus JSON zu lesen, erfordert eine Menge versteckter String-Methodenaufrufe ...
Was bedeutet das für dich?
Ihr liebenswerter DB-Künstler :-D weiß, dass das Speichern von JSON so wie es ist gegen die allgemeinen Prinzipien von RDBMs verstößt. Er weiß,
Es gibt einige Problemumgehungen (abhängig vom verwendeten RDBMS), aber die meisten funktionieren nicht so, wie Sie es möchten ...
Die Antwort auf Ihre Frage kurz
JA
Sie können dies so speichern, wie jeder andere nur vorhandene Inhalt. Wir speichern viele Bilder als BLOBs, aber wir würden nicht versuchen, nach allen Bildern mit einer Blume zu filtern ...
NEIN
Sie können mit dem JSON in einer Zeichenfolgenspalte oder als BLOB beginnen und dies bei Bedarf in physische Tabellen ändern. Meine magische Kristallkugel sagt mir, das könnte morgen sein :-D
AKTUALISIEREN
Einige Ideen zur Leistung und zum Speicherplatz finden Sie hier: https://stackoverflow.com/a/47408528/5089204
UPDATE 2: Mehr zur Leistung ...
Im Folgenden wird die JSON- und XML-Unterstützung in SQL Server 2016 behandelt
User @ mike123 verwies auf einen Artikel in einem offiziellen Microsoft-Blog, der in einem Experiment zu beweisen scheint, dass das Abfragen eines JSON 10-mal schneller ist als das Abfragen eines XML in SQL-Server.
Einige Gedanken dazu:
Einige Gegenprüfungen mit dem "Experiment":
XQuery
Unterstützung! Suchen Sie ein Produkt mit einer bestimmten ID in einem Array? JSON muss das gesamte Los lesen und anschließend einen Filter verwendenWHERE
, währendXML
ein interner zulässig istXQuery predicate
. Nicht zu sprechenFLWOR
.../text()
zuXPath
wird dies auf weniger als 2x reduziert . In dem verwandten Artikel hat Benutzer "Mister Magoo" bereits darauf hingewiesen, aber der Titel des Klick-Köders bleibt unverändert ...SUBSTRING
undCHARINDEX
:-DDer folgende Code zeigt ein realistischeres Experiment
Product
(JSON-Array vs. Geschwisterknoten)GO 10
wird dieser Block zehnmal durchlaufen, um eine Verzerrung beim ersten Anruf zu vermeidenDas Endergebnis zeigt deutlich, dass JSON langsamer als XML ist (nicht so viel, etwa 1,5x in einem noch sehr einfachen Beispiel).
Die abschließende Aussage:
Der Testcode
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
Das Ergebnis (SQL Server 2016 Express auf einem Acer Aspire v17 Nitro Intel i7, 8 GB RAM)
Test SumTime ------------------ json 2706 xml 1604
quelle
ProductID=1234
existiert? Haben Sie versucht, den JSON zu manipulieren? Haben Sie dies mit tieferen verschachtelten Hierarchien versucht?Dies ist zu lang für einen Kommentar.
Wenn es "absolut falsch" wäre, würden die meisten Datenbanken es nicht unterstützen. Okay, die meisten Datenbanken unterstützen Kommas in der
FROM
Klausel und ich sehe das als "absolut falsch" an. Die Unterstützung für JSON ist jedoch eine Neuentwicklung und keine abwärtskompatible "Funktion".Ein offensichtlicher Fall ist, wenn die JSON-Struktur einfach ein BLOB ist, das an die Anwendung zurückgegeben wird. Dann gibt es keine Debatte - abgesehen vom Aufwand für das Speichern von JSON, der für strukturierte Daten mit gemeinsamen Feldern in jedem Datensatz unnötig ausführlich ist.
Ein anderer Fall ist der Fall "spärlicher" Spalten. Sie haben Zeilen mit vielen möglichen Spalten, die jedoch von Zeile zu Zeile variieren.
Ein anderer Fall ist, wenn Sie "verschachtelte" Datensätze in einem Datensatz speichern möchten. JSON ist mächtig.
Wenn der JSON gemeinsame Felder für Datensätze hat, für die Sie eine Abfrage durchführen möchten, sollten Sie diese normalerweise besser in die richtigen Datenbankspalten einfügen. Daten sind jedoch kompliziert und es gibt einen Platz für Formate wie JSON.
quelle
Ich werde meinen Zauberstab schwingen. Poof! Goldene Regeln für die Verwendung von JSON:
Wenn MySQL nicht suchen müssen innerhalb der JSON, und die Anwendung muss einfach eine Sammlung von Sachen, dann ist JSON gut, vielleicht sogar besser.
Wenn Sie auf Daten suchen wird, die im Inneren ist und Sie haben MariaDB 10.0.1 oder MySQL 5.7 (mit JSON - Datentyp und Funktionen), dann JSON könnte praktisch sein. Die "dynamischen" Spalten von MariaDB 5.3 sind eine Variante davon.
Wenn Sie "Entity-Attribute-Value" machen, dann ist JSON nicht gut, aber es ist das geringste von mehreren Übeln. http://mysql.rjweb.org/doc.php/eav
Für die Suche nach einer indizierten Spalte ist es ein großes Plus, wenn der Wert nicht in JSON vergraben ist.
Für die Suche nach einem Bereich in einer indizierten Spalte oder einer
FULLTEXT
Suche oderSPATIAL
ist JSON nicht möglich.Denn
WHERE a=1 AND b=2
der "zusammengesetzte" IndexINDEX(a,b)
ist großartig; kann JSON wahrscheinlich nicht nahe kommen.JSON funktioniert gut mit "spärlichen" Daten; INDEXing funktioniert damit aber nicht so gut. (Ich beziehe mich auf Werte, die für viele Zeilen 'fehlen' oder NULL sind.)
JSON kann Ihnen "Arrays" und "Bäume" geben, ohne auf zusätzliche Tabellen zurückgreifen zu müssen. Aber graben Sie in solche Arrays / Bäume nur in der App, nicht in SQL.
JSON ist um Welten besser als XML. (Meine Meinung)
Wenn Sie nur über die App in die JSON-Zeichenfolge gelangen möchten, empfehle ich, sie (im Client) zu komprimieren und in einer zu speichern
BLOB
. Stellen Sie es sich wie eine JPG-Datei vor - da sind Dinge drin, aber SQL ist das egal.Geben Sie Ihre Bewerbung an. Vielleicht können wir genauer sein.
quelle
New SQL Server bietet Funktionen zur Verarbeitung von JSON-Text. Als JSON formatierte Informationen können als Text in Standard-SQL Server-Spalten gespeichert werden, und SQL Server bietet Funktionen, mit denen Werte von diesen JSON-Objekten abgerufen werden können.
DROP TABLE IF EXISTS Person CREATE TABLE Person ( _id int identity constraint PK_JSON_ID primary key, value nvarchar(max) CONSTRAINT [Content should be formatted as JSON] CHECK ( ISJSON(value)>0 ) )
Diese einfache Struktur ähnelt der Standard-NoSQL-Sammlung, die Sie in NoSQL-Datenbanken (z. B. Azure DocumentDB oder MongoDB) erstellen können, in denen Sie nur einen Schlüssel haben, der die ID darstellt, und einen Wert, der JSON darstellt.
Beachten Sie, dass NVARCHAR nicht nur ein einfacher Text ist. SQL Server verfügt über einen integrierten Textkomprimierungsmechanismus, mit dem auf der Festplatte gespeicherte Daten transparent komprimiert werden können. Die Komprimierung hängt von der Sprache ab und kann je nach Ihren Daten bis zu 50% betragen (siehe UNICODE-Komprimierung).
Der Hauptunterschied zwischen SQL Server und anderen einfachen NoSQL-Datenbanken besteht darin, dass Sie mit SQL Server ein hybrides Datenmodell verwenden können, in dem Sie mehrere JSON-Objekte in derselben „Sammlung“ speichern und mit regulären relationalen Spalten kombinieren können.
Stellen Sie sich beispielsweise vor, wir wissen, dass jede Person in Ihrer Sammlung Vor- und Nachnamen hat und dass Sie allgemeine Informationen über die Person als ein JSON-Objekt und Telefonnummern / E-Mail-Adressen als separate Objekte speichern können. In SQL Server 2016 können wir diese Struktur ohne zusätzliche Syntax einfach erstellen:
DROP TABLE IF EXISTS Person CREATE TABLE Person ( PersonID int IDENTITY PRIMARY KEY, FirstName nvarchar(100) NOT NULL, LastName nvarchar(100) NOT NULL, AdditionalInfo nvarchar(max) NULL, PhoneNumbers nvarchar(max) NULL, EmailAddresses nvarchar(max) NULL CONSTRAINT [Email addresses must be formatted as JSON array] CHECK ( ISJSON(EmailAddresses)>0 ) )
Anstelle eines einzelnen JSON-Objekts können Sie Ihre Daten in dieser „Sammlung“ organisieren. Wenn Sie die Struktur jeder JSON-Spalte nicht explizit überprüfen möchten, müssen Sie nicht für jede Spalte eine JSON-Überprüfungsbeschränkung hinzufügen (in diesem Beispiel habe ich die CHECK-Einschränkung nur für die Spalte EmailAddresses hinzugefügt).
Wenn Sie diese Struktur mit der Standard-NoSQL-Sammlung vergleichen, werden Sie möglicherweise feststellen, dass Sie schneller auf stark typisierte Daten (Vorname und Nachname) zugreifen können. Daher ist diese Lösung eine gute Wahl für Hybridmodelle, bei denen Sie einige Informationen identifizieren können, die für alle Objekte wiederholt werden, und andere variable Informationen als JSON gespeichert werden können. Auf diese Weise können Sie Flexibilität und Leistung kombinieren.
Wenn Sie diese Struktur mit dem Schema der Personentabelle AdventureWorks vergleichen, stellen Sie möglicherweise fest, dass wir viele verwandte Tabellen entfernt haben.
Neben der Einfachheit des Schemas sind Ihre Datenzugriffsvorgänge im Vergleich zu komplexen relationalen Strukturen einfacher. Jetzt können Sie eine einzelne Tabelle lesen, anstatt mehrere Tabellen zu verbinden. Wenn Sie eine neue Person mit verwandten Informationen (E-Mail-Adressen, Telefonnummern) einfügen müssen, können Sie einen einzelnen Datensatz in eine Tabelle einfügen, anstatt einen Datensatz in die AdventureWorks Person-Tabelle einzufügen. Verwenden Sie dabei die Identitätsspalte, um den Fremdschlüssel zu finden, der zum Speichern von Telefonen verwendet wird , E-Mail-Adressen usw. Darüber hinaus können Sie in diesem Modell problemlos einzelne Personenzeilen löschen, ohne Kaskadenlöschungen mithilfe von Fremdschlüsselbeziehungen durchzuführen.
NoSQL-Datenbanken sind für einfache Operationen zum Lesen, Einfügen und Löschen optimiert. Mit SQL Server 2016 können Sie dieselbe Logik in relationalen Datenbanken anwenden.
JSON-Einschränkungen In den vorherigen Beispielen haben wir gesehen, wie einfache Einschränkungen hinzugefügt werden, die überprüfen, ob der in der Spalte gespeicherte Text ordnungsgemäß formatiert ist. Obwohl JSON kein starkes Schema hat, können Sie auch komplexe Einschränkungen hinzufügen, indem Sie Funktionen kombinieren, die Werte aus JSON lesen, und Standard-T-SQL-Funktionen:
ALTER TABLE Person ADD CONSTRAINT [Age should be number] CHECK ( ISNUMERIC(JSON_VALUE(value, '$.age'))>0 ) ALTER TABLE Person ADD CONSTRAINT [Person should have skills] CHECK ( JSON_QUERY(value, '$.skills') IS NOT NULL) First constraint will take the value of $.age property and check is this numeric value. Second constraint will try to find JSON object in $.skills property and verify that it exists. The following INSERT statements will fail due to the violation of constraints: INSERT INTO Person(value) VALUES ('{"age": "not a number", "skills":[]}') INSERT INTO Person(value) VALUES ('{"age": 35}')
Beachten Sie, dass CHECK-Einschränkungen Ihre Einfüge- / Aktualisierungsprozesse verlangsamen können, sodass Sie sie möglicherweise vermeiden, wenn Sie eine schnellere Schreibleistung benötigen.
Komprimierter JSON-Speicher Wenn Sie großen JSON-Text haben, können Sie JSON-Text mithilfe der integrierten COMPRESS-Funktion explizit komprimieren. Im folgenden Beispiel wird komprimierter JSON-Inhalt als Binärdaten gespeichert, und wir haben eine Spalte berechnet, die JSON als Originaltext mithilfe der DECOMPRESS-Funktion dekomprimiert:
CREATE TABLE Person ( _id int identity constraint PK_JSON_ID primary key, data varbinary(max), value AS CAST(DECOMPRESS(data) AS nvarchar(max)) ) INSERT INTO Person(data) VALUES (COMPRESS(@json))
Die Funktionen COMPRESS und DECOMPRESS verwenden die Standard-GZip-Komprimierung. Wenn Ihr Client die GZip-Komprimierung verarbeiten kann (z. B. ein Browser, der GZIP-Inhalte versteht), können Sie komprimierte Inhalte direkt zurückgeben. Beachten Sie, dass dies ein Kompromiss zwischen Leistung und Speicher ist. Wenn Sie häufig komprimierte Daten abfragen, ist die Leistung möglicherweise geringer, da der Text jedes Mal dekomprimiert werden muss.
Hinweis: JSON-Funktionen sind nur in SQL Server 2016+ und in der Azure SQL-Datenbank verfügbar.
Weitere Informationen finden Sie in der Quelle dieses Artikels
https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/23/storing-json-in-sql-server/
quelle
Die "goldene Regel", die ich auf eine Art von Handwelle verwende, lautet: Wenn ich JSON in seinem Rohformat benötige, ist es in Ordnung, es zu speichern. Wenn ich einen besonderen Punkt beim Parsen machen muss, dann ist es nicht.
Wenn ich beispielsweise eine API erstelle, die unformatiertes JSON sendet, und sich dieser Wert aus irgendeinem Grund nicht ändert, ist es in Ordnung , ihn als unformatiertes JSON zu speichern. Wenn ich es analysieren, ändern, aktualisieren usw. muss, dann nicht so sehr.
quelle
Die Frage, die Sie stellen müssen, lautet:
TUN
NICHT
quelle
Jsons sind nicht großartig in relationalen DBs. Wenn Sie den JSON in Spalten aufklappen und in einer Datenbank speichern, ist dies großartig, aber das Speichern eines JSON als Blob ist neben der Verwendung als Datenarchivierungssystem.
Es kann mehrere Gründe geben, ein json nicht zu entfalten und in einer einzelnen Spalte zu speichern, aber die Entscheidung wäre getroffen worden, da die Werte in diesem json-Feld nicht für Abfragen verwendet würden (oder die Werte bereits in Spalten entfaltet wurden).
Außerdem würde der größte Teil der JSON-Verarbeitung, wenn das Feld überhaupt abgefragt wurde, außerhalb der SQL-Umgebung liegen, da SQL einfach nicht für die JSON-Verarbeitung gedacht ist. Die eigentliche Frage lautet dann: Wo speichere ich diesen JSON, lasse ich ihn einfach als Flatfiles und frage ihn bei Bedarf über ein anderes System (spark / hive / etc) ab.
Ich würde Ihrem DB-Künstler zustimmen, verwenden Sie RDBMS nicht für die Archivierung. Es gibt günstigere Optionen. Außerdem können JSON-Blobs sehr groß werden und mit der Zeit den DB-Speicherplatz blockieren.
quelle
PostgreSQL hat einen eingebauten,
json
undjsonb
DatentypDies sind einige Beispiele:
CREATE TABLE orders ( ID serial NOT NULL PRIMARY KEY, info json NOT NULL ); INSERT INTO orders (info) VALUES ( '{ "customer": "Lily Bush", "items": {"product": "Diaper","qty": 24}}' ), ( '{ "customer": "Josh William", "items": {"product": "Toy Car","qty": 1}}' ), ( '{ "customer": "Mary Clark", "items": {"product": "Toy Train","qty": 2}}' );
PostgreSQL bietet zwei einheimische Betreiber
->
und->>
zur Abfrage JSON - Daten.Der Operator
->
gibt das JSON-Objektfeld per Schlüssel zurück.Der Operator
->>
gibt das JSON-Objektfeld per Text zurück.SELECT info -> 'customer' AS customer FROM orders; SELECT info ->> 'customer' AS customer FROM orders WHERE info -> 'items' ->> 'product' = 'Diaper'
quelle