AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 16.08.2007, 17:58   #1  
SHiSHok is offline
SHiSHok
Участник
Аватар для SHiSHok
Дети Юза
 
219 / 103 (4) +++++
Регистрация: 28.07.2005
Адрес: Донецк
Post оптимизируем запросы.
если у вас тормоза при нажатии кнопочки запросы в шапках (заказо/закупок..) то предлагаю следующее:
есть такой метод на карте (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);
            }
        }
в запросах вся надежда на сиквел (как бы правильно), но вот сиквел на нашей базе начал интересный план придумывать : оба запроса начинаются с выборки по factureTrans, что чем дальше - тем больше выражается в задержках.
Пришлось явно сиквелу сказать что делать: для первого запроса наилучшую селективность обеспечивает 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  
egorych is offline
egorych
Участник
Самостоятельные клиенты AX
Oracle
 
761 / 154 (7) ++++++
Регистрация: 09.11.2006
Адрес: Краснодарский край
ИМХО лучше не
X++:
index CustVendModuleIdx
а
X++:
index hint CustVendModuleIdx
ибо судя по доке просто index используется для сортировки (типа вместо order by ), я так думаю остался от древних времен
Старый 16.08.2007, 19:29   #3  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
join RecId для неиспользуемых таблиц есть смысл заменить на join TableId
__________________
-ТСЯ или -ТЬСЯ ?
Старый 16.08.2007, 20:23   #4  
SHiSHok is offline
SHiSHok
Участник
Аватар для SHiSHok
Дети Юза
 
219 / 103 (4) +++++
Регистрация: 28.07.2005
Адрес: Донецк
Цитата:
Сообщение от Vadik Посмотреть сообщение
join RecId для неиспользуемых таблиц есть смысл заменить на join TableId
разница есть, но небольшая:
эти 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  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от SHiSHok Посмотреть сообщение
но вот сиквел на нашей базе начал интересный план придумывать
Дык, и разберитесь с сиквелом.

Цитата:
Сообщение от SHiSHok Посмотреть сообщение
Пришлось явно сиквелу сказать что делать: для первого запроса наилучшую селективность обеспечивает MARKUPTRANS, для второго карта CUSTINVOICETRANS - имеем:
Во-первых, с чего вы взяли, что так будет всегда и у всех.
Во-вторых, явные хинты требуют постоянного административного внимания.
В-третьих, с какой стати у вас сиквел сам этого не понимает? Разберитесь именно с этим.


В-четвертых, где-то в блогах, по-моему, Еременко писал, что условия после всех 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. Обратите внимание на проверку на модуль. Лучше сравните с константой, нежели поля таблиц

В общем, разберитесь с тем как работает запрос в Аксапте и что происходит в момент выполнения.
Перестаньте насиловать Аксапту и СКЛ. Помните: ласку, чистоту и смазку.
__________________
полезное на axForum, github, vk, coub.
Старый 17.08.2007, 00:57   #6  
SHiSHok is offline
SHiSHok
Участник
Аватар для SHiSHok
Дети Юза
 
219 / 103 (4) +++++
Регистрация: 28.07.2005
Адрес: Донецк
Цитата:
Сообщение от mazzy Посмотреть сообщение
Во-первых, с чего вы взяли, что так будет всегда и у всех.
Во-вторых, явные хинты требуют постоянного административного внимания.
В-третьих, с какой стати у вас сиквел сам этого не понимает? Разберитесь именно с этим.
В-четвертых, где-то в блогах, по-моему, Еременко писал, что условия после всех join'ов в Аксапте работает медленнее, чем условия внутри.
1) думаю будет:
- запрос состоит из 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 слоя)

Цитата:
Сообщение от mazzy Посмотреть сообщение
Про recId и TableId тоже писали. Суть в том, что TableId - аксаптовское поле, не хранимое в базе СКЛе. Это значит, что использование TableId может привести к тому, что таблица исчезнет из SQL-запроса.
это я и пытался довести в сообщении о счетчике сиквела 'page lookups/sec', а вот таблица из запроса никуда не исчезнет (с чего бы ей исчезать из inner join-ов)
Цитата:
Сообщение от mazzy Посмотреть сообщение
Обратите внимание на порядок полей в условиях.
1. Порядок полей должен по возможности совпадать с порядком полей в индексе (в противном случае SQL должен выполнить доп-работу)
учту.

Цитата:
Сообщение от mazzy Посмотреть сообщение
2. Если у вас задействуется несколько индексов, то сначала укажите самый селективный (см. factureTrans)
не совсем понял.

Цитата:
Сообщение от mazzy Посмотреть сообщение
3. Обратите внимание на проверку на модуль. Лучше сравните с константой, нежели поля таблиц
может быть - сиквел подобную подстановку сам делает.
__________________
--- SHiSHok

Последний раз редактировалось SHiSHok; 17.08.2007 в 01:05.
Старый 17.08.2007, 01:41   #7  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от SHiSHok Посмотреть сообщение
- запрос состоит из inner join-ов, т.о. отсутствие записи в любой таблице ведет к пустому запросу
Ну и что?
Если таблица markupTrans содержит значительно больше записей, чем таблица FatureTrans, то почему надо начинать именно с MarkupTrans?

Цитата:
Сообщение от SHiSHok Посмотреть сообщение
2) отсутствие хинтов,на мой взгляд, требует не менее пристального внимания DBA
Но при правильной настройке статистики сервер сам сможет имзенить план.
В отличие от явно заданных хинтов.
При явно заданных хинтах сервер будет использовать даже неоптимальный явно заданный план.
См. типичный пример http://axapta.mazzy.ru/lib/querytuning/

Цитата:
Сообщение от SHiSHok Посмотреть сообщение
4) буду знать, даже визуально приятнее читать условия к конкретному join (в общем то следовал схеме запроса DIS слоя)
Это не "визуально приятнее".
Это особенность Аксапты. В dis-слое писали неоптимально.
Об этом писал Еременко, по-моему, в своем блоге (торможу и не могу сейчас найти)

Цитата:
Сообщение от SHiSHok Посмотреть сообщение
это я и пытался довести в сообщении о счетчике сиквела 'page lookups/sec', а вот таблица из запроса никуда не исчезнет (с чего бы ей исчезать из inner join-ов)
Оптимизатор, блн.
Еще как может исчезнуть. Аксапта может разбить один запрос на несколько вложенных если в середине используется временная таблица или нет полей для выборки.

Например,
select table1 where ...
join table2 where ...
join table3 where ...

ЕСЛИ table2
1. привязана к незакупленному лицензионному ключу
2. или выключенному конфигурационному
3. или является временной
ТО на sql пойдет не один запрос, а несколько вложенных.

В некоторых сервис-паках подобные "оптимизации" выполнялись и для таблиц, для которых не выбирается ни одно поле.

То, что у вас таблица осталась в запросе еще ни о чем не говорит
Чтобы таблица гарантировано попала в SQL запрос, запрос по ней должен содержать хотя бы одно хранимое на SQL поле.
Поэтому tableId использовать опасно. TableId - не хранимое поле.

Цитата:
Сообщение от SHiSHok Посмотреть сообщение
может быть - сиквел подобную подстановку сам делает.
Нет, не делает.
__________________
полезное на axForum, github, vk, coub.
За это сообщение автора поблагодарили: belugin (4).
Старый 17.08.2007, 03:14   #8  
glibs is offline
glibs
Member
Сотрудники компании It Box
Most Valuable Professional
Лучший по профессии 2011
Лучший по профессии 2009
 
4,942 / 911 (40) +++++++
Регистрация: 10.06.2002
Адрес: I am from Kyiv, Ukraine. Now I am in Moscow. For private contacts: glibs@hotmail.com
Цитата:
Сообщение от 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 поле.
...
А наличие чего-то в where разве этого не гарантирует?

А если в твоем примере
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 - не хранимое поле.
...
Если по таблице необходимо наложить условие в where, то почему бы и нет?

Я поддерживаю участника Vadik в этом вопросе.

SHiSHok, как говорил... все тот же Vadik, когда я начинал учиться писать запросы в Аксапте, "* from" можно не писать.
__________________
С уважением,
glibs®
За это сообщение автора поблагодарили: zemlyn (1).
Старый 17.08.2007, 09:53   #9  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Цитата:
Сообщение от SHiSHok Посмотреть сообщение
4) буду знать, даже визуально приятнее читать условия к конкретному join (в общем то следовал схеме запроса DIS слоя)
Оформление данного запроса - не тот стиль, которого стоит придерживаться. Он трудно читается, в нем трудно искать ошибки при отладке
SQL Style - FROM x,y,z or INNER JOIN; We are all Cowboy Coders

На производительности запроса с использованием только INNER JOIN и AND-условий такое оформление не сказывается никак (оптимизатор это щелкает как орехи), скорее неряшливость в оформлении кода. "Выстрелить" это может при использовании EXIST и NOTEXIST JOIN

Цитата:
Сообщение от mazzy Посмотреть сообщение
Это особенность Аксапты. В dis-слое писали неоптимально.
Об этом писал Еременко, по-моему, в своем блоге
действительно, куда же еще это писать, если не в блог

Цитата:
Сообщение от mazzy Посмотреть сообщение
Аксапта может разбить один запрос на несколько вложенных если в середине используется временная таблица или нет полей для выборки.
Например,
select table1 where ...
join table2 where ...
join table3 where ...
ЕСЛИ table2
1. привязана к незакупленному лицензионному ключу
2. или выключенному конфигурационному
3. или является временной
ТО на sql пойдет не один запрос, а несколько вложенных
по-моему, ты что-то с чем-то путаешь.. в любом случае, при отключенной table2 что один запрос, что несколько - приводят к пустой выборке
__________________
-ТСЯ или -ТЬСЯ ?
За это сообщение автора поблагодарили: George Nordic (2), zemlyn (1).
Старый 17.08.2007, 10:15   #10  
SHiSHok is offline
SHiSHok
Участник
Аватар для SHiSHok
Дети Юза
 
219 / 103 (4) +++++
Регистрация: 28.07.2005
Адрес: Донецк
Цитата:
Сообщение от mazzy Посмотреть сообщение
Ну и что?
Если таблица markupTrans содержит значительно больше записей, чем таблица FatureTrans, то почему надо начинать именно с MarkupTrans?
потому что в описанной ситуации из FaсtureTrans_RU выберется множество записей с заданными FACTURELINETYPE и MODULE (минимум 1) и по КАЖДОЙ из них будет сделан lookup из MARKUPTRANS по полю MARKUPREFRECID. Только в этом случае у нас будет не менее одного обращения к большой MARKUPTRANS , а в случае с ведущей таблицей MARKUPTRANS - гарантировано 1 index seek. Я так думаю
Единственное когда план плох - это когда в FaсtureTrans_RU по условию (FACTURELINETYPE, MODULE) вообще нет записей и таблица намного меньше MARKUPTRANS. Но надо посмотреть код - может при таких условиях код не будет исполнятся и вовсе.

Цитата:
Сообщение от mazzy Посмотреть сообщение
Оптимизатор, блн.
да уж приходится. на базе под 100Gb мнооого чего вылезает...

Цитата:
Сообщение от mazzy Посмотреть сообщение
Нет, не делает.
поправочка: в сиквел передается условие 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
план не изменился - сиквел идет в первую очередь по FACTURETRANS_RU (11млн. записей),а не по MARKUPTRANS (2млн. записей). Но это уже по свободе на sql.ru схожу.

ЗЫ: Я не навязываю своего решения никому!
__________________
--- SHiSHok

Последний раз редактировалось SHiSHok; 17.08.2007 в 10:19.
За это сообщение автора поблагодарили: belugin (4).
Старый 17.08.2007, 10:22   #11  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Про 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  
ViV is offline
ViV
Axapta Retail User
Самостоятельные клиенты AX
Axapta Retail User
 
200 / 79 (3) ++++
Регистрация: 14.09.2005
Цитата:
Сообщение от 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_RU индекс FactureIdx (FactureId и Module) уникальный.
Следует ли отсюда, что условие "&& factureJour.CustVendInvoiceAccount == this.InvoiceAccount" лишнее?
Старый 07.09.2009, 17:51   #13  
Dron AKA andy is offline
Dron AKA andy
Moderator
 
944 / 253 (10) ++++++
Регистрация: 27.03.2002
Адрес: Москва
Ну, в общем случае из наличия на таблице первичного ключа и его использования в запросе еще не следует, что налагаемое доп. условие-фильтр является лишним
__________________
Андрей.
Старый 08.09.2009, 07:53   #14  
ViV is offline
ViV
Axapta Retail User
Самостоятельные клиенты AX
Axapta Retail User
 
200 / 79 (3) ++++
Регистрация: 14.09.2005
Dron AKA andy, а можете пояснить на примере когда это не так? Когда это доп условие - не лишнее - а то в голову ничего не приходит.
Старый 08.09.2009, 10:23   #15  
Dron AKA andy is offline
Dron AKA andy
Moderator
 
944 / 253 (10) ++++++
Регистрация: 27.03.2002
Адрес: Москва
Да вроде все очевидно из кода...
Заголовок счета-фактуры (factureJour) используется для наложения фильтра по плательщику.
X++:
factureJour.FactureId == factureTrans.FactureId
- связка со строками фактуры,
X++:
factureJour.Module    == module
- фильтр по модулю (клиенты, поставщики etc),
X++:
factureJour.CustVendInvoiceAccount == this.InvoiceAccount
- фильтр по контрагенту.
Если нужно наложить фильтр по контрагенту, то это условие не лишнее, вот и весь пример. Т.е. вернется пустая запись, если данных по текущему контрагенту нет.
__________________
Андрей.

Последний раз редактировалось Dron AKA andy; 08.09.2009 в 10:25.
Старый 08.09.2009, 10:49   #16  
ViV is offline
ViV
Axapta Retail User
Самостоятельные клиенты AX
Axapta Retail User
 
200 / 79 (3) ++++
Регистрация: 14.09.2005
:(
Но в данном конкретном запросе (я рассматриваю только его) выше есть строки
X++:
while select RecId from markupTrans
    where markupTrans.TransRecId             == this.RecId
       && markupTrans.TransTableId           == this.TableId
где мы четко по номеру таблицы и RecId находим запись в таблице накладных расходов (она привязана четко к одному документу но разным строкам - что нам не важно). Потом по ней строку фактуры (по RecID - опять четкая увязка к документу). А двух контрагентов в одном документе быть никак не может.
Так зачем потом еще фильтр по контрагенту? Ведь даже если мы накладные расходы разбили по нескольким фактурам (не знаю зачем) то контрагент то все равно один. Или все-таки бывают варианты?.
Старый 08.09.2009, 11:54   #17  
Dron AKA andy is offline
Dron AKA andy
Moderator
 
944 / 253 (10) ++++++
Регистрация: 27.03.2002
Адрес: Москва
К своему стыду, не могу ничего сказать про связь фактур с накладными расходами "по жизни". А из запроса и свойств входящих в него таблиц неочевидно, например, что запись в табл. накладных расходов будет связана только с одной строкой конкретного счета-фактуры.
Я про вот эту связку:
X++:
where factureTrans.MarkupRefRecId        == markupTrans.RecId
       && factureTrans.Module == module
       && factureTrans.FactureLineType       == FactureLineType_RU::Markup
Связь в ней "один ко многим".
__________________
Андрей.
Старый 10.09.2009, 07:28   #18  
dech is offline
dech
Участник
Аватар для dech
Самостоятельные клиенты AX
 
643 / 347 (13) ++++++
Регистрация: 25.06.2009
Адрес: Омск
Записей в блоге: 3
Цитата:
Сообщение от ViV Посмотреть сообщение
Но в данном конкретном запросе (я рассматриваю только его) выше есть строки
X++:
while select RecId from markupTrans
    where markupTrans.TransRecId             == this.RecId
       && markupTrans.TransTableId           == this.TableId
где мы четко по номеру таблицы и RecId находим запись в таблице накладных расходов
Сколько записей найдётся у join'ов, столько записей markupTrans'а вы и получите. А то, что в уникальном индексе присутствует еще дополнительное условие, как уже написал Dron AKA andy, означает, что у нас либо есть запись с таким контрагентом, либо её нет вообще.
__________________
// no comments
Старый 13.09.2009, 21:26   #19  
SRF is offline
SRF
Участник
MCBMSS
Axapta Retail User
 
365 / 542 (19) +++++++
Регистрация: 08.08.2007
Записей в блоге: 1
Цитата:
Сообщение от ViV Посмотреть сообщение

Вопрос про блок
X++:
 where factureJour.FactureId              == factureTrans.FactureId
        && factureJour.Module                 == module
        && factureJour.CustVendInvoiceAccount == this.InvoiceAccount
На таблице FactureJour_RU индекс FactureIdx (FactureId и Module) уникальный.
Следует ли отсюда, что условие "&& factureJour.CustVendInvoiceAccount == this.InvoiceAccount" лишнее?
Цитата:
Сообщение от ViV Посмотреть сообщение
...
Так зачем потом еще фильтр по контрагенту? Ведь даже если мы накладные расходы разбили по нескольким фактурам (не знаю зачем) то контрагент то все равно один. Или все-таки бывают варианты?.
Данное условие не совсем лишнее, конечно, если для вашего сервера СУБД лучше использовать запрос(т.е. склеивать таблицы в ниже приведенном порядке), то, наверное, можно обойтись и без него:
Цитата:
Сообщение от 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
Индекса CustVendModuleIdx - нет в стандарте, судя по наименованию, содержит пару полей (CustVendInvoiceAccount, Module). Так вот, если, склеивать таблицы строго в порядке MarkupTrans, FactureTrans, FactureJour, то его использование мне кажется бесполезным(смысл сортировки по двум указанным полям не понятен, поскольку для всех отобранных строк эти значения одинаковы). Если же порядок не указывать, то вполне вероятно, что использование данного индекса может дать прирост производительности, но это уже, нужно проверять на реальных данных.
__________________
Sergey Nefedov
Теги
axapta, faq, запрос (query), производительность

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Производство. Запросы\Развертывание\Обработка не дает нужный результат e@gle DAX: Функционал 11 11.05.2007 18:10
Сложные запросы в RLS Ruff DAX: Администрирование 12 30.08.2005 18:02
Запросы в Аксапта ibc DAX: Программирование 5 08.08.2005 22:47
Разные запросы в 2-х и 3-х уровневой конфигурациях. Что делать?! Anais DAX: Программирование 12 04.11.2004 12:47
Сложные while select-запросы или вложенные циклы Atani DAX: Программирование 10 03.02.2004 13:46

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 17:44.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.