|
12.02.2007, 18:17 | #1 |
Участник
|
Глюки в Query с разными типами Join (в т.ч. NonExistsJoin) к одной таблице
Есть несколько компаний, которые "торгуют" между собой через intercompany, и внешние контрагенты, которые торгуют с этими компаниями. Необходимо вывести в отчет проводки только по этим внешним контрагентам, сгруппировав проводки по одному и тому же контрагенту в разных компаниях по внешнему коду. Т.е. внешний контрагент может в разных компаниях называться по-разному, но внешний код у него везде будет одинаковый. Так вот, чтобы отфильтровать контрагентов из справочника поставщиков, которые завязаны на внутренние компании, решил сделать, если упрощенно, примерно следующее:
PHP код:
X++: static void testJoins(Args _args) { VendTrans vt; VendTable vtbl; ExtCodeValueTable extcvt; QueryRun qr; Query query = new Query(); QueryBuildDataSource qbds, qbdsT; void joinGatewayOrg() { // filter out intercompany contragents qbds = qbdsT.addDataSource(tablenum(GatewayOrgRef)); qbds.joinMode(JoinMode::NoExistsJoin); qbds.fetchMode(QueryFetchMode::One2One); qbds.addLink(fieldnum(VendTrans, AccountNum), fieldnum(GatewayOrgRef, RefId)); qbds.addRange(fieldnum(GatewayOrgRef, RefType)).value(queryValue(SysActionRefTypes::Vend)); qbds = qbds.addDataSource(tablenum(GatewayOrganization)); qbds.fetchMode(QueryFetchMode::One2One); qbds.addLink(fieldnum(GatewayOrgRef, GatewayOrgId), fieldnum(GatewayOrganization, GatewayOrgId)); qbds.addRange(fieldnum(GatewayOrganization, InterCompanyOrg)).value(queryValue(NoYes::Yes)); qbds.addRange(fieldnum(GatewayOrganization, Active)).value(queryValue(NoYes::Yes)); } void joinExtCodeValue() { ExtCodeId extCodeId; ; extCodeId = (select firstonly ExtCodeId from ExtCodeTable where ExtCodeTable.ExtCodeTableId == tablenum(VendTable)).extcodeId; info(strfmt(@"For table '%1' ExtCodeId = '%2'", tablestr(VendTable), extCodeId)); // join vendtable p on v.accountnum = p.accountnum qbds = qbdsT.addDataSource(tablenum(VendTable)); qbds.joinMode(JoinMode::InnerJoin); qbds.fetchMode(QueryFetchMode::One2One); qbds.relations(false); qbds.addLink(fieldnum(VendTrans, AccountNum), fieldnum(VendTable, AccountNum)); // join extcodevaluetable e on p.tableid = e.extCodeRelationTableId and // p.recid = e.extCodeRelationRecId and e.extcodeid = ExtCodeId qbds = qbds.addDataSource(tablenum(ExtCodeValueTable)); qbds.fetchMode(QueryFetchMode::One2One); qbds.addLink(fieldnum(VendTable, RecId), fieldnum(ExtCodeValueTable, ExtCodeRelationRecId)); qbds.addRange(fieldnum(ExtCodeValueTable, ExtCodeRelationTableId)).value(queryValue(tablenum(VendTable))); qbds.addRange(fieldnum(ExtCodeValueTable, ExtCodeId)).value(extCodeId); } ; // select * from vendtrans v qbdsT = query.addDataSource(tablenum(VendTrans)); qbdsT.addSortField(fieldnum(VendTrans, AccountNum)); qbdsT.addSortField(fieldnum(VendTrans, TransDate)); if (Box::yesNo(@"NonExists join first?", DialogButton::Yes) == DialogButton::Yes) { joinGatewayOrg(); joinExtCodeValue(); } else { joinExtCodeValue(); joinGatewayOrg(); } qr = new QueryRun(query); while(qr.next()) { vt = qr.get(tablenum(VendTrans)); vtbl = qr.get(tablenum(VendTable)); extcvt = qr.get(tablenum(ExtCodeValueTable)); info(strfmt("%1 %2 name='%3', extCode='%4' %5", vt.TransDate, vt.AccountNum, vtbl.Name, extcvt.ExtCodeValue, (vt.Invoice ? vt.Invoice : vt.Txt))); } } Axapta 3.0 SP5 KR2, SQL Server 2005 SP1 |
|
12.02.2007, 21:44 | #2 |
Участник
|
Цитата:
1. Может стоит накидать запрос в АОТ мышкой, а в коде написать X++: Query q = new Query(querystr(mySuperQuery)) 2. Вместо одного суперзапроса в многопользовательской системе лучше делать несколько небольших. См. aEremenko: Ресурс заблокирован, ждите... 3. Ваш select не эквивалентен Query. В select вы получаете выборку из одного большого union'а. В Query вы прицепили две таблицы к одной ведущей. Порядок обхода будет другим. См. Руководство разработчика. Раздел по ключевому слову "Sequencing of retrieved records". |
|
13.02.2007, 00:21 | #3 |
Участник
|
Цитата:
Цитата:
2. Вместо одного суперзапроса в многопользовательской системе лучше делать несколько небольших. См. aEremenko: Ресурс заблокирован, ждите...
Цитата:
3. Ваш select не эквивалентен Query. В select вы получаете выборку из одного большого union'а. В Query вы прицепили две таблицы к одной ведущей. Порядок обхода будет другим. См. Руководство разработчика. Раздел по ключевому слову "Sequencing of retrieved records".
|
|
13.02.2007, 08:44 | #4 |
Участник
|
Как скажете.
Но следуя за российским кодом вы плохому учитесь. Я же написал: Впрочем, как вам будет угодно. |
|
|
За это сообщение автора поблагодарили: gl00mie (3). |
13.02.2007, 11:17 | #5 |
Участник
|
Т.е. вместо того, чтобы делать по аналогии, скажем, с RLedgerSheetEngine_Turnover*, надо делать два идентичных класса и два идентичных Query на поставщиков/покупателей?.. Зачем же тогда те же Map'ы для таблиц придумали?.. Или учиться плохому - это именно использовать подстановки кодов полей, а использовать Map'ы - это нормально (видел кучу классов в SYS-слое, которые так делают)? Или же надо делать два идентичных Query и один класс, который выбирает их в зависимости от ситуации и обрабатывает через Map'ы CustVend*?..
Мне просто интересно: как обычно поступают в случае обработки данных по покупателям/поставщикам: ограничиваются только использованием Map CustVend*, а Query строят отдельно в AOT, или же позволяют себе параметрическое задание таблиц/полей для программно создаваемого Query вместо жесткого прописывания их в коде? Цитата:
Я же написал:
Цитата:
В Query вы прицепили две таблицы к одной ведущей. Порядок обхода будет другим. См. Руководство разработчика. Раздел по ключевому слову "Sequencing of retrieved records".
PHP код:
Впрочем, это натолкнуло на кое-какие мысли, в результате чего запрос-таки заработал, как надо Я просто прицепил GatewayOrgRef не к VendTrans, а к VendTable: PHP код:
X++: static void testJoins(Args _args) { VendTrans vt; VendTable vtbl; ExtCodeValueTable extcvt; QueryRun qr; Query query = new Query(); QueryBuildDataSource qbds, qbdsT; ExtCodeId extCodeId; ; extCodeId = (select firstonly ExtCodeId from ExtCodeTable where ExtCodeTable.ExtCodeTableId == tablenum(VendTable)).extcodeId; info(strfmt(@"For table '%1' ExtCodeId = '%2'", tablestr(VendTable), extCodeId)); qbds = query.addDataSource(tablenum(VendTrans)); qbds.addSortField(fieldnum(VendTrans, AccountNum)); qbds.addSortField(fieldnum(VendTrans, TransDate)); qbdsT = qbds.addDataSource(tablenum(VendTable)); qbdsT.fetchMode(QueryFetchMode::One2One); qbdsT.relations(false); qbdsT.addLink(fieldnum(VendTrans, AccountNum), fieldnum(VendTable, AccountNum)); qbds = qbdsT.addDataSource(tablenum(ExtCodeValueTable)); qbds.fetchMode(QueryFetchMode::One2One); qbds.addLink(fieldnum(VendTable, RecId), fieldnum(ExtCodeValueTable, ExtCodeRelationRecId)); qbds.addRange(fieldnum(ExtCodeValueTable, ExtCodeRelationTableId)).value(queryValue(tablenum(VendTable))); qbds.addRange(fieldnum(ExtCodeValueTable, ExtCodeId)).value(extCodeId); // filter out intercompany contragents qbds = qbdsT.addDataSource(tablenum(GatewayOrgRef)); qbds.joinMode(JoinMode::NoExistsJoin); qbds.fetchMode(QueryFetchMode::One2One); qbds.addLink(fieldnum(VendTrans, AccountNum), fieldnum(GatewayOrgRef, RefId)); qbds.addRange(fieldnum(GatewayOrgRef, RefType)).value(queryValue(SysActionRefTypes::Vend)); qbds = qbds.addDataSource(tablenum(GatewayOrganization)); qbds.fetchMode(QueryFetchMode::One2One); qbds.addLink(fieldnum(GatewayOrgRef, GatewayOrgId), fieldnum(GatewayOrganization, GatewayOrgId)); qbds.addRange(fieldnum(GatewayOrganization, InterCompanyOrg)).value(queryValue(NoYes::Yes)); qbds.addRange(fieldnum(GatewayOrganization, Active)).value(queryValue(NoYes::Yes)); qr = new QueryRun(query); while(qr.next()) { vt = qr.get(tablenum(VendTrans)); vtbl = qr.get(tablenum(VendTable)); extcvt = qr.get(tablenum(ExtCodeValueTable)); info(strfmt(@"%1 %2 name='%3', extCode='%4' %5", vt.TransDate, vt.AccountNum, vtbl.Name, extcvt.ExtCodeValue, (vt.Invoice ? vt.Invoice : vt.Txt))); } } Последний раз редактировалось gl00mie; 13.02.2007 в 11:20. Причина: typo |
|
13.02.2007, 11:48 | #6 |
Участник
|
Цитата:
Если кто-то хочет услышать, то рискну дать совет - не делайте по аналогии с объектами, у которых есть префикс R или суффикс _ru. Среди объектов с префиксом R или суффиксом _ru есть примеры грамотного программирования. Но с огромной вероятностью вы натолкнетесь на антипаттерн. И уж ни в коем случае не берите за основу RLedgerSheet..., не берите за основу объекты, которые тянут данные на клиента и держат их в свопе клиента. Не берите за основу объекты, в которых так много избыточного программирования (см предыдущий пост). Не берите за основу объекты с нестандартным интерфейсом, в которых нет поиска, нет сортировки, нет работы с секьюрити, нет работы с rls, нет автоотчетов, а контролы создаются динамически... По крайней мере не начинайте с таких объектов. |
|
13.02.2007, 13:49 | #7 |
Участник
|
Если клиент хочет получить определенный функционал, а наиболее близкое подобное решение RLedgerSheet..., к примеру, то как можно не брать его за основу?
Не больше ли получится избыточного программирования. А отсутствие автоотчетов - так ли это критично? |
|
13.02.2007, 14:22 | #8 |
Участник
|
Цитата:
Подумайте над этим. Наводящие вопросы: кто может сформлировать ТЗ именно так? Какие цели достигает этот кто-то формулируя ТЗ именно так? В самом деле... Ну, не сможет пользователь сам делать отчеты... Ну, должен будет обращаться за каждым чихом к программисту... Действительно, "так ли это критично?" |
|
14.02.2007, 11:57 | #9 |
Участник
|
Цитата:
Автоотчеты обсуждаются крайне редко либо потому что их важность никто под сомнение не ставит, либо потому что конечный пользователь все таки ими не так уж часто. Если объективно в целом имеет место первое- обязательно приму к сведению. |
|
14.02.2007, 12:22 | #10 |
Участник
|
Цитата:
Хочу только отметить, что клиент может попросить сделать не по аналогии с классом как вы сказали, а по аналогии с внешним видом и поведением. Только программист может поставить задачу - сделать класс по аналогии с другим классом. Пользуются нечасто, поскольку им их не показывают. Вы наверное забыли, но автоотчеты 1) показывают те записи, которые видны на экране, 2) позволяют подводить итоги. Вы попробуйте показать как при помощи автоотчетом получать итоги по заказу, закупке, журналу... И у вас перестанут просить добавить поле с суммой на форму... Открытые строки заказов/закупок, бюджеты, проводки... В сводном планировании это вообще незаменимая штука... Научите пользователей добавлять таблицы в фильтры... Научите пользователей записывать свои сложные фильтры... Это-ж просто праздник какой-то... А если вы научите пользователей указывать пустой шаблон (чтобы не было header'а с компанией), то пользователь сможет записывать результаты в текстовый файл без примесей. А если вы научите сразу указывать у файла расширение xls, то пользователь сможет быстро и без дополнительных диалогов открывать результаты в Excel... Скорее всего, не обсуждается, поскольку описано http://axapta.mazzy.ru/lib/autoreport/ |
|
14.02.2007, 13:22 | #11 |
Участник
|
Именно это я и подразумевал под визуальным интерфейсом, просто Вы сформулировали правильно.
И все таки у меня сомнения, базовая ли это вещь. Цитата:
Кстати, почему так мало функционала где фильтры не привязываются к пользователю а доступны для всех. Только в расчетах с персоналом где-то встречалось. По идее, удобная штука. Цитата:
Сообщение от mazzy
Скорее всего, не обсуждается, поскольку описано http://axapta.mazzy.ru/lib/autoreport/
Сводное планирование - да, им не будет заниматься человек, неспособный сложными фильтрами оперировать. Скорее всего вещь незаменимая. |
|
Теги |
ax3.0, шаблон |
|
Похожие темы | ||||
Тема | Ответов | |||
Двойной Outer Join в Query | 2 | |||
Как сдлеать join по одной таблице, а курсор по другой | 9 | |||
Join в Query | 2 | |||
Вопрос по query и join | 2 | |||
Join трех таблиц (две к одной) | 25 |
|