2017-09-20 2 views
0

Ich suche nach der besten Lösung, um eine geschachtelte JSON-Zeichenfolge direkt aus (T) SQL mit einer dynamischen SQL-Abfrage zu erstellen.Verschachtelte JSON mit dynamischer Abfrage

In SQL Server 2016 ist es einfach, einen flachen JSON-String mit einer Anweisung wie folgt zu erstellen:

SELECT * 
FROM tblTableName 
FOR JSON AUTO 

Wenn Sie ein komplexere verschachteltes Ergebnis müssen Sie eine einfache rekursive Routine wie verwenden können:

CREATE FUNCTION [dbo].[NestedJSON](@Id uniqueidentifier) 
RETURNS NVARCHAR(MAX) 
AS 
BEGIN 
    DECLARE @Json NVARCHAR(MAX) = '{}'; 

    IF @Id Is NULL 
     SET @Json = 
      (SELECT *, JSON_QUERY(dbo.NestedJSON(Id)) AS child 
      FROM dbo.tblTableName 
      WHERE IDParent is NULL 
      FOR JSON AUTO); 
    ELSE 
     SET @Json = 
      (SELECT *, JSON_QUERY(dbo.NestedJSON(Id)) AS child 
      FROM dbo.tblTableName 
      WHERE IDParent = @Id 
      FOR JSON AUTO); 
    RETURN @Json 
END 

Id is the ID of each record in tblTableName

IDParent is the parent Id of the record in tblTableName

Diese rekursive Funktion funktioniert nur, wenn die SQL-Abfrage festgelegt ist.

In meiner Situation habe ich viele Abfragen mit einer verschachtelten Struktur. Um all die vielen verschachtelten SQL-Abfragen zu unterstützen, habe ich versucht, die obige NestedJSON-Funktion zu modifizieren, aber es ist Nähte, dass es nicht erlaubt ist, dynamisches SQL in einer Funktion zu verwenden. Ich habe versucht, Optionen wie:

IF @Id Is NULL 
     Set @SQL = 'SELECT @Json=(SELECT ' + @FieldList + ' ,JSON_QUERY(dbo.MenuNested(' + @Id + ')) AS Child FROM ' + @TheTables + ' WHERE IDParent is NULL FOR JSON AUTO)' 
    ELSE 
     Set @SQL = 'SELECT @Json=(SELECT ' + @FieldList + ' ,JSON_QUERY(dbo.MenuNested(' + @Id + ')) AS Child FROM ' + @TheTables + ' WHERE IDParent = ' + @Id + ' FOR JSON AUTO)' 

    Exec(@SQL) 
    --or 
    execute sp_executesql @SQL; 

Aber alle Modifikationen führten zu den gleichen Fehler: „Nur Funktionen und einige erweiterte gespeicherte Prozeduren können innerhalb einer Funktion ausgeführt werden.“

Ich rufe den SQL-Server von vb.net, so konnte ich eine zusätzliche Funktion zu Tree-a-fy der verschachtelten JSON erstellen, aber das ist die letzte Option für mich. Ich denke, die schnellste und sauberste Lösung ist die vollständige Verschachtelung in (T) SQL.

Also, gibt es jemanden, der mir helfen kann, eine Lösung zu erstellen, die Dynamic SQL unterstützt und einen verschachtelten JSON zurückgibt?

Danke jede Hilfe wird geschätzt.

Arno

+0

Sie können gespeicherte Prozedur anstelle der Funktion – sepupic

+0

verwenden Danke für den Rat. Ich habe noch nicht an einen Remote-Prozess gedacht. Ist eine Remote-Prozedur so schnell wie eine Funktion? – Arno

+0

Wenn Ihre SP die gleichen Dinge tun, wird es die gleiche Ausführungszeit haben. Der Unterschied ist, wie Sie das Ergebnis erhalten: funcion Ergebnis kann direkt in Abfragen verwendet werden, sp Ergebnis sollte gespeichert werden, bevor Sie es verwenden können – sepupic

Antwort

0

Ich endlich das Problem gelöst.Für diejenigen, die hier interessiert sind, ist die Lösung:

Erster Schritt ist einen neuen Tabellentyp zu schaffen, die in der rekursiven Routine verwendet werden können:

CREATE TYPE [dbo].[JSONCTETableType] AS TABLE(
    [Sequence] [uniqueidentifier] NULL, 
    [ParentSequence] [uniqueidentifier] NULL, 
    [JSON] [nvarchar](max) NULL, 
    [IndentLevel] [int] NULL, 
    [WBS] [nvarchar](512) NULL, 
    [ChildCount] [int] NULL 
) 

Im zweiten Schritt ich die ursprüngliche rekursive Funktion geändert in diese Tabelle Definition:

-- ============================================= 
-- Author:  Arno Voerman 
-- Create date: 2017, September 
-- ============================================= 
CREATE FUNCTION [dbo].[TREEIFY] 
(
    @ParentId uniqueidentifier, @JSONTABLE JSONCTETableType READONLY 
) 
RETURNS NVARCHAR(MAX) 
AS 
BEGIN 
    DECLARE @Json NVARCHAR(MAX); 

    If @ParentId Is NULL 
     Begin 
      SET @Json = (
       Select JSON + 
        Case When ChildCount > 0 
         THEN ',"children":[' + dbo.treeIfy(Sequence,@JSONTABLE) + ']}' 
         ELSE ',"leaf":true}' 
        END + ',' AS 'data()' 
       FROM @JSONTABLE 
       Where ParentSequence Is Null 
       FOR XML PATH('') 
      ) 
     END 
    Else 
     Begin 
      SET @Json = (
       Select JSON + 
        Case When ChildCount > 0 
         THEN ',"children":[' + dbo.treeIfy(Sequence,@JSONTABLE) + ']}' 
         ELSE ',"leaf":true}' 
        END + ',' AS 'data()' 
       FROM @JSONTABLE 
       Where ParentSequence = @ParentId 
       FOR XML PATH('') 
      ) 
     End 
    RETURN @Json 
END 

Um das verschachtelte JSON-Ergebnis aus der Abfrage des Abfrage-Ergebnis zu erhalten muss in JSONCTETableType umgewandelt werden. Dies wird in der folgenden CTE durchgeführt, die auch die JSON und zusätzliche Daten erzeugt, die mein Programm benötigt:

DECLARE @JSONTT as JSONCTETableType; 
WITH JSONCTETable AS (
    SELECT Sequence, IDParentSequence As ParentSequence, 
      (SELECT * FROM YourTable Where Sequence=anchor.Sequence FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER) As JSON, 
      0 as IndentLevel, 
      Cast(ROW_NUMBER() OVER(ORDER BY mnu_Order ASC) As NVARCHAR(512)) AS WBS 
    FROM YourTable AS anchor 
    WHERE IDParentSequence is null 
UNION ALL ------------------------------------------------------------------- 

SELECT recur.Sequence, recur.IDParentSequence As ParentSequence, 
     (SELECT * FROM YourTable Where Sequence=recur.Sequence FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER) As JSON, 
     cte.IndentLevel + 1, 
     Cast(cte.WBS + '.' + Cast(ROW_NUMBER() OVER(ORDER BY mnu_Order ASC) As NVARCHAR(10)) AS NVARCHAR(512)) AS WBS 
FROM YourTable AS recur 
     INNER JOIN JSONCTETable AS cte ON cte.Sequence = recur.IDParentSequence 

)INSERT INTO @JSONTT (Sequence, ParentSequence, JSON, IndentLevel, WBS, ChildCount) 
SELECT *, (Select count(*) From JSONCTETable tmpCTE Where tmpCTE.ParentSequence=JSONCTETable.Sequence) as ChildCount 
FROM JSONCTETable 
ORDER BY WBS; 

--Remove last character ("}") of all JSON. Needed to simplify the add "CHILD" 
Update @JSONTT SET JSON = stuff(JSON,len(JSON),1,''); 

Der letzte Schritt ist einfach:

Select '[' + dbo.treeIfy(Null,@JSONTT) + ']' as data, (Select count(Sequence) From @JSONTT) as totRows; 

Viel Glück!

Arno

0

Danke sepupic für die Antwort.

transformiert ich die Funktion aus dem ersten Post in eine gespeicherte Prozedur, aber ich bin stuggeling diesen Teil bei der Umwandlung:

SET @Json = 
    (SELECT *, JSON_QUERY(dbo.NestedJSON(Id)) AS child 
    FROM dbo.tblTableName 
    WHERE IDParent is NULL 
    FOR JSON AUTO); 

Eine gespeicherte Prozedur kann nicht innerhalb der Abfrage-Definition selbst verwendet werden, da es nicht der Fall ist geben Sie den Rückgabewert auf die gleiche Weise zurück. Da es in der Abfrage selbst nicht verwendet werden kann, kann ich die Parameter Id (dbo.NestedJSON (Id)) nicht übergeben.

Die einzige Lösung, die ich mir vorstellen kann, besteht darin, alle Abfrageergebnisse zu wiederholen und jede ID einzeln zu erhalten und dann auszuführen.

Irgendwelche anderen Vorschläge?

Arno

Verwandte Themen