Использовать SQL для возврата строки JSON

Это вопрос "лучшей практики". У нас есть внутренние дискуссии по этой теме и мы хотим получить информацию от более широкой аудитории.

Мне нужно сохранить свои данные в традиционной таблице MS SQL Server с обычными столбцами и строками. Иногда мне нужно вернуть DataTable в мое веб-приложение, а иногда мне нужно вернуть строку 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"}

Конечно, пользователь может также получить таблицу, передав false параметру @isJson.

Я хочу быть ясным, что на хранение данных не влияет, ни на какие-либо существующие представления и другие процессы. Это изменение ТОЛЬКО результатов некоторых хранимых процедур.

Мои вопросы:

  • Кто-нибудь пробовал это в большом приложении? Если да, каков был результат?
  • Какие проблемы вы видели/ожидаете ли вы с помощью этого подхода?
  • Есть ли более быстрый способ перейти от таблицы к JSON в SQL Server, кроме изменения хранимой процедуры таким образом или разбора строки в среднем уровне?

Ответ 1

Я лично считаю, что лучшее место для такого рода манипуляций с строками - это программный код на полностью выразительном языке, который имеет функции и может быть скомпилирован. Выполнение этого в T-SQL не очень хорошо. Программный код может иметь быстрые функции, которые выполняют надлежащее экранирование.

Немного подумайте о вещах:

  • При развертывании новых версий частей и частей приложения, где лучше всего подходит эта функциональность?

  • Если вам нужно восстановить свою базу данных (и все ее хранимые процедуры), это негативно повлияет на что-либо? Если вы развертываете новую версию своего веб-интерфейса, произойдет ли преобразование JSON, связанное с базой данных, проблемы?

  • Как вы будете избегать персонажей правильно? Вы отправляете какие-либо даты? Какой формат будет содержать строки даты и как они будут преобразованы в фактические объекты Date на другом конце (если это необходимо)?

  • Как вы будете unit test его (и с автоматическими тестами!), чтобы доказать, что он работает правильно? Как вы проверите его регрессию?

  • SQL Server UDF могут быть очень медленными. Вы довольны тем, что используете медленную функцию, или можете быстро взломать код SQL-кода, например Replace(Replace(Replace(Replace(Value, '\', '\\'), '"', '\"'), '''', '\'''), Char(13), '\n')? Как насчет Unicode, \u и \x экранирования? Как насчет разделения '</script>' на '<' + '/script>'? (Может быть, это не относится, но, возможно, и так, в зависимости от того, как вы используете свой JSON.) Будет ли ваша процедура T-SQL делать все это и быть повторно используемой для разных наборов записей, или вы будете переписывать ее каждый раз в каждый SP, что вам нужно вернуть JSON?

  • У вас может быть только один SP, который должен вернуть JSON. На данный момент. Когда-нибудь у вас может быть больше. Тогда, если вы обнаружите ошибку, вы должны ее исправить в двух местах. Или пять. Или больше.

Может показаться, что вы делаете вещи более сложными, если средний слой выполняет перевод, но я обещаю, что в конечном итоге это будет лучше. Что делать, если ваш продукт масштабируется и начинается массово параллельно - вы всегда можете бросить на него больше веб-серверов, но вы не можете так легко исправить насыщенность ресурсов сервера баз данных! Поэтому не делайте БД больше работы, чем нужно. Это уровень доступа к данным, а не уровень представления. Сделайте так, чтобы минимальный объем работы был возможен. Напишите код для всего остального. Вы будете рады, что сделали.

Советы по скорости для обработки строк в веб-приложении

  • Убедитесь, что код конкатенации вашей веб-строки не страдает Schlemiel the Painter Algorithm. Либо напрямую записывайте в выходной буфер, как генерируется JSON (Response.Write), либо используйте соответствующий объект StringBuilder, либо записывайте части JSON в массив и Join() позже. Не делайте простой консистенции ванили для более длинной и длинной струны снова и снова.
  • Разделить объекты как можно меньше. Я не знаю ваш серверный язык, но, если это происходит, ASP Classic, не используйте имена полей - либо получайте ссылку на каждое поле в переменной, либо, по крайней мере, используйте индексы целочисленного поля. Выделение поля, основанного на его имени внутри цикла, (хуже) хуже.
  • Используйте встроенные библиотеки. Не сворачивайте свои собственные, когда вы можете использовать проверенную и истинную библиотеку. Производительность должна быть одинаковой или лучшей для вашей собственной и (что наиболее важно), она будет проверена и исправлена.
  • Если вы собираетесь потратить время на это, сделайте его достаточно абстрактным, чтобы обрабатывать любой набор записей, а не только тот, который у вас есть.
  • Используйте скомпилированный код. Вы всегда можете получить самый быстрый код, когда он скомпилирован, а не интерпретирован. Если вы определите, что процедуры JSON-преобразования действительно являются узким местом (и вы ДОЛЖНЫ доказать это по-настоящему, не угадывайте), тогда получите код в компилируемое.
  • Уменьшить длину строки. Это не большой, но если это вообще возможно, используйте однобуквенные имена json вместо многобуквенных. Для гигантского набора записей это сэкономит на обоих концах.
  • Убедитесь, что он 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" );

print (date.toISOString()); // 2009-05-21T16: 06: 05.000Z

Ответ 2

Я бы не сделал так, как вы делаете (contatenating)

Вы можете попробовать создать функцию CLR SQL, которая использует JSON.net и возвращает varchar.

См. здесь, как создать 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));
    }
}