Нормализовать текст 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(), вы знаете, что вы получите уникальный маркер для каждого запроса, даже если он выполняется с разных клиентов.

Другие вопросы по тегам