[英]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上時)。
鑒於:
你可以試試...
只是不使用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。
通過內置的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中似乎很容易處理:
證明您確實可以擁有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.