16.08.2007, 17:58 | #1 |
Участник
|
оптимизируем запросы.
если у вас тормоза при нажатии кнопочки запросы в шапках (заказо/закупок..) то предлагаю следующее:
есть такой метод на карте (MAP) - CustVendInvoiceJour.factureJourSortedList_RU X++: DIS : // Header markups while select factureJour join RecId from factureTrans join RecId from markupTrans where factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount && factureTrans.FactureId == factureJour.FactureId && factureTrans.Module == module && factureTrans.FactureLineType == FactureLineType_RU::Markup && markupTrans.RecId == factureTrans.MarkupRefRecId && markupTrans.TransTableId == this.TableId && markupTrans.TransRecId == this.RecId { if ( ! ret.find(factureJour)) { ret.ins(factureJour); } } // Line markups while select factureJour join RecId from factureTrans join RecId from markupTrans join RecId from custVendInvoiceTrans where factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount && factureTrans.FactureId == factureJour.FactureId && factureTrans.Module == module && factureTrans.FactureLineType == FactureLineType_RU::Markup && markupTrans.RecId == factureTrans.MarkupRefRecId && markupTrans.TransTableId == custVendInvoiceTrans.TableId && custVendInvoiceTrans.RecId == markupTrans.TransRecId && custVendInvoiceTrans.SalesPurchId == this.Num && custVendInvoiceTrans.InvoiceNum == this.InvoiceId && (module == FactureModule_RU::Cust || (module == FactureModule_RU::Vend && custVendInvoiceTrans.PurchInternalInvoiceId == this.PurchInternalInvoiceId)) && custVendInvoiceTrans.InvoiceDate == this.InvoiceDate && custVendInvoiceTrans.NumberSequenceGroupId == this.NumberSequenceGroupId { if ( ! ret.find(factureJour)) { ret.ins(factureJour); } } Пришлось явно сиквелу сказать что делать: для первого запроса наилучшую селективность обеспечивает MARKUPTRANS, для второго карта CUSTINVOICETRANS - имеем: X++: // Header markups // SHiSHok.FRA_Optimize --> factureJour.clear(); while select forceselectorder forcenestedloop forceplaceholders RecId from markupTrans join RecId from factureTrans join * from factureJour index CustVendModuleIdx where markupTrans.TransTableId == this.TableId && markupTrans.TransRecId == this.RecId && factureTrans.FactureLineType == FactureLineType_RU::Markup && factureTrans.MarkupRefRecId == markupTrans.RecId && factureJour.FactureId == factureTrans.FactureId && factureJour.Module == factureTrans.Module && factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount { if ( ! ret.find(factureJour)) { ret.ins(factureJour); } } // Line markups while select forceselectorder forcenestedloop forceplaceholders RecId from custVendInvoiceTrans join RecId from markupTrans join RecId from factureTrans join * from factureJour index CustVendModuleIdx where custVendInvoiceTrans.SalesPurchId == this.Num && custVendInvoiceTrans.InvoiceNum == this.InvoiceId && custVendInvoiceTrans.InvoiceDate == this.InvoiceDate && custVendInvoiceTrans.NumberSequenceGroupId == this.NumberSequenceGroupId && (module == FactureModule_RU::Cust || (module == FactureModule_RU::Vend && custVendInvoiceTrans.PurchInternalInvoiceId == this.PurchInternalInvoiceId)) && markupTrans.TransTableId == custVendInvoiceTrans.TableId && markupTrans.TransRecId == custVendInvoiceTrans.RecId && factureTrans.FactureId == factureJour.FactureId && factureTrans.Module == factureJour.Module && factureTrans.FactureLineType == FactureLineType_RU::Markup && factureTrans.MarkupRefRecId == markupTrans.RecId && factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount { if ( ! ret.find(factureJour)) { ret.ins(factureJour); } } // SHiSHok.FRA_Optimize <--
__________________
--- SHiSHok Последний раз редактировалось SHiSHok; 16.08.2007 в 18:43. |
|
|
За это сообщение автора поблагодарили: kashperuk (3). |
16.08.2007, 18:56 | #2 |
Участник
|
ИМХО лучше не
X++: index CustVendModuleIdx X++: index hint CustVendModuleIdx |
|
16.08.2007, 19:29 | #3 |
Модератор
|
join RecId для неиспользуемых таблиц есть смысл заменить на join TableId
__________________
-ТСЯ или -ТЬСЯ ? |
|
16.08.2007, 20:23 | #4 |
Участник
|
разница есть, но небольшая:
эти 2 запроса на 10000 итераций с чистым кэшем сиквела (freeproccache + DROPCLEANBUFFERS) такие результаты дают: 1) выборка с TableId - прошло сек: 58-59 2) выборка с RecId - прошло сек: 58-59 т.е. результаты примерно одинаковые но что примечательно для всего этого: сиквел таки грузится чуть меньше (за recId не лезет на другую станицу): Page lookups/sec для вариантов соответственно (средний показатель): 1) 5200 страниц/сек 2) 5600 страниц/сек реальных таких нагрузок практически не будет, но знание - сила!
__________________
--- SHiSHok |
|
16.08.2007, 22:20 | #5 |
Участник
|
Дык, и разберитесь с сиквелом.
Цитата:
Во-вторых, явные хинты требуют постоянного административного внимания. В-третьих, с какой стати у вас сиквел сам этого не понимает? Разберитесь именно с этим. В-четвертых, где-то в блогах, по-моему, Еременко писал, что условия после всех join'ов в Аксапте работает медленнее, чем условия внутри. Про recId и TableId тоже писали. Суть в том, что TableId - аксаптовское поле, не хранимое в базе СКЛе. Это значит, что использование TableId может привести к тому, что таблица исчезнет из SQL-запроса. Т.е. нужно делать примерно так X++: // Header markups // SHiSHok.FRA_Optimize --> factureJour.clear(); while select RecId from markupTrans where markupTrans.TransRecId == this.RecId && markupTrans.TransTableId == this.TableId join RecId from factureTrans where factureTrans.MarkupRefRecId == markupTrans.RecId && factureTrans.Module == module && factureTrans.FactureLineType == FactureLineType_RU::Markup join * from factureJour index CustVendModuleIdx where factureJour.FactureId == factureTrans.FactureId && factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount 1. Порядок полей должен по возможности совпадать с порядком полей в индексе (в противном случае SQL должен выполнить доп-работу) 2. Если у вас задействуется несколько индексов, то сначала укажите самый селективный (см. factureTrans) 3. Обратите внимание на проверку на модуль. Лучше сравните с константой, нежели поля таблиц В общем, разберитесь с тем как работает запрос в Аксапте и что происходит в момент выполнения. Перестаньте насиловать Аксапту и СКЛ. Помните: ласку, чистоту и смазку. |
|
17.08.2007, 00:57 | #6 |
Участник
|
Цитата:
Сообщение от mazzy
Во-первых, с чего вы взяли, что так будет всегда и у всех.
Во-вторых, явные хинты требуют постоянного административного внимания. В-третьих, с какой стати у вас сиквел сам этого не понимает? Разберитесь именно с этим. В-четвертых, где-то в блогах, по-моему, Еременко писал, что условия после всех join'ов в Аксапте работает медленнее, чем условия внутри. - запрос состоит из inner join-ов, т.о. отсутствие записи в любой таблице ведет к пустому запросу - множество записей в markupTrans обозначено явно (TransTableId + TransRecId) - каждая запись в множестве markupTrans ссылается на 1 (и более) записей в FACTURETRANS_RU - FACTURETRANS_RU.MARKUPREFRECID=MARKUPTRANS.RECID (есть индекс по этому полю) - каждая запись из множества FACTURETRANS_RU ссылается на 1 запись в FACTUREJOUR_RU - это может быть одна и та же запись(FACTUREJOUR_RU.FACTUREID=FACTURETRANS_RU.FACTUREID, MODULE фиксирован - есть уникальный индекс) = Таком образом выбираются только необходимые данные от меньшего к большему (нет ибыточной выборки) - Не вижу более оптимального плана для данного запроса. 2) отсутствие хинтов,на мой взгляд, требует не менее пристального внимания DBA 3) поработаю над сиквелом. 4) буду знать, даже визуально приятнее читать условия к конкретному join (в общем то следовал схеме запроса DIS слоя) Цитата:
Цитата:
Цитата:
может быть - сиквел подобную подстановку сам делает.
__________________
--- SHiSHok Последний раз редактировалось SHiSHok; 17.08.2007 в 01:05. |
|
17.08.2007, 01:41 | #7 |
Участник
|
Цитата:
Если таблица markupTrans содержит значительно больше записей, чем таблица FatureTrans, то почему надо начинать именно с MarkupTrans? Цитата:
В отличие от явно заданных хинтов. При явно заданных хинтах сервер будет использовать даже неоптимальный явно заданный план. См. типичный пример http://axapta.mazzy.ru/lib/querytuning/ Цитата:
Это особенность Аксапты. В dis-слое писали неоптимально. Об этом писал Еременко, по-моему, в своем блоге (торможу и не могу сейчас найти) Цитата:
Еще как может исчезнуть. Аксапта может разбить один запрос на несколько вложенных если в середине используется временная таблица или нет полей для выборки. Например, select table1 where ... join table2 where ... join table3 where ... ЕСЛИ table2 1. привязана к незакупленному лицензионному ключу 2. или выключенному конфигурационному 3. или является временной ТО на sql пойдет не один запрос, а несколько вложенных. В некоторых сервис-паках подобные "оптимизации" выполнялись и для таблиц, для которых не выбирается ни одно поле. То, что у вас таблица осталась в запросе еще ни о чем не говорит Чтобы таблица гарантировано попала в SQL запрос, запрос по ней должен содержать хотя бы одно хранимое на SQL поле. Поэтому tableId использовать опасно. TableId - не хранимое поле. Нет, не делает. |
|
|
За это сообщение автора поблагодарили: belugin (4). |
17.08.2007, 03:14 | #8 |
Member
|
Цитата:
Сообщение от mazzy
...
Аксапта может разбить один запрос на несколько вложенных если в середине используется временная таблица или нет полей для выборки. Например, select table1 where ... join table2 where ... join table3 where ... ЕСЛИ table2 ... 2. или выключенному конфигурационному ... ТО на sql пойдет не один запрос, а несколько вложенных. ... Вот джоб написал. static void glibs() { InventTable inventTable; PlInventTransExternal plInventTransExternal; InventDim inventDim; ; select inventTable join plInventTransExternal where inventTable.ItemId == plInventTransExternal.ItemId join inventDim where inventDim.inventDimId == plInventTransExternal.InventDimId; } Польша у меня отключена. Получаю (4.0): Error Message (02:31:32) Cannot select a record in Items (InventTable). Temporary tables must be the inner tables when joined to permanent tables. Info Message (02:31:32) (C)\Jobs\glibs - line 8 Если польскую таблицу поставить первой, то отработает без ошибки. Только ничего не вернет. Я б тоже так поступил. Между таблицами отсутствует связь в таком случае. По-моему, логично. Цитата:
Сообщение от mazzy
...
Чтобы таблица гарантировано попала в SQL запрос, запрос по ней должен содержать хотя бы одно хранимое на SQL поле. ... А если в твоем примере table1 — это CustInvoiceJour table2 — это CustInvoiceTrans table3 — это InventDim и нам нужно CustInvoiceJour.OrderAccount и InventDim.InventLocationId, то какой запрос пойдет на сервер в таком случае? Ну, если вот так вот написать static void glibs() { CustInvoiceJour custInvoiceJour; CustInvoiceTrans custInvoiceTrans; InventDim inventDim; ; while select OrderAccount from custInvoiceJour join TableId from custInvoiceTrans where custInvoiceTrans.SalesId == custInvoiceJour.SalesId && custInvoiceTrans.InvoiceId == custInvoiceJour.InvoiceId && custInvoiceTrans.InvoiceDate == custInvoiceJour.InvoiceDate && custInvoiceTrans.numberSequenceGroup == custInvoiceJour.numberSequenceGroup join InventLocationId from inventDim where inventDim.inventDimId == custInvoiceTrans.InventDimId { info (strfmt("%1 -- %2", custInvoiceJour.OrderAccount, inventDim.InventLocationId)); } } Цитата:
Сообщение от mazzy
...
Поэтому tableId использовать опасно. TableId - не хранимое поле. ... Я поддерживаю участника Vadik в этом вопросе. SHiSHok, как говорил... все тот же Vadik, когда я начинал учиться писать запросы в Аксапте, "* from" можно не писать.
__________________
С уважением, glibs® |
|
|
За это сообщение автора поблагодарили: zemlyn (1). |
17.08.2007, 09:53 | #9 |
Модератор
|
Цитата:
SQL Style - FROM x,y,z or INNER JOIN; We are all Cowboy Coders На производительности запроса с использованием только INNER JOIN и AND-условий такое оформление не сказывается никак (оптимизатор это щелкает как орехи), скорее неряшливость в оформлении кода. "Выстрелить" это может при использовании EXIST и NOTEXIST JOIN Цитата:
Цитата:
Сообщение от mazzy
Аксапта может разбить один запрос на несколько вложенных если в середине используется временная таблица или нет полей для выборки.
Например, select table1 where ... join table2 where ... join table3 where ... ЕСЛИ table2 1. привязана к незакупленному лицензионному ключу 2. или выключенному конфигурационному 3. или является временной ТО на sql пойдет не один запрос, а несколько вложенных
__________________
-ТСЯ или -ТЬСЯ ? |
|
|
За это сообщение автора поблагодарили: George Nordic (2), zemlyn (1). |
17.08.2007, 10:15 | #10 |
Участник
|
Цитата:
Единственное когда план плох - это когда в FaсtureTrans_RU по условию (FACTURELINETYPE, MODULE) вообще нет записей и таблица намного меньше MARKUPTRANS. Но надо посмотреть код - может при таких условиях код не будет исполнятся и вовсе. да уж приходится. на базе под 100Gb мнооого чего вылезает... поправочка: в сиквел передается условие as is (C.MODULE=B.MODULE), а в плане исполнения уже все подставляется как надо (C.MODULE=0). Немного в продолжение: На сиквеле у меня все как надо: дефрагментаниция нужных таблиц в нужное время , обновление статистики, и т.д. Вспомнив обсуждение одного запроса с Киселевым, сделал следующее: Код: update statistics factureJour_RU with fullscan update statistics factureTrans_RU with fullscan update statistics markupTrans with fullscan update statistics custInvoiceTrans with fullscan dbcc freeproccache ЗЫ: Я не навязываю своего решения никому!
__________________
--- SHiSHok Последний раз редактировалось SHiSHok; 17.08.2007 в 10:19. |
|
|
За это сообщение автора поблагодарили: belugin (4). |
17.08.2007, 10:22 | #11 |
Участник
|
Про join TableId это Mazzy опять не в ту степь повел.
Все там нормально отрабатывает. Возможно были глюки на версии 2.0 или раньше, но на 2.5, насколько я понмю, уже все ОК. Да что говорить. Достаточно посмотреть на один из самых часто используемых запросов в логистической Аксапте: Macros\InventDimJoin так InventDim join'ится как раз по TableId, так как в результирующей выборке она не требуется. Поясню про "опять" - просто тему эту уже обсуждали пару раз Последний раз редактировалось kashperuk; 17.08.2007 в 10:25. |
|
|
За это сообщение автора поблагодарили: belugin (4). |
07.09.2009, 09:05 | #12 |
Axapta Retail User
|
Цитата:
Сообщение от mazzy
Т.е. нужно делать примерно так
X++: // Header markups // SHiSHok.FRA_Optimize --> factureJour.clear(); while select RecId from markupTrans where markupTrans.TransRecId == this.RecId && markupTrans.TransTableId == this.TableId join RecId from factureTrans where factureTrans.MarkupRefRecId == markupTrans.RecId && factureTrans.Module == module && factureTrans.FactureLineType == FactureLineType_RU::Markup join * from factureJour index CustVendModuleIdx where factureJour.FactureId == factureTrans.FactureId && factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount 1. Порядок полей должен по возможности совпадать с порядком полей в индексе (в противном случае SQL должен выполнить доп-работу) 2. Если у вас задействуется несколько индексов, то сначала укажите самый селективный (см. factureTrans) 3. Обратите внимание на проверку на модуль. Лучше сравните с константой, нежели поля таблиц. X++: where factureJour.FactureId == factureTrans.FactureId
&& factureJour.Module == module
&& factureJour.CustVendInvoiceAccount == this.InvoiceAccount Следует ли отсюда, что условие "&& factureJour.CustVendInvoiceAccount == this.InvoiceAccount" лишнее? |
|
07.09.2009, 17:51 | #13 |
Moderator
|
Ну, в общем случае из наличия на таблице первичного ключа и его использования в запросе еще не следует, что налагаемое доп. условие-фильтр является лишним
__________________
Андрей. |
|
08.09.2009, 07:53 | #14 |
Axapta Retail User
|
Dron AKA andy, а можете пояснить на примере когда это не так? Когда это доп условие - не лишнее - а то в голову ничего не приходит.
|
|
08.09.2009, 10:23 | #15 |
Moderator
|
Да вроде все очевидно из кода...
Заголовок счета-фактуры (factureJour) используется для наложения фильтра по плательщику. X++: factureJour.FactureId == factureTrans.FactureId X++: factureJour.Module == module X++: factureJour.CustVendInvoiceAccount == this.InvoiceAccount Если нужно наложить фильтр по контрагенту, то это условие не лишнее, вот и весь пример. Т.е. вернется пустая запись, если данных по текущему контрагенту нет.
__________________
Андрей. Последний раз редактировалось Dron AKA andy; 08.09.2009 в 10:25. |
|
08.09.2009, 10:49 | #16 |
Axapta Retail User
|
Но в данном конкретном запросе (я рассматриваю только его) выше есть строки
X++: while select RecId from markupTrans where markupTrans.TransRecId == this.RecId && markupTrans.TransTableId == this.TableId Так зачем потом еще фильтр по контрагенту? Ведь даже если мы накладные расходы разбили по нескольким фактурам (не знаю зачем) то контрагент то все равно один. Или все-таки бывают варианты?. |
|
08.09.2009, 11:54 | #17 |
Moderator
|
К своему стыду, не могу ничего сказать про связь фактур с накладными расходами "по жизни". А из запроса и свойств входящих в него таблиц неочевидно, например, что запись в табл. накладных расходов будет связана только с одной строкой конкретного счета-фактуры.
Я про вот эту связку: X++: where factureTrans.MarkupRefRecId == markupTrans.RecId
&& factureTrans.Module == module
&& factureTrans.FactureLineType == FactureLineType_RU::Markup
__________________
Андрей. |
|
10.09.2009, 07:28 | #18 |
Участник
|
Цитата:
Сообщение от ViV
Но в данном конкретном запросе (я рассматриваю только его) выше есть строки
X++: while select RecId from markupTrans where markupTrans.TransRecId == this.RecId && markupTrans.TransTableId == this.TableId
__________________
// no comments |
|
13.09.2009, 21:26 | #19 |
Участник
|
Цитата:
Сообщение от ViV
Вопрос про блок X++: where factureJour.FactureId == factureTrans.FactureId
&& factureJour.Module == module
&& factureJour.CustVendInvoiceAccount == this.InvoiceAccount Следует ли отсюда, что условие "&& factureJour.CustVendInvoiceAccount == this.InvoiceAccount" лишнее? Цитата:
Цитата:
Сообщение от mazzy Т.е. нужно делать примерно так
X++: // Header markups // SHiSHok.FRA_Optimize --> factureJour.clear(); while select RecId from markupTrans where markupTrans.TransRecId == this.RecId && markupTrans.TransTableId == this.TableId join RecId from factureTrans where factureTrans.MarkupRefRecId == markupTrans.RecId && factureTrans.Module == module && factureTrans.FactureLineType == FactureLineType_RU::Markup join * from factureJour index CustVendModuleIdx where factureJour.FactureId == factureTrans.FactureId && factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount Цитата:
Сообщение от SHiSHok
X++: DIS : // Header markups while select factureJour join RecId from factureTrans join RecId from markupTrans where factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount && factureTrans.FactureId == factureJour.FactureId && factureTrans.Module == module && factureTrans.FactureLineType == FactureLineType_RU::Markup && markupTrans.RecId == factureTrans.MarkupRefRecId && markupTrans.TransTableId == this.TableId && markupTrans.TransRecId == this.RecId { if ( ! ret.find(factureJour)) { ret.ins(factureJour); } } P.S. Мне не понятно - другое, в варианте после оптимизации : Цитата:
Сообщение от SHiSHok
X++: // Header markups // SHiSHok.FRA_Optimize --> factureJour.clear(); while select forceselectorder forcenestedloop forceplaceholders RecId from markupTrans join RecId from factureTrans join * from factureJour index CustVendModuleIdx where markupTrans.TransTableId == this.TableId && markupTrans.TransRecId == this.RecId && factureTrans.FactureLineType == FactureLineType_RU::Markup && factureTrans.MarkupRefRecId == markupTrans.RecId && factureJour.FactureId == factureTrans.FactureId && factureJour.Module == factureTrans.Module && factureJour.Module == module && factureJour.CustVendInvoiceAccount == this.InvoiceAccount { if ( ! ret.find(factureJour)) { ret.ins(factureJour); } } // SHiSHok.FRA_Optimize <-- X++: join * from factureJour index CustVendModuleIdx
__________________
Sergey Nefedov |
|
Теги |
axapta, faq, запрос (query), производительность |
|
|