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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 14.03.2014, 00:34   #1  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
? Некорректная работа notExists join
Всем привет!

Как такое может быть?
(Извиняюсь что много текста, просто показываю все варианты что я перепробовал).

X++:
static void Job69(Args _args)
{
    AM_SalesParmBatchSeq    parmseq, invSeq;
    AM_SalesBAtchSeq        salesSeq;

    InventTransid  _inventTransid = "LT01125843";
    ParmId         _parmId        = "01354429_sys";

    int cnt;
    ;

    ttsBegin;

    delete_from invSeq
        where invSeq.parmid         == _parmid;

    select count(recId) from salesSeq
        where salesSeq.InventTransId == _inventTransid
            && !salesSeq.InvoiceId
            notexists join parmseq
            where parmseq.InventTransId == salesSeq.InventTransId
            && parmseq.InventBatchId == salesSeq.InventBatchId
            && parmseq.sequenceNum  == salesSeq.sequenceNum
            && parmseq.ParmId   == _parmid;
            
    info(int2str(salesSeq.RecId)); // 184
    
    While select salesSeq
        where salesSeq.InventTransId == _inventTransid
            && !salesSeq.InvoiceId
            notexists join parmseq
            where parmseq.InventTransId == salesSeq.InventTransId
            && parmseq.InventBatchId == salesSeq.InventBatchId
            && parmseq.sequenceNum  == salesSeq.sequenceNum
            && parmseq.ParmId   == _parmid
    {
            cnt++;
    }

    info(int2str(cnt)); // 184
    cnt = 0;

    While select salesSeq
        order by InventBatchId, SequenceNumber // !!!
        where salesSeq.InventTransId == _inventTransid
            && !salesSeq.InvoiceId
            notexists join parmseq
            where parmseq.InventTransId == salesSeq.InventTransId
            && parmseq.InventBatchId == salesSeq.InventBatchId
            && parmseq.sequenceNum  == salesSeq.sequenceNum
            && parmseq.ParmId   == _parmid
    {
            invSeq.clear();
            invSeq.InventTransId  = _inventTransid;
            invSeq.InventBatchId  = salesseq.InventBatchId;
            invSeq.SequenceNumber = salesseq.SequenceNumber;
            invSeq.parmid         = _parmid;
            invSeq.insert();

            cnt++;
    }

    info(int2str(cnt)); // 184
    cnt = 0;
    
    delete_from invSeq
        where invSeq.parmid         == _parmid;
    
    While select salesSeq
        where salesSeq.InventTransId == _inventTransid
            && !salesSeq.InvoiceId
    {
        select firstonly parmseq
            where parmseq.InventTransId == salesSeq.InventTransId
            && parmseq.InventBatchId == salesSeq.InventBatchId
            && parmseq.sequenceNum  == salesSeq.sequenceNum
            && parmseq.ParmId   == _parmid;
            
        if (! parmSeq)
        {
            invSeq.clear();
            invSeq.InventTransId  = _inventTransid;
            invSeq.InventBatchId  = salesseq.InventBatchId;
            invSeq.SequenceNumber = salesseq.SequenceNumber;
            invSeq.parmid         = _parmid;
            invSeq.insert();

            cnt++;
        }
    }

    info(int2str(cnt)); // 184
    cnt = 0;

    delete_from invSeq
        where invSeq.parmid         == _parmid;

    While select salesSeq
        // order by InventBatchId, SequenceNumber // !!!
        where salesSeq.InventTransId == _inventTransid
            && !salesSeq.InvoiceId
            notexists join parmseq
            where parmseq.InventTransId == salesSeq.InventTransId
            && parmseq.InventBatchId == salesSeq.InventBatchId
            && parmseq.sequenceNum  == salesSeq.sequenceNum
            && parmseq.ParmId   == _parmid
    {
            invSeq.clear();
            invSeq.InventTransId  = _inventTransid;
            invSeq.InventBatchId  = salesseq.InventBatchId;
            invSeq.SequenceNumber = salesseq.SequenceNumber;
            invSeq.parmid         = _parmid;
            invSeq.insert();

            cnt++;
    }

    ttsAbort;

    info(int2str(cnt)); // 100 (или 70)
}
PHP код:
Message (11:22:17 pm)
184
184
184
184
100 
Т.е. последний цикл каким-то образом не хочет видеть ещё 84 записи.
Вероятно это как-то связано с SQL сервером, но у меня идеи закончились уже куда смотреть.
Может кто-то сталкивался?

DAX 2009 5.0.1500.1670
SQL Server 2008 10.50.2500

Уровень изоляции - как положено, read_committed_snapshot.

Причём, пока я не поигрался немного с индексом (а именно прописал а потом удалил кластерный индекс у AM_SalesParmBatchSeq таблицы в AOT) вместо 100 в последнем цикле выдавалось 70.

Пробовал воспроизвести на новых упрощённых таблицах - не получилось.

Спасибо!
__________________
Zhirenkov Vitaly
Старый 14.03.2014, 08:20   #2  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Скорее всего с order by и без него формируются разные планы запроса. Из за этого разное поведение.

А транзакция не спасает из за того что запись и чтение происходит в одной и той же транзакции. Для читающего запроса новые данные не являются грязными, т.к. порождены в той же самой транзакцией. Если целостность данных не сильно важна, то можно попробовать вставку записей делать через дополнительный Connection к БД. Только как бы блокировок не создать самому же себе.

P.S.: Как должен работать такой запрос
Старый 14.03.2014, 08:37   #3  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Интересно ещё как будет работать запрос если на таблице включить логирование транзакций и в запрос добавить условие
X++:
&& parmseq.createdTransactionId != appl.curTransactionId()
Старый 14.03.2014, 10:01   #4  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
->
2 S.Kuskov

Кстати, да, если сделать через insert_recordset, то опять будет 184 записи вставлено.
И это нормально.

По поводу приведённой ссылки - я её видел раньше и в ней отписывался.
И опять таки не считаю, что должно быть по-другому.

Т.е. сначала должен отработать внешний запрос и получить результирующее множество. И изменение записей в уже в этом цикле не должно оказывать никакого влияния на это результирующее множество - т.е. по сути мы должны получать их из "снэпшота".

Более того, что касается конкретно данного случая - то там значения комбинации InventTransId, InventBatchId, SequenceNumber в выбранных данных - уникально!
Поэтому даже если б и учитывались данные вставленные внутри цикла, то всё равно результат не должен быть таким и сортировка не может на него повлиять.

Может я, конечно, всё ещё живу Ораклом, где такое в принципе невозможно.
Но такое поведение является источником непредсказуемости поведения системы на разных данных и просто рвёт все мои шаблоны...

Upd: Кстати говоря, проблемы в этом месте были замечены только для случаев когда кол-во попадающих записей в запрос превышает 100.
Если меньше 100, то вроде как (говорят) всегда отрабатывает корректно.
__________________
Zhirenkov Vitaly

Последний раз редактировалось ZVV; 14.03.2014 в 10:10.
Старый 14.03.2014, 11:29   #5  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
While select это не простая операция, в том смысле что она реализуется через дополнительные курсоры, доступ к которым в свою очередь обернут во вспомогательные хранимые процедуры. Все не просто и искать там логику сложно.

В общем, из данной истории вывод один:
"Не руби сук на котором сидишь" в смысле "Не меняй таблицу которую фетчишь"
Старый 14.03.2014, 11:48   #6  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
?
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
While select это не простая операция, в том смысле что она реализуется через дополнительные курсоры, доступ к которым в свою очередь обернут во вспомогательные хранимые процедуры. Все не просто и искать там логику сложно.
Откуда такой вывод?
Достаточно включить трассировку запроса и увидеть что в БД уходит обычный нормальный SQL запрос, который должен вернуть соответствующие данные.
__________________
Zhirenkov Vitaly
Старый 14.03.2014, 11:51   #7  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
В общем, из данной истории вывод один:
"Не руби сук на котором сидишь" в смысле "Не меняй таблицу которую фетчишь"
Ну во-первых подобных примеров множество я думаю можно найти даже в стандартном коде.
Во вторых, если цепляться к словам, то exists join данные не фетчит
__________________
Zhirenkov Vitaly
Старый 14.03.2014, 12:21   #8  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от ZVV Посмотреть сообщение
Откуда такой вывод?
Достаточно включить трассировку запроса и увидеть что в БД уходит обычный нормальный SQL запрос, который должен вернуть соответствующие данные.
Это вы на стороне БД видите? Трасировка, которая в аксапте не все показывает

Оптимизация запросов: CustInvoiceTrans + markupTrans
Старый 14.03.2014, 13:00   #9  
ZVV is offline
ZVV
MCITP
MCP
Oracle
MCBMSS
 
1,006 / 246 (11) ++++++
Регистрация: 13.02.2004
Адрес: Минск
->
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Это вы на стороне БД видите? Трасировка, которая в аксапте не все показывает

Оптимизация запросов: CustInvoiceTrans + markupTrans
Честно говоря я не понимаю что вы хотите мне сказать?
Курсор или не курсор - запрос там один и тот же. И план выполнения запроса на результирующее множество никак влиять не должен, только на время выполнения.

Если вы намекаете, что где-то там внутри он разбивает запрос на 2 запроса - то опять таки (это показано в джобе) это не должно влиять на результат, т.к. имеет место уникальность в данных.
__________________
Zhirenkov Vitaly
Старый 14.03.2014, 14:28   #10  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Все что вы говорите вполне логично. Претензии вполне обоснованы. Я никоим образом не пытаюсь оправдать аксапту. Я пытаюсь её понять и простить. Нам же с ней ещё жить.
За это сообщение автора поблагодарили: ZVV (1).
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
AX2009. Не используйте NotExists Wamr DAX: Программирование 7 16.09.2020 21:45
Вложенные Notexists Join в запросе McArrow DAX: Программирование 4 13.02.2014 12:53
addicted2ax: Dynamics AX 2012 & SQL Server 2008R2: Cross Join vs. Inner Join – Houston we have *NO* Problem Blog bot DAX Blogs 0 20.12.2012 20:11
Не работают 2 notexists join savel DAX: Программирование 13 23.07.2004 13:49
Некорректная работа сопоставления проводок Garic DAX: Функционал 5 23.04.2003 16:45

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

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

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