Нормализовать текст SQL-запроса в C#
Я выполнил следующий запрос в базе данных SQL Server.
SELECT * FROM Production.Product WHERE Name = 'Bearing Ball'
Затем я попытался получить текст запроса вместе со статистикой, используя следующую команду SQL:
SELECT
qs.sql_handle,
qs.execution_count AS EXECUTION_COUNT,
AVG_TIME = --Converted from microseconds
(qs.total_elapsed_time/1000000) / qs.execution_count,
qs.total_elapsed_time,
TOTAL_TIME = --Converted from microseconds
qs.total_elapsed_time/1000000,
st.text AS TEXT
FROM
sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
CROSS apply sys.dm_exec_query_plan (qs.plan_handle) AS qp
ORDER BY qs.total_worker_time DESC
Я получаю что-то вроде этого
(@1 varchar(8000))SELECT * FROM [Production].[Product] WHERE [Name]=@1
Но я хочу выполнить сопоставление строк из моей программы на C# и получить статистику текста запроса. Программа C# знает только об исходном запросе (SELECT * FROM Production.Product WHERE Name = 'Bearing Ball'). Замена параметров - не единственная проблема, поскольку вы можете видеть, что сервер базы данных добавляет квадратные скобки к именам таблиц.
Есть ли способ преодолеть эту проблему. Как преобразовать исходный запрос (SELECT * FROM Production.Product WHERE Name = 'Bearing Ball') в нормализованный ((@1 varchar(8000))SELECT * FROM [Production].[Product] WHERE [Name]=@1)? Могу ли я сделать это с помощью Microsoft.Data.Schema.ScriptDom? Обратите внимание, что я хочу нормализовать свой запрос, не выполняя его на сервере базы данных.
2 ответа
Можно нормализовать код T-SQL, используя ScriptDom. Перед синтаксическим анализом вам нужно будет удалить список префиксных параметров, поскольку ScriptDom анализирует только действительный T-SQL.
Microsoft.Data.Schema.ScriptDom, тем не менее, является старой версией, поставляемой с Visual Studio. Начиная с SQL Server 2012, новая версия поставляется вместе с самим SQL Server. Он имеет пространство имен Microsoft.SqlServer.TransactSql.ScriptDom и обновляется до самых последних версий SQL-сервера. Программирование ScriptDom, однако, не простая задача. Нормализация кода T-SQL - еще более сложная задача. Arvind Shyamsundar опубликовал блог на эту тему в блогах msdn. Демонстрационный метод Arvind состоит в том, чтобы нормализовать все копии кода, а затем проверять итоговые операторы, сохраняя результаты в словаре, используя контрольную сумму для ключа. Если контрольная сумма одинакова, ваш T-SQL должен быть эквивалентен тому, который был введен ранее. Эта идея будет работать и для вас, хотя, вероятно, не все нормализации в демоверсии Arvind полезны для вашего случая использования; возможно, вам придется отключить некоторые из них, как, например, нормализация литеральных значений.
У Арвинда есть почтовый индекс, прикрепленный к его блогу с демо-кодом, так что вы можете легко начать с него. Я попросил Арвинда в комментарии опубликовать его код на GitHub, чтобы мы могли добавить наши расширения, но пока не получил от него известия. Я расширил (для своих собственных целей) демонстрацию Арвинда, чтобы сделать еще больше нормализаций, таких как устранение лишних скобок, нормализация логической логики и нормализация скалярных выражений. Кроме того, я работаю над еще большим количеством нормализаций, таких как нормализация операторов соединения и предложений соединения. Но это оказывается довольно сложным... Я хочу поделиться своим кодом, отправьте мне сообщение, если вы думаете, что можете что-то с этим сделать.
Одним из решений, если вы просто хотите иметь возможность сопоставить их, было бы добавить префикс SQL-оператора к GUID в комментарии, а затем сопоставить это:
Итак, в C#:
var statementMagicStringMarker = Guid.NewGuid().ToString("N");
cmd.CommandText = "--" + statementMagicStringMarker + "\n" +
"SELECT * FROM Production.Product WHERE Name = 'Bearing Ball'";
cmd.ExecuteNonQuery();
Затем, когда вы выполняете запрос статистики, ищите запрос, который содержит ваш оператор StatementMagicStringMarker, и вы будете знать, что у вас есть тот же оператор, даже если он может быть очищен. Используя Guid.NewGuid(), вы знаете, что вы получите уникальный маркер для каждого запроса, даже если он выполняется с разных клиентов.