|
![]() |
#1 |
Участник
|
|
|
|
За это сообщение автора поблагодарили: mazzy (5). |
![]() |
#2 |
Участник
|
Цитата:
Сообщение от mazzy
![]() Задача:
внешняя система читает проводки по клиентам. Причем внешняя система для каждой проводки ожидает получить в выборке счет ГК из профиля разноски. Как оптимально написать T-SQL запрос для такой выборки? Основная проблема - в настроечной таблице для одной проводки может быть несколько разных подходящих настроек для одной исходной мастер-записи. Цитата:
Но возьмем для примера иерархию категорий из AX 2012. С одной стороны, есть понятная пользователю настроечная таблица, хранящая узлы иерархии (категории), а с другой, для той или иной категории есть потребность быстро определять в запросах связанные подкатегории выше по иерархии, на которые могут ссылаться другие настройки, скажем, те же скидки за комплект и т.п. Тут уже разработчики стандарта пошли по второму пути из приведенных выше и реализовали таблицу RetailCategoryContainmentLookup, содержащую SQL-friendly представление иерархии категорий. Так вот, мне кажется, в следующем сценарии:
|
|
|
За это сообщение автора поблагодарили: mazzy (2). |
![]() |
#3 |
Участник
|
эм.... вариант с настройками пришел еще из конкорда, где была база данных собственного формата с доступом по одной записи (типа DBF). sql'я тогда еще не было.
не. не валидно. ) Замечательно. А пример реализации? куда можно подсмотреть? |
|
![]() |
#4 |
Модератор
|
Ну раз про потенциальные проблемы с выверкой модулей и ГК уже писали, то "чисто техническое" решение - сделать CTE которое по "наивному" (хотя что там наивного, вполне рабочая логика) варианту отрезолвит комбинацию счета клиента и профиля разноски (CustTable - CustGroup - CustLedgerAccounts) в счет ГК. Это достаточно компактная выборка и ее уже можно джойнить с CustTrans (один раз, вместо трех)
__________________
-ТСЯ или -ТЬСЯ ? Последний раз редактировалось Vadik; 21.04.2016 в 13:40. |
|
|
За это сообщение автора поблагодарили: mazzy (2), gl00mie (1). |
![]() |
#5 |
Участник
|
Цитата:
Сообщение от Vadik
![]() Ну раз про потенциальные проблемы с выверкой модулей и ГК уже писали, то "чисто техническое" решение - сделать CTE которое по "наивному" (хотя что там наивного, вполне рабочая логика) варианту отрезолвит комбинацию счета клиента и профиля разноски (CustTable - CustGroup - CustLedgerAccounts) в счет ГК. Это достаточно компактная выборка и ее уже можно джойнить с CustTrans
В смысле, что вижу, то и пишу. Меня беспокоит высокая цена join'ов. если так писать, то будет ли делаться лишняя выборка? или SQL закэширует выборки и по возможности повторять не будет? |
|
![]() |
#6 |
Участник
|
Цитата:
Или где-то по тригеру? Ведь и клиенты могут меняться, и настройки... |
|
![]() |
#7 |
Moderator
|
mazzy, ну ты там сам написал что твой запрос с partition и over by как-то подозрительно много времени тратит на джойн. Причина проста - из за or между несколькими условиями джойна, SQL обычно не может использовать ни одного индекса. В результате он делает что-то типа декартова произведения таблиц и потом медленно и печально фильтрует.
Вообще - подход "n запросов без OR, вместо одного запроса с n OR" мне неоднократно экономил производительность. Последний пример - у клиента с порядка миллиона батчей, запрос в inventUpdateOnHand отрабатывал порядка 2-3-4 минут (блокируя все в пессиместическом режиме), а после того как я его заменил на n запросов по числу испольуемых масок аналитик - стало срабатывать за 3-4 секунды. |
|
|
За это сообщение автора поблагодарили: mazzy (2), twilight (1). |
![]() |
#8 |
Участник
|
Цитата:
Или где-то по тригеру? Ведь и клиенты могут меняться, и настройки... Блин, код жеж нереентерабельный. Запросы шага 4 нельзя строить пока не будет выполнено построение временной/вспомогательной/зарезолвенной таблицы. А перестроение придется делать при каждом создании/удалении клиента, плана счетов или настройки. Тут пора вспомнить акс2012 с ее безумными фин.аналитиками. Там перестроение придется делать для каждой комбинации фин.аналитик (напомню, что в акс2012 в фин.аналитики входит и счет). Да, я говорил вначале про упрощение задачи. Но не такой же ценой. Подход отдельной вспомогательной таблицы по-моему неприменим для акс2012 и выше )))) Последний раз редактировалось mazzy; 21.04.2016 в 14:09. |
|
![]() |
#9 |
Moderator
|
Цитата:
Ах да - и перестраивать при каждом запросе, разумеется. А чтобы не тормозило - построй по custTrans индекс по custAccount+postingProfile. Последний раз редактировалось fed; 21.04.2016 в 14:09. |
|
![]() |
#10 |
Участник
|
Последний раз редактировалось mazzy; 21.04.2016 в 14:25. |
|
![]() |
#11 |
Участник
|
Для начала - да, при каждом выполнении. Философского булыжника нет. Нужно пробовать.
X++: SELECT DATAAREAID, COUNT(*) FROM ( SELECT DATAAREAID, ACCOUNTNUM, POSTINGPROFILE FROM CUSTTRANS GROUP BY DATAAREAID, ACCOUNTNUM, POSTINGPROFILE ) T GROUP BY DATAAREAID |
|
![]() |
#12 |
Модератор
|
--
Цитата:
наивный: 4% по плану запроса, выполнено за 1:17, выбрано 342294 записи
![]()
__________________
-ТСЯ или -ТЬСЯ ? |
|
![]() |
#13 |
Участник
|
Цитата:
я сформулировал свои вопросы в первом сообщении этой темы Как оптимально написать T-SQL запрос для выборки настройки Table/Group/All (например, счет ГК из профиля разноски) собственно главный вопрос - Как оптимально написать T-SQL запрос для выборки настройки Table/Group/All (ответ похоже получили. Но так писать не шибко удобно) побочный квест - как, на ваш взгляд должны быть устроены подобные настроечные таблицы, чтобы и без Аксаптовского кэширования можно было бы удобно работать с такими настроечными таблицами на уровне SQL? |
|
![]() |
#14 |
Модератор
|
Цитата:
Цитата:
Злодей
__________________
-ТСЯ или -ТЬСЯ ? Последний раз редактировалось Vadik; 21.04.2016 в 17:17. |
|
![]() |
#15 |
Участник
|
мдя... на большой базе пришлось подбирать фильтры. в принципе там получалось минут 20 на всю выборку в 15млн результирующих записей.
вот какие запросы были: Код: -- наивный: 4% по плану запроса, выполнено за 1:24, выбрано 342294 записи with trans as ( SELECT isnull(accTable.SUMACCOUNT, isnull(accGroup.SumAccount, accAll.SumAccount)) as pSumAccount, tr.* from custtrans as tr join custtable as tab on (tab.accountnum = tr.accountnum) left join CUSTLEDGERACCOUNTS as accTable on (accTable.DATAAREAID = 'eras' and accTable.POSTINGPROFILE = tr.POSTINGPROFILE and accTable.AccountCode = 0 and accTable.NUM = tab.ACCOUNTNUM) left join CUSTLEDGERACCOUNTS as accGroup on (accGroup.DATAAREAID = 'eras' and accGroup.POSTINGPROFILE = tr.POSTINGPROFILE and accGroup.AccountCode = 1 and accGroup.NUM = tab.CUSTGROUP) left join CUSTLEDGERACCOUNTS as accAll on (accAll.DATAAREAID = 'eras' and accAll.POSTINGPROFILE = tr.POSTINGPROFILE and accAll.AccountCode = 2) where tr.DATAAREAID = '3r' and tab.dataAreaId = 'edat' ) select * from trans where trans.pSumAccount in ('62.02.01', '62.02.02', '62.02.02', '62.01.09') Код: -- row_number, 87% по плану запроса, выполнился 4:39, выбрано 342294 записи with trans as ( SELECT --top 100 la.SUMACCOUNT as pSumAccount ,row_number() over (partition by tr.DataAreaId, tr.RecId, la.dataareaid, la.POSTINGPROFILE order by la.ACCOUNTCODE) as acc_rn ,tr.* from custtrans as tr join custtable as tab on (tab.accountnum = tr.accountnum) left join CUSTLEDGERACCOUNTS as la on ( la.dataareaid = 'eras' and la.POSTINGPROFILE = tr.POSTINGPROFILE and la.num = (case la.AccountCode when 0 then tab.AccountNum when 1 then tab.CustGroup else la.num end) ) where tr.DATAAREAID = '3r' and tab.dataAreaId = 'edat' ) select * from trans where trans.acc_rn = 1 and trans.pSumAccount in ('62.02.01', '62.02.02', '62.02.02', '62.01.09') Код: -- подзапрос: 8% по плану запроса, выполнился за 1:59, выбрано 342294 записи with trans as ( select (select top 1 SumAccount from CustLedgerAccounts as la where la.PostingProfile = tr.postingProfile and la.num = (case la.AccountCode when 0 then tab.AccountNum when 1 then tab.CustGroup else la.num end) and la.dataAreaId = 'eras' order by AccountCode ---- <---- ) as pSumAccount ,tr.* from custtrans as tr inner join custtable as tab on (tab.AccountNum = tr.AccountNum) where tr.DATAAREAID = '3r' and tab.dataAreaId = 'edat' ) select * from trans where trans.pSumAccount in ('62.02.01', '62.02.02', '62.02.02', '62.01.09') А есть у кого-нибудь объяснение таким результатам? И как можно сделать оптимальнее? Последний раз редактировалось mazzy; 21.04.2016 в 16:19. |
|
![]() |
#16 |
Участник
|
Copy-paste detected
![]() Цитата:
Сообщение от mazzy
![]() вот какие запросы были:
Код: -- наивный: 4% по плану запроса, выполнено за 1:17, выбрано 342294 записи with trans as ( SELECT isnull(accTable.SUMACCOUNT, isnull(accGroup.SumAccount, accAll.SumAccount)) as pSumAccount, tr.* from custtrans as tr join custtable as tab on (tab.accountnum = tr.accountnum) left join CUSTLEDGERACCOUNTS as accTable on (accTable.DATAAREAID = 'eras' and accTable.POSTINGPROFILE = tr.POSTINGPROFILE and accTable.AccountCode = 0 and accTable.NUM = tab.ACCOUNTNUM) left join CUSTLEDGERACCOUNTS as accGroup on (accTable.DATAAREAID = 'eras' and accGroup.POSTINGPROFILE = tr.POSTINGPROFILE and accGroup.AccountCode = 1 and accGroup.NUM = tab.CUSTGROUP) Код: on (accGroup.DATAAREAID = 'eras' Код: on (accTable.DATAAREAID = 'eras' |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
![]() |
#17 |
Участник
|
|
|
![]() |
#18 |
Злыдни
|
Может стоит для оптимизации пойти по следующему пути:
1. Select into во временную таблицу по custtable, custledger, custledgeraccount с использованием union. Записать код клиента, тип счета, профиль, найденный счет разноски и dataareaid. На этом этапе отсекаем записи с отсутствующими типами настройки; 2. Для custtrans ищем первую запись во временной таблице по совпадению кода клиента и профиля разноски, отсортировав их по коду клиента и типу счета. На мой взгляд, для SQL вставка во временную таблицу максимум [кол-во клиентов * кол-во профилей * 3] записей и единственный join отработает быстрее. чем несколько left join с последующим отбором. Но это надо тестить, а "боевой" базы под рукой нет. PS: custledger в первом запросе не нужен, т.к. код профиля уже есть в custledgeraccount
__________________
люди...считают, что если техника не ломается, то ее не нужно ремонтировать. Инженеры считают, что если она не ломается, то нуждается в совершенствовании. Последний раз редактировалось KiselevSA; 22.04.2016 в 09:26. |
|
![]() |
#19 |
Участник
|
отсекать нельзя - не соответствует стандартному поведению.
стандартное поведение на X++ - настройка может отсутствовать. Цитата:
сначала вставка во временную(!) а потом join - это режим "cross apply join". см кучу статей по поводу разницы между "cross join" и "join" из того, что я знаю - просто join будет работать быстрее. и чтобы выйти на такой режим не надо городить временные таблицы. достаточно ключевого слова в select ))))) |
|
![]() |
#20 |
Злыдни
|
X++: select ct.ACCOUNTNUM, cla.ACCOUNTCODE, cla.POSTINGPROFILE, dim.DISPLAYVALUE, cla.DATAAREAID from CUSTTABLE as ct join CUSTLEDGERACCOUNTS as cla on ct.ACCOUNTNUM = cla.NUM and ct.DATAAREAID = cla.DATAAREAID and cla.ACCOUNTCODE = 0 join DIMENSIONATTRIBUTEVALUECOMBINATION as dim on cla.SUMMARYLEDGERDIMENSION = dim.RECID where ct.DATAAREAID = 'usmf' union select ct1.ACCOUNTNUM, cla1.ACCOUNTCODE, cla1.POSTINGPROFILE, dim1.DISPLAYVALUE, cla1.DATAAREAID from CUSTTABLE as ct1 join CUSTLEDGERACCOUNTS as cla1 on ct1.CUSTGROUP = cla1.NUM and ct1.DATAAREAID = cla1.DATAAREAID and cla1.ACCOUNTCODE = 1 join DIMENSIONATTRIBUTEVALUECOMBINATION as dim1 on cla1.SUMMARYLEDGERDIMENSION = dim1.RECID where ct1.DATAAREAID = 'usmf' union select ct2.ACCOUNTNUM, cla2.ACCOUNTCODE, cla2.POSTINGPROFILE, dim2.DISPLAYVALUE, cla2.DATAAREAID from CUSTTABLE as ct2 join CUSTLEDGERACCOUNTS as cla2 on ct2.DATAAREAID = cla2.DATAAREAID and cla2.ACCOUNTCODE = 2 join DIMENSIONATTRIBUTEVALUECOMBINATION as dim2 on cla2.SUMMARYLEDGERDIMENSION = dim2.RECID where ct2.DATAAREAID = 'usmf'
__________________
люди...считают, что если техника не ломается, то ее не нужно ремонтировать. Инженеры считают, что если она не ломается, то нуждается в совершенствовании. |
|
|
Опции темы | Поиск в этой теме |
Опции просмотра | |
|