10

这是一个“最佳实践”问题。我们正在就这个话题进行内部讨论,并希望获得更广泛受众的意见。

我需要将我的数据存储在MS SQL Server具有普通列和行的传统表中。有时我需要将 a 返回DataTable到我的 Web 应用程序,而其他时候我需要返回一个JSON字符串。

目前,我将表格返回到中间层并将其解析为JSON字符串。这似乎在大多数情况下运行良好,但有时在大型数据集上需要一段时间(解析数据,而不是返回表)。

我正在考虑修改存储过程以选择性地返回一个DataTable或一个JSON字符串。我会简单地@isJson bit向 SP 添加一个参数。

如果用户想要字符串而不是表,SP 将执行如下查询:

DECLARE @result varchar(MAX)
SELECT @result = COALESCE(@results ',', '') + '{id:"' + colId + '",name:"' + colName + '"}'
    FROM MyTable
SELECT @result

这会产生如下内容:

{id:"1342",name:"row1"},{id:"3424",name:"row2"}

当然,用户也可以通过@isJson参数传入false来获取表。

我想明确一点,数据存储不受影响,任何现有视图和其他流程也不受影响。这只是对某些存储过程的结果的更改。

我的问题是:

  1. 有没有人在大型应用程序中尝试过这个?如果有,结果如何?
  2. 您看到/期望这种方法有哪些问题?
  3. 除了以这种方式修改存储过程或在中间层解析字符串之外,是否有更好更快的方法从 SQL Server 中的表转到 JSON?
4

2 回答 2

8

我个人认为这种字符串操作的最佳位置是在程序代码中使用具有功能并且可以编译的完全表达语言的程序代码。在 T-SQL 中这样做并不好。程序代码可以有快速的函数来进行正确的转义。

让我们稍微考虑一下:

  • 当您部署应用程序的各个部分的新版本时,该功能的最佳位置是哪里?

  • 如果您必须恢复您的数据库(及其所有存储过程),这会对任何事情产生负面影响吗?如果您正在部署新版本的 Web 前端,绑定到数据库的 JSON 转换是否会导致问题?

  • 你将如何正确地转义字符?您是否通过发送任何日期?日期字符串将采用什么格式以及如何将它们转换为另一端的实际 Date 对象(如果需要)?

  • 您将如何对其进行单元测试(并使用自动化测试!)以证明它工作正常?你将如何对其进行回归测试?

  • SQL Server UDF 可能非常慢。您是否满足于使用慢速函数,或者为了快速破解您的 SQL 代码之类的东西Replace(Replace(Replace(Replace(Value, '\', '\\'), '"', '\"'), '''', '\'''), Char(13), '\n')?Unicode\u\x转义呢?分裂'</script>'成怎么样'<' + '/script>'?(也许这不适用,但也许确实如此,这取决于您如何使用 JSON。)您的 T-SQL 过程是否会完成所有这些工作,并且可重复用于不同的记录集,或者您是否每次都将其重写为每个记录集? SP 需要返回 JSON 吗?

  • 您可能只有一个 SP 需要返回 JSON。目前。有一天,你可能会拥有更多。然后,如果您发现错误,则必须在两个地方进行修复。或五个。或者更多。

通过让中间层进行翻译,您似乎使事情变得更加复杂,但我向您保证,从长远来看,它会变得更好。如果您的产品横向扩展并开始大规模并行化——您总是可以廉价地投入更多的 Web 服务器,但您不能轻易解决数据库服务器资源饱和的问题!所以不要让数据库做比它应该做的更多的工作。它是数据访问层,而不是表示层。让它做尽可能少的工作。为其他一切编写代码。你会很高兴你做到了。

Web 应用程序中字符串处理的速度提示

  1. 确保您的 Web 字符串连接代码不会受到Schlemiel the Painter's Algorithm 的影响。要么在生成 JSON 时直接写入输出缓冲区(Response.Write),要么使用适当的 StringBuilder 对象,或者将 JSON 的部分写入数组并稍后加入()它。不要一遍又一遍地对越来越长的字符串进行简单的香草连接。
  2. 尽可能少地取消引用对象。我不知道您的服务器端语言,但如果它恰好是 ASP Classic,请不要使用字段名称——要么获取对变量中每个字段的引用,要么至少使用整数字段索引。在循环中根据字段名称取消引用字段会(非常)糟糕的性能。
  3. 使用预建库。当您可以使用经过验证的真实库时,请不要自己动手。性能应该与您自己的相同或更好,并且(最重要的是)它将经过测试正确
  4. 如果您打算花时间做这件事,请使其抽象到足以处理任何记录集的转换,而不仅仅是您现在拥有的记录集。
  5. 使用编译后的代码。在编译而不是解释时,您总是可以获得最快的代码。如果您确定 JSON 转换例程确实是瓶颈(并且您必须证明这一点是真实的,不要猜测)然后将代码放入已编译的内容中。
  6. 减少字符串长度。这不是一个大的,但如果可能的话,使用一个字母的 json 名称而不是多字母。对于一个巨大的记录集,这增加两端的节省。
  7. 确保它是 GZipped。这并不是服务器端的改进,但我不能不完整地提及 JSON 性能。

在 JSON 中传递日期

我建议使用单独的 JSON 模式(它本身在 JSON 中,定义要遵循的虚拟记录集的结构)。该模式可以作为标头发送到“记录集”以进行跟踪,也可以已经加载到页面中(包含在基本 javascript 文件中),因此不必每次都发送。然后,在您的 JSON 解析回调(或最终结果对象的后回调)中,查看当前列的架构并根据需要进行转换。您可能会考虑使用 ISO 格式,因为在ECMAScript 5 严格模式下应该有更好的日期支持,并且您的代码可以简化而无需更改数据格式(并且简单的对象检测可以让您将此代码用于任何支持的浏览器它):

日期

日期现在能够解析和输出 ISO 格式的日期。

Date 构造函数现在尝试解析日期,就好像它是 ISO 格式的,首先,然后移动到它接受的其他输入。

此外,日期对象现在有一个新的 .toISOString() 方法,它以 ISO 格式输出日期。var date = new Date("2009-05-21T16:06:05.000Z");

打印(日期.toISOString());// 2009-05-21T16:06:05.000Z

于 2012-07-16T21:24:15.430 回答
3

我不会那样做你正在做的(contatenating)

您可以尝试创建一个使用 JSON.net 并返回 varchar 的 CLR SQL 函数。

请参阅此处如何创建 SQL CLR 函数: http: //msdn.microsoft.com/en-us/library/w2kae45k (v=vs.80).aspx

像这样的东西(未经测试的代码)

[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString MyFunctionName(int id) {
    // Put your code here (maybe find the object you want to serialize using the id passed?)
    using (var cn = new SqlConnection("context connection=true") ) {
        //get your data into an object
        var myObject = new {Name = "My Name"};
        return new SqlString(Newtonsoft.Json.JsonConvert.SerializeObject(myObject));
    }
}
于 2012-07-16T20:56:18.227 回答