18.02.2009, 15:04 | #1 |
Участник
|
Query - getNo(n) - не видит данных
Пишу запрос.
В нем создаю много источников. Например, поставщик, договора, проводки. Проблема в том, что он видит getNo(3) данные, а вот getNo(2), getNo(1) - не видит. Т.е. он по какой - то причине теряет данные. Не могу понять в чем дело. Ниже примерная идея кода. X++: QueryBuildDataSource qbdsTable = query.addDataSource(tableNum()); QueryBuildDataSource qbds2Trans = qbdsTable.addDataSource(tableNum()); . . . qrunVend = new QueryRun(qryVend); while (qrunVend.next()) { _VendQ = qrunVend.getno(1); vendCode = _VendQ.AccountNum; _ContrQ = qrunVend.getno(2); .. } |
|
18.02.2009, 15:07 | #2 |
Ищущий знания...
|
а не проще использовать get ? наприме: _VendQ = qrunVend.get(tablennum(VendTable));
и можно посмотреть формирование самого запроса?
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
18.02.2009, 15:15 | #3 |
Боец
|
ой, а что это такое ?
X++: tableNum() |
|
18.02.2009, 15:21 | #4 |
Участник
|
В оригинале порядок следующий: Отбор поставщика - Отбор договора - Отбор проводок по поставщику - Отбор в ledgerTrans. На ledgerTrans ставлю суммирование.
qbdsLT.orderMode(OrderMode::GROUPBY); После этого он теряет связь с DS 1, 2, 3. Как только убираю группировку, то все работает. Как же этого избежать? |
|
18.02.2009, 15:29 | #5 |
Программатор
|
Цитата:
Сообщение от Arahnid
В оригинале порядок следующий: Отбор поставщика - Отбор договора - Отбор проводок по поставщику - Отбор в ledgerTrans. На ledgerTrans ставлю суммирование.
qbdsLT.orderMode(OrderMode::GROUPBY); После этого он теряет связь с DS 1, 2, 3. Как только убираю группировку, то все работает. Как же этого избежать? |
|
18.02.2009, 15:33 | #6 |
MCITP
|
Цитата:
Сообщение от Arahnid
В оригинале порядок следующий: Отбор поставщика - Отбор договора - Отбор проводок по поставщику - Отбор в ledgerTrans. На ledgerTrans ставлю суммирование.
qbdsLT.orderMode(OrderMode::GROUPBY); После этого он теряет связь с DS 1, 2, 3. Как только убираю группировку, то все работает. Как же этого избежать? У вас будут заполнены только те поля по которым вы группируете и суммируете, всё остальное не заполнено. Так что квери то работает, просто у вас в ваших остальных таблицах все поля пустые - скорее всего вам нужно добавлять необходимые поля в группировку, либо перечитывать их внутри цикла.
__________________
Zhirenkov Vitaly |
|
18.02.2009, 17:49 | #7 |
Administrator
|
Все гораздо проще.
Есть у датасорса в квери (объект QueryBuildDataSource) свойство fetchMode. По умолчанию оно имеет значение QueryFetchMode::One2Many. Но его можно поставить в значение QueryFetchMode::One2One. Смысл следующий. Допустим у нас есть запрос из двух таблиц - шапки и строк. Если fetchMode = QueryFetchMode::One2Many, то getNo(шапки) возвратит курсор шапки только один (первый) раз для всех строк данной шапки. Т.е. это будет выглядеть так: X++: while (queryRun.next()) { = queryRun.getNo(1); do { = queryRun.getNo(2); .................... // код } while (queryRun.next()); } Т.е. при каждом вызове queryRun.next() оба курсора будут заполнены данными. Я не знаю как это связано с производительностью и зачем такая логика. Но она (логика) заложена именно такая.
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 18.02.2009 в 18:11. |
|
|
За это сообщение автора поблагодарили: kashperuk (2), Logger (4), pbcio (0), AP-1055D (1). |
18.02.2009, 18:08 | #8 |
Участник
|
2 sukhanchik:
уж больно странная у вас в коде конструкция... |
|
|
За это сообщение автора поблагодарили: sukhanchik (1). |
18.02.2009, 18:12 | #9 |
Administrator
|
Спасибо, не заметил - подправил исходное сообщение
__________________
Возможно сделать все. Вопрос времени |
|
18.02.2009, 18:24 | #10 |
MCITP
|
Цитата:
Сообщение от sukhanchik
Все гораздо проще.
Есть у датасорса в квери (объект QueryBuildDataSource) свойство fetchMode. По умолчанию оно имеет значение QueryFetchMode::One2Many. Но его можно поставить в значение QueryFetchMode::One2One. Смысл следующий. Допустим у нас есть запрос из двух таблиц - шапки и строк. Если fetchMode = QueryFetchMode::One2Many, то getNo(шапки) возвратит курсор шапки только один (первый) раз для всех строк данной шапки. Т.е. это будет выглядеть так: X++: while (queryRun.next()) { = queryRun.getNo(1); do { = queryRun.getNo(2); .................... // код } while (queryRun.next()); } Т.е. при каждом вызове queryRun.next() оба курсора будут заполнены данными. Я не знаю как это связано с производительностью и зачем такая логика. Но она (логика) заложена именно такая. Я про ваше замечание про "странный эффект": X++: static void ZVVTestOne2OneMany(Args _args) { Query query = New Query(); QueryRun queryRun; QueryBuildDataSource qbds1; QueryBuildDataSource qbds2; CustTable custTable; RContractTable contractTable ; qbds1 = query.addDataSource(tableNum(CustTable)); qbds2 = qbds1.addDataSource(tableNum(RContractTable)); qbds2.relations(true); qbds2.fetchMode(QueryFetchMode::One2Many); qbds2.addRange(fieldnum(RContractTable,RContractPartnerCode)).value(queryValue("4100")); queryRun = New QueryRun(query); info(qbds1.toString()); while (queryRun.next()) { custTable = queryRun.getNo(1); contractTable = queryRun.getNo(2); info(strFmt("%1 %2", custTable.AccountNum, contractTable.RContractAccount)); } } X++: SELECT * FROM CustTable JOIN * FROM RContractTable WHERE 0 = RContractTable.RContractPartnerType AND CustTable.AccountNum = RContractTable.RContractPartnerCode AND (((RContractPartnerCode = '4100'))) 4100 0080 4100 560/1431 4100 0014 4100 0122 4100 0120 4100 0026 4100 0035 4100 0036 4100 1277/1431 4100 689/1441 UPD уточню: - QueryFetchMode::One2Many можно ставить и у первого и у обоих ДС - ничего не меняется - попробовал на 40сп2 - то же самое. Вероятно ваши данные с каких-то старых версий? Или чего в моём примере не хватает чтоб добиться вашего эффекта?
__________________
Zhirenkov Vitaly Последний раз редактировалось ZVV; 18.02.2009 в 18:35. |
|
18.02.2009, 23:35 | #11 |
Administrator
|
2Logger: Хм... А Вы ведь правы. Я прочел только первый пост, где ни слова не упоминалось про группировку. Тогда можно считать мой пост оффтопиком в данной теме
А по поводу джобика... Хм... Действительно отрабатывает. Но ведь помню - сталкивался! Лично на грабли наступал. На Ax 4.0 SP2. Найду ведь! Обязательно отпишу!
__________________
Возможно сделать все. Вопрос времени |
|
19.02.2009, 00:11 | #12 |
MCITP
|
Цитата:
Сообщение от sukhanchik
2Logger: Хм... А Вы ведь правы. Я прочел только первый пост, где ни слова не упоминалось про группировку. Тогда можно считать мой пост оффтопиком в данной теме
А по поводу джобика... Хм... Действительно отрабатывает. Но ведь помню - сталкивался! Лично на грабли наступал. На Ax 4.0 SP2. Найду ведь! Обязательно отпишу! А вообще найдите, обязательно, будет интересно... У меня есть кое-какие мысли по этому поводу, возможно поможет: дело в том что в режиме QueryFetchMode::One2Many Axapta иногда имеет свойство разбивать сложный запрос на несколько запросов. Т.е. вместо одного запроса с inner join на БД отсылается несколько запросов - один запрос к "шапке", и соответсвующие запросы к "строкам", примерно как "delay" на форме. Несколько раз на такое нарывался, из-за этого были проблемы с производительностью. Установка QueryFetchMode::One2One ситуацию "вылечивало". Но я на данный момент не готов сказать в каких именно случаях такое бывает, если вспомню и\или найду - отпишусь. Так вот есть подозрение что именно в таком случае и может иметь место описанная вами выше ситуёвина, т.к. на самом деле очередной "фетч" (внутри одной "шапки") из бд вытягивает только строку из таблицы-"дитёнка" а таблица-"шапка" при этом возможно как-то чистится... Но это только мои догадки, отвечать за свои слова пока не берусь... Модераторам - может в отдельную ветку?
__________________
Zhirenkov Vitaly |
|
19.02.2009, 09:31 | #13 |
Administrator
|
Извините, совсем плохим стал. Прошу великодушнейшего прощения.
Нашел тот запрос, в котором споткнулся в свое время с fetchMode. DAX 4.0 SP2 EE. Исходный запрос упростил для отображения работы именно fetchMode. Нюанс, с которым столкнулся вчера: сие не отрабатывает, если датасорса 2. Т.е. важно, чтобы датасорсов было больше двух. Итак: X++: static void vsuh_testFetchMode(Args _args) { CustTrans custTrans; CustTable custTable; Currency currency; Query query; QueryBuildDataSource qbds1, qbds2, qbds3; QueryRun qr; Counter cnt; ; query = new Query(); qbds1 = query.addDataSource(tablenum(CustTrans)); qbds2 = qbds1.addDataSource(tablenum(CustTable)); // qbds2.fetchMode(QueryFetchMode::One2One); qbds2.relations(true); qbds3 = qbds1.addDataSource(tablenum(Currency)); // qbds3.fetchMode(QueryFetchMode::One2One); qbds3.relations(true); qr = new QueryRun(query); while (qr.next() && cnt < 10) { custTrans = qr.getNo(1); custTable = qr.getNo(2); currency = qr.getNo(3); info(strfmt("%1-%2-%3!", custTrans.Voucher, custTable.AccountNum, currency.CurrencyCode)); cnt++; } } В таблице CustTrans ваучеры уникальные - т.е. в двух записях всегда присутствуют разные ваучеры. Итак, вариант 1 - как в джобе (fetchMode у всех QueryFetchMode::One2Many). вариант 2 - включаем fetchMode One2One у датасорса №2 вариант 3 - включаем fetchMode One2One у датасорса №3 и fetchMode One2Many у датасорса №2 вариант 4 - включаем fetchMode One2One у датасорсов №2 и №3 на датасорс №1 значение свойства fetchMode влияния не оказывает. Из примера - видно - что строки выбираются всегда - а "шапки" (в моем примере они подчинены строкам) - в зависимости от параметра fetchMode
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 19.02.2009 в 10:12. |
|
|
За это сообщение автора поблагодарили: ZVV (3). |
19.02.2009, 09:52 | #14 |
Участник
|
|
|
19.02.2009, 10:42 | #15 |
MCTS
|
Цитата:
Допустим у нас есть запрос из двух таблиц - шапки и строк. Если fetchMode = QueryFetchMode::One2Many, то getNo(шапки) возвратит курсор шапки только один (первый) раз для всех строк данной шапки.
|
|
19.02.2009, 11:52 | #16 |
MCITP
|
Да, спасибо за пример, он подтвердил высказанные мною выше подозрения:
(в вашем джобе добавил X++: info (qbds1.toString()); info (qbds2.toString()); info (qbds3.toString()); оба One2Many: X++: SELECT * FROM CustTrans SELECT * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum SELECT * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode SQL: (CustTrans) SELECT ... FROM CUSTTRANS A WHERE (SUBSTR(NLS_LOWER(DATAAREAID),1,7)=NLS_LOWER(:in1)) ORDER BY SUBSTR(NLS_LOWER(A.DATAAREAID),1,7),SUBSTR(NLS_LOWER(A.INVOICE),1,41),SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41) 000003_181 - - ! 000003_181 - 000001_045 - ! 000003_181 - - BYR ! CustTable One2One, Currency One2Many: X++: SELECT * FROM CustTrans JOIN * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum SELECT * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum SELECT * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode SQL: (CustTrans,CustTable) SELECT ... FROM CUSTTRANS A,CUSTTABLE B WHERE (SUBSTR(NLS_LOWER(A.DATAAREAID),1,7)=NLS_LOWER(:in1)) AND ((SUBSTR(NLS_LOWER(B.DATAAREAID),1,7)=NLS_LOWER(:in2)) AND (SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41)=SUBSTR(NLS_LOWER(B.ACCOUNTNUM),1,41))) ORDER BY SUBSTR(NLS_LOWER(A.DATAAREAID),1,7),SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41),A.TRANSDATE,SUBSTR(NLS_LOWER(A.VOUCHER),1,41) 000003_181 - 000001_045 - ! 000003_181 - 000001_045 - BYR ! CustTable One2Many, Currency One2One: X++: SELECT * FROM CustTrans JOIN * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode SELECT * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum SELECT * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode SQL: (CustTrans,Currency) SELECT ... FROM CUSTTRANS A,CURRENCY B WHERE (SUBSTR(NLS_LOWER(A.DATAAREAID),1,7)=NLS_LOWER(:in1)) AND ((SUBSTR(NLS_LOWER(B.DATAAREAID),1,7)=NLS_LOWER(:in2)) AND (SUBSTR(NLS_LOWER(A.CURRENCYCODE),1,7)=SUBSTR(NLS_LOWER(B.CURRENCYCODE),1,7))) ORDER BY SUBSTR(NLS_LOWER(A.DATAAREAID),1,7),SUBSTR(NLS_LOWER(A.INVOICE),1,41),SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41)0 00003_181 - - BYR ! 000003_181 - 000001_045 - BYR ! оба One2One: X++: SELECT * FROM CustTrans JOIN * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum JOIN * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode SELECT * FROM CustTable WHERE CustTrans.AccountNum = CustTable.AccountNum SELECT * FROM Currency WHERE CustTrans.CurrencyCode = Currency.CurrencyCode SQL: (CustTrans,CustTable,Currency) SELECT ... FROM CUSTTRANS A,CUSTTABLE B,CURRENCY C WHERE (SUBSTR(NLS_LOWER(A.DATAAREAID),1,7)=NLS_LOWER(:in1)) AND ((SUBSTR(NLS_LOWER(B.DATAAREAID),1,7)=NLS_LOWER(:in2)) AND (SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41)=SUBSTR(NLS_LOWER(B.ACCOUNTNUM),1,41))) AND ((SUBSTR(NLS_LOWER(C.DATAAREAID),1,7)=NLS_LOWER(:in3)) AND (SUBSTR(NLS_LOWER(A.CURRENCYCODE),1,7)=SUBSTR(NLS_LOWER(C.CURRENCYCODE),1,7))) ORDER BY SUBSTR(NLS_LOWER(A.DATAAREAID),1,7),SUBSTR(NLS_LOWER(A.ACCOUNTNUM),1,41),A.TRANSDATE,SUBSTR(NLS_LOWER(A.VOUCHER),1,41) 000003_181 - 000001_045 - BYR ! Насколько я понял, данный эффект происходит только в том случае, когда идёт непоследовательное соединение датасорсов! Только в этом случае Аксапта начинает "оптимизировать" (как в примере, естественно датасорсов при этом всегда больше двух, как вы верно подметили ) и тогда нужно либо указывать fetchMode=One2One, чтоб запрос к БД был один и тогда можно пользоваться стандартным циклом по квериРун, как обычно. Либо добавлять сложные конструкции с проверкой смены "папы" последством record.Changed(). Если же порядок соединения последовательный, то fetchMode никакого влияния не оказывает, можете сами проверить поменяв местами CustTable & CustTrans и прицепив, соответсвенно, Currency ко второму DS. Вот! Так что будьте осторожны и учитывайте данную особенность! ЗЫ Ещё из интересных особенностей данного поведения: Аксаптовский SQL-Trace не ловит подчинённые запросы к БД при такой оптимизации. Их можно увидеть только в трэйсе на уровне БД...
__________________
Zhirenkov Vitaly |
|
|
За это сообщение автора поблагодарили: sukhanchik (7). |
Теги |
query, queryrun, выборка, запрос (query) |
|
|