14.03.2014, 00:34 | #1 |
MCITP
|
Некорректная работа 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 код:
Вероятно это как-то связано с 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 |
Участник
|
Скорее всего с order by и без него формируются разные планы запроса. Из за этого разное поведение.
А транзакция не спасает из за того что запись и чтение происходит в одной и той же транзакции. Для читающего запроса новые данные не являются грязными, т.к. порождены в той же самой транзакцией. Если целостность данных не сильно важна, то можно попробовать вставку записей делать через дополнительный Connection к БД. Только как бы блокировок не создать самому же себе. P.S.: Как должен работать такой запрос |
|
14.03.2014, 08:37 | #3 |
Участник
|
Интересно ещё как будет работать запрос если на таблице включить логирование транзакций и в запрос добавить условие
X++: && parmseq.createdTransactionId != appl.curTransactionId() |
|
14.03.2014, 10:01 | #4 |
MCITP
|
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 |
Участник
|
While select это не простая операция, в том смысле что она реализуется через дополнительные курсоры, доступ к которым в свою очередь обернут во вспомогательные хранимые процедуры. Все не просто и искать там логику сложно.
В общем, из данной истории вывод один: "Не руби сук на котором сидишь" в смысле "Не меняй таблицу которую фетчишь" |
|
14.03.2014, 11:48 | #6 |
MCITP
|
Цитата:
Достаточно включить трассировку запроса и увидеть что в БД уходит обычный нормальный SQL запрос, который должен вернуть соответствующие данные.
__________________
Zhirenkov Vitaly |
|
14.03.2014, 11:51 | #7 |
MCITP
|
Цитата:
Во вторых, если цепляться к словам, то exists join данные не фетчит
__________________
Zhirenkov Vitaly |
|
14.03.2014, 12:21 | #8 |
Участник
|
Цитата:
Оптимизация запросов: CustInvoiceTrans + markupTrans |
|
14.03.2014, 13:00 | #9 |
MCITP
|
Цитата:
Сообщение от S.Kuskov
Это вы на стороне БД видите? Трасировка, которая в аксапте не все показывает
Оптимизация запросов: CustInvoiceTrans + markupTrans Курсор или не курсор - запрос там один и тот же. И план выполнения запроса на результирующее множество никак влиять не должен, только на время выполнения. Если вы намекаете, что где-то там внутри он разбивает запрос на 2 запроса - то опять таки (это показано в джобе) это не должно влиять на результат, т.к. имеет место уникальность в данных.
__________________
Zhirenkov Vitaly |
|
14.03.2014, 14:28 | #10 |
Участник
|
Все что вы говорите вполне логично. Претензии вполне обоснованы. Я никоим образом не пытаюсь оправдать аксапту. Я пытаюсь её понять и простить. Нам же с ней ещё жить.
|
|
|
За это сообщение автора поблагодарили: ZVV (1). |