簡體   English   中英

從Select中構建JSON,但在SQL Server 2016中沒有列名

[英]Build JSON out of Select but without column names in SQL Server 2016

我今天早些時候發布的這個問題涉及到一個JSON數組到表的轉換,是很幸運地找到了后進一步搜索解決方案。

現在,經過比上次更多的搜索,我仍然陷入困境(盡管我在該論壇中看到了一些條目,但是它們並不能專門解決我的問題)。

在某些情況下,我需要以選擇的結果來響應請求,該選擇的結果具有可變數量的記錄(可以是數千條),每條記錄大約有20列。

現在,我發現從選擇中構建JSON的方式(通過添加FOR JSON AUTO )非常好,並且確實創建了一個記錄數組,每個記錄的所有列均以列名開頭。

但是,這會使結果超出所需的幾倍(我正在考慮網絡流量,尤其是當它不在LAN上時)。

為了克服這個問題,我將響應分為標題和正文兩部分,其中標題包含響應中列名的列表(按正確的順序),而正文為每條記錄包含值列表(匹配標題的編號和順序)。

例:

如果源表如下所示:

  A     |      B      |         C
--------+-------------+--------------------
 11     |     22      |   2018-04-07 12:44
 33     |     44      |   2017-02-21 18:55
 55     |     66      |   2016-11-12 00:03

並且響應的正文應包含表中列“ A”和“ B”的值,響應將如下所示:

{"Response": {
               "Header":["A","B","C"],
                "Body":[["11","22","2018-04-07 12:44"],
                        ["33","44","2017-02-21 18:55"],
                        ["55","66","2016-11-12 00:03"]
                       ]
             }
}

不幸的是,我沒有找到一種方式來獲得的內容Body 沒有 "A""B""C"的名字。

更新

我想強調一個事實,表記錄中的不同列可能具有不同的類型,因此我將它們全部轉換為字符串。 請參閱更新的示例表和預期結果。

正如@ Jeroen-Mostert所指出的那樣,這在過程代碼中非常簡單。 您甚至可以讓SQL Server使用SQL CLR來做到這一點。 因為這種形狀對於FOR JSON查詢而言並不自然,所以基於CLR的解決方案可能比TSQL更好。

下面將結果連接成一個字符串,但是您可以更改該結果以將結果流式傳輸到多行中(就像FOR JSON查詢一樣),或者添加GZip壓縮。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text;
using Microsoft.SqlServer.Server;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void RunQuery (string sql)
    {
        /*
         {"Response":{"Header":["A","B","C"],
                "Body":[["11","22","2018-04-07 12:44"],
                        ["33","44","2017-02-21 18:55"],
                        ["55","66","2016-11-12 00:03"]
                       ]
             }
         }
         * */
        using (var con = new SqlConnection("Context Connection=true"))
        {
            var sb = new StringBuilder();
            con.Open();
            var cmd = con.CreateCommand();
            cmd.CommandText = sql;
            using (var rdr = cmd.ExecuteReader())
            {
                sb.Append("{\"Response\":{\"Header\":[");
                for (int i = 0; i < rdr.FieldCount; i++)
                {
                    var fn = rdr.GetName(i);
                    sb.Append('"').Append(fn).Append('"');
                    if (i + 1 < rdr.FieldCount)
                        sb.Append(',');
                }
                sb.Append("],\"Body\":[");

                //SqlContext.Pipe.Send(sb.ToString());

                if (rdr.Read())
                {
                    while (true)
                    {

                        sb.Append('[');
                        for (int i = 0; i < rdr.FieldCount; i++)
                        {
                            var val = rdr[i].ToString();
                            sb.Append('"').Append(val).Append('"');
                            if (i + 1 < rdr.FieldCount)
                                sb.Append(',');
                        }
                        sb.Append(']');
                        if (rdr.Read())
                        {
                            sb.Append(',');
                        }
                        else
                        {
                            break;
                        }

                    }
                }
                sb.Append("]}}");

                var md = new SqlMetaData("JSON", SqlDbType.NVarChar,-1);


                var r = new SqlDataRecord(md);
                r.SetString(0, sb.ToString());
                SqlContext.Pipe.SendResultsStart(r);
                SqlContext.Pipe.SendResultsRow(r);
                SqlContext.Pipe.SendResultsEnd();

            }
        }
    }
}

但是,這會使結果超出所需的幾倍(我正在考慮網絡流量,尤其是當它不在LAN上時)。

鑒於:

  1. 您的目標是減少網絡流量,並且
  2. 您正在使用Servlet處理數據庫通信
  3. 涉及多種數據類型

你可以試試...

  1. 只是使用JSON將結果集返回給servlet。 將結果集轉換為JSON會占用大量資源,需要額外的CPU,RAM和時間。 而且,盡管JSON是比XML更有效的文本格式,但它並不一定比堅持本機TDS格式更有效。 考慮到0到3位的整數將更有效,因為文本,4位將同樣有效,而5位或更多位的效率將更低。 因此,對於INT字段,哪種格式更有效取決於實際結果。 另一方面, DATETIME字段本機為8個字節,但是當序列化為JSON的文本時,它將變為23個字節。 當然,這假設您要將JSON轉換為VARCHAR 如果由於結果中包含Unicode字符串而導致無法轉換為VARCHAR以將JSON返回到Servlet,則這些字符串日期各占46個字節(SQL Server僅對Unicode使用UTF-16,因此沒有UTF-8選項)。 並且這也改變了INT值之間的差異,以使得僅0或1位數字的值更有效,因為文本,2位數字等效,而3位或更多位數字效率更低。

    總的說來:您需要徹底測試這兩種方法,因為我(顯然還有其他人)懷疑您充其量只能得到同樣有效的運輸,但會付出更多麻煩的代價代碼, 需要更多的系統資源。 因此為凈負數。 但是,除了為此付出代價之外,這種方法更有可能會變慢。

    因此,正如@ PanagiotisKanavos,@ JeroenMostert和@DavidBrowne在各種注釋中所建議的那樣:只需在servlet中轉換為JSON。

  2. 通過內置的COMPRESS函數(在SQL Server 2016中引入)壓縮返回的標准JSON。 通過簡單地向查詢添加單個函數,您可以將返回的JSON減少90%以上。 例如:

     DECLARE @Results VARCHAR(MAX); SET @Results = (SELECT * FROM sys.objects FOR JSON AUTO); SELECT @Results AS [JSON], DATALENGTH(@Results) AS [UncompressedBytes], DATALENGTH(COMPRESS(@Results)) AS [CompressedBytes]; 

    收益:

    [{ “名稱”: “sysrscols”, “的object_id”:3 “schema_id”:4 “parent_object_id”:0, “類型”: “S”, “type_desc”: “SYSTEM_TABLE”, “CREATE_DATE”:“2017年-08-22T19:38:02.860" , “modify_date”: “2017-08-22T19:38:02.867”, “is_ms_shipped”:真實的, “is_published”:假的, “is_schema_published”:假},...

    29521

    2594

    這極大地減少了網絡流量,而無需卷積代碼或使用專有格式。

    壓縮是使用GZip完成的,在Java中似乎很容易處理:

    Java GZIP示例–壓縮和解壓縮文件

    證明您確實可以擁有JSON並對其進行壓縮😸


PS關於您對David的回答的評論:

我想到附加一個JAVA存儲過程

不,這不是SQL Server的選項。

PPS關於不推薦使用SQLCLR的評論,即使只是“有效”,請參閱我的文章:

SQLCLR與SQL Server 2017,第8部分:是否贊成使用Python或R(sp_execute_external_script)?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM