AXForum  
Вернуться   AXForum > Блоги > Трудности перехода
NAV
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

Заметки об опыте перехода с Axapta 3.0 на AX 2009
Рейтинг: 3.00. Голосов: 5.

Поиск источника SQL-запросов в коде X++ методом пересечения множеств перекрестных ссылок

Запись от gl00mie размещена 07.11.2022 в 14:52
Теги xrefs

Допустим, со стороны СУБД вы (или ваши DBA) поймали какой-то SQL-запрос из X++, который вам нужно оптимизировать - именно в коде X++, а не просто пришпилить plan guide. Как найти источник запроса в коде приложения? Можно включить трассировку "длинных" SQL-запросов для всех подряд пользователей и потом периодически шерстить логи. Но, во-первых, не факт, что ваш запрос выполняется долго и попадет в лог (а логировать быстрые часто выполняемые запросы может выйти себе дороже). Во-вторых, нет гарантии, что запрос будет повторно выполняться вскоре после включения трассировки. И в-третьих, само по себе включение трассировки SQL-запросов для всех пользователей в рабочей системе может потребовать выполнения определеннных бюрократических процедур.

При таких вводных может подойти вариант поиска, основанный на пересечении множеств перекрестных ссылок на поля таблиц в запросе. В чем-то это похоже на определение местоположения точки за счет триангуляции.

Название: xrefs-triangulation-picture.jpg
Просмотров: 1862

Размер: 41.9 Кб

Возьмем для примера такой SQL-запрос (в чуть причесанном виде):
PHP код:
SELECT
  T1
.REFERENCEDISTRIBUTION,
  
T4.EXCHANGERATE1,
  
T4.EXCHANGERATE2,
  
T4.REPORTINGEXCHANGERATE1,
  
T4.REPORTINGEXCHANGERATE2
FROM
  tempdb
."DBO".t100007_18B95DDA3C21488C85AC09C9FEB59FE5 T1
  CROSS JOIN ACCOUNTINGDISTRIBUTION T2
  CROSS JOIN SUBLEDGERJOURNALACCOUNTENTRYDISTRIBUTION T3
  CROSS JOIN SUBLEDGERJOURNALACCOUNTENTRY T4
WHERE 
((T1.PARTITION = @P1) AND (T1.REFERENCEDISTRIBUTION <> @P2))
  AND ((
T2.PARTITION = @P3) AND (T2.RECID T1.REFERENCEDISTRIBUTION))
  AND ((
T3.PARTITION = @P4) AND (T3.ACCOUNTINGDISTRIBUTION T1.REFERENCEDISTRIBUTION))
  AND ((
T4.PARTITION = @P5) AND (T4.RECID T3.SUBLEDGERJOURNALACCOUNTENTRY))
GROUP BY
  T1
.REFERENCEDISTRIBUTION,
  
T4.EXCHANGERATE1,
  
T4.EXCHANGERATE2,
  
T4.REPORTINGEXCHANGERATE1,
  
T4.REPORTINGEXCHANGERATE2
ORDER BY
  T1
.REFERENCEDISTRIBUTION,
  
T4.EXCHANGERATE1,
  
T4.EXCHANGERATE2,
  
T4.REPORTINGEXCHANGERATE1,
  
T4.REPORTINGEXCHANGERATE2 
Здесь t100007_* - это экземпляр временной таблицы AccountingDistributionTmpJournalize. В запросе упоминаются поля в условиях WHERE (AccountingDistributionTmpJournalize.ReferenceDistribution, SubledgerJournalAccountEntryDistribution.SubledgerJournalAccountEntry, SubledgerJournalAccountEntryDistribution.AccountingDistribution), а также поля в списке выбора SELECT и группировки GROUP BY (SubledgerJournalAccountEntry.ExchangeRate1, SubledgerJournalAccountEntry.ReportingExchRate1, etc). Обратим внимание, что в запросе нет агрегирования, следовательно, все перекрестные ссылки на поля будут с типом Read (для DAX2012 и более ранних версий, которые это различают). Если бы в запросе использовалось агрегирование значений полей (что-то вроде sum(PurchQty) или maxOf(RecId)), то соотв. ссылки были бы типа Write - это в общем случае позволило бы дополнительно сузить выборку.

Далее описан способ локализации источника запроса по перекрестным ссылкам с помощью Excel. Весьма вероятно, что кто-то уже давно так делает. Также наверняка это всё можно худо-бедно автоматизировать в среде разработки и исключить Excel из инструментария.

Найдем перекрестные ссылки на перечисленные поля таблиц и выгрузим их по отдельности на листы одной книги Excel. Для простоты будем рассматривать только ссылки из методов, игнорируя ссылки из Query, View, Form и прочих подобных объектов - с Query и View разговор отдельный... Для этого ссыки можно отфильтровать по наличию номера строки (1..) и дополнительно при желании - по типу доступа (Read/Write). Тип доступа может быть особенно интересен, если запрос использует агрегирование. После выгрузки можно удалить столбцы "Строка" и "Столбец", оставив только "Путь" и "Ссылка" (Read/Write), а затем с помощью Excel удалить дубликаты (Данные/Работа с данными/Удалить дубликаты, если кто еще не пользуется). На выходе мы получим для каждого интересующего нас поля в SQL-запросе отдельный лист Excel с таблицей уникальных путей к методам, где это поле используется. В зависимостти от "везения" ссылок на каждое поле может быть от полудюжины до нескольких сотен. Если выгружать перекрестные ссылки штатно через Ctrl-T, то данные будут отформатированы как таблицы, что весьма удобно. Если же это не так, то стоит заняться форматированием самостоятельно (в Excel Главная/Стили/Форматировать как таблицу) - так вы получите именованные таблицы в книге и именованные колонки в таблицах вместо безымянных ссылок на ячейки.

На одном из листов добавим столбцы для каждого поля из соседних листов. В колонке нужно использовать формулу, которая позволит понять, есть ли пути из ссылок на текущем листе среди путей, ссылающихся на соотв. дополнительное поле. Я обычно использую формулу наподобие такой:
Код:
=ЕСЛИОШИБКА(ПОИСКПОЗ([@Путь];Table2[[#Все];[Путь]];0)>0;"")
Для тех, кто по каким-то причинам нечасто работает в Excel с данными, отформатированными как таблицы, поясню. Здесь [@Путь] - это ссылка на ячейку в той же строке текущей таблицы и в колонке "Путь"; Table2[[#Все];[Путь]] - ссылка на колонку (одномерный массив ячеек) на соседнем листе, где данные отформатированы в виде именованной таблицы Table2, причем из таблицы тоже берется колонка "Путь". Т.е. мы ищем путь из перекрестных ссылок в текущей строке таблицы среди ссылок в другой таблице, относящихся к другому полю. Если такой путь найдется, то его индекс будет больше нуля, и мы увидим значение "ИСТИНА", если же не найдется, то функция ЕСЛИОШИБКА() подставит пустую строку.

Вот как может выглядеть результат такого пересечения множеств перекрестных ссылок:
Нажмите на изображение для увеличения
Название: xrefs-triangulation-table.png
Просмотров: 7455
Размер:	64.6 Кб
ID:	439

Отфильтровав все колонки с формулами по значению "ИСТИНА", мы останемся с тремя ссылками:
  • \Classes\SubledgerJournalizer\loadaccountingDistributionTmp
  • \Classes\SubledgerJournalizer\PSALoadaccountingDistReleaseTmp
  • \Classes\SubledgerJournalizer\loadReferenceDistributionInformation
После этого методом пристального взгляда нетрудно установить, что в двух методах поля в действительности лишь "читаются", а вот SQL-запрос выполняет из \Classes\SubledgerJournalizer\loadReferenceDistributionInformation.
Нажмите на изображение для увеличения
Название: xrefs-triangulation-result.png
Просмотров: 7345
Размер:	125.5 Кб
ID:	440

Описанный способ, разумеется, - не панацея, и встречаются разного рода нюансы. Скажем, в коде X++ работа может вестись с Map-ами, а не непосредственно с таблицами, тогда перекрестные ссылки нужно будет искать на поля Map-ов. Также SQL-запрос может формироваться на основе заранее созданного Query и лишь дополнительно фильтроваться в коде, впрочем, из моего скромного опыта, это встречается существенно реже.
Размещено в Без категории
Просмотров 199241 Комментарии 1
Всего комментариев 1

Комментарии

  1. Старый комментарий
    Прикольно.
    Запись от Logger размещена 07.11.2022 в 18:19 Logger is offline
 


Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 23:12.