|
12.09.2011, 10:56 | #1 |
Участник
|
вопрос по Query
Простейшая задача: есть query по таблице CustTable с какими-то условиями.
Есть значение "КлиентАБВ". Нужно проверить, есть ли запись с таким значением поля AccountNum в имеющемся query. Понято, через queryRun.next() можно перебрать все записи и условием проверить, но хотелось бы по быстрей и покрасивей. |
|
12.09.2011, 11:09 | #2 |
Участник
|
А что если создать копию этого query и добавить туда ренж по AccountNum, много перебирать не надо, поле уникальное – достаточно одного одного queryRun.next().
__________________
Не принимайте жизнь всерьез - это временное явление... |
|
12.09.2011, 11:11 | #3 |
Участник
|
В общем случае подцепить к имеющемуся query ещё один CustTable с типом связи exists join и наложить уже на него условие "КлиентАБВ". Затем, сделав один раз queryRun.next(), посмотреть вернулось ли что-нибудь.
to Jorj: если в исходном query на поле AccountNum уже есть условие, то ещё одно условие "КлиентАБВ". добавится к нему через "ИЛИ". Ещё вариант, чтобы добится присоединения условия через "И",можно воспользоваться "расширенным синтаксисом" range (http://www.axaptapedia.com/Expressions_in_query_ranges) и добавить условие "КлиентАБВ" через какое-нибудь свободное поле (например через recid. Но и тогда в общем случае могут возникнуть проблемы совместимости, например при наличии RLS: Что делает RLS с связанными запросами в отчете). Последний раз редактировалось S.Kuskov; 12.09.2011 в 12:57. |
|
12.09.2011, 11:45 | #4 |
Участник
|
Цитата:
В принципе, общая идея, заключающаяся в том, что нужно наложить еще один Range по этому полю - правильная. Проблема только в том, что этот Range должен подкючаться по "И", а если уже есть Range по этому же полю, скажем, настроенный пользователем, то подключение произойдет по "ИЛИ" Именно для данного конкретного случая можно сделать "хитрый трюк". Дело в том, что определение того факта, что Range добавляется по уже существующему полю выполняется по Id поля. При этом, как правило, используется FieldId. Однако Axapta может идентифицировать поле как по FieldId, так и по ExtendedFieldId. Т.е. идентифицировать поле, как первый элемент массива. Естесственно, что FieldId и ExtendedFieldId - это разные значения. Как следствие, построитель запросов интепретирует их как разные Range и выполняет объединение по "И". Получается, примерно следующее X++: static void Job_Test(QueryRun queryRun) { Query queryFind; QueryRun queryRunFind; QueryBuildDataSource qbds; QueryBuildRange qbr; ; queryFind = new Query(queryRun.query().pack()); qbds = queryFind.dataSourceTable(tablenum(CustTable)); // Вот этот трюк qbr = qbds.findRange(fieldnum(CustTable, accountNum)); if (qbr) { // Если Range уже есть, то создаю новый Range по ExdendendFieldId qbr = qbds.addRange(fieldId2ext(fieldnum(CustTable, accountNum),1)); } qbr.value('КлиентАБВ'); queryRunFind = new QueryRun(queryFind); if (queryRunFind.next()) { info("Есть такая запись"); } } Естесственно, этот трюк работает только для полей, которые массивами не являются. Для полей-массивов можно сделать подобное только для первого элемента массива. |
|
|
За это сообщение автора поблагодарили: coolibin (1), S.Kuskov (1), propeller (1). |
12.09.2011, 12:41 | #5 |
Участник
|
Цитата:
Если выбирать из предложенных вариантов, то мне больше нравится мой про использование http://www.axaptapedia.com/Expressions_in_query_ranges P.S.: propeller, вот уж действительно "простейшая задача" Последний раз редактировалось S.Kuskov; 12.09.2011 в 13:32. |
|
13.09.2011, 11:21 | #6 |
Участник
|
Цитата:
Цитата:
Сообщение от Владимир Максимов
К сожалению, это ничего не гарантирует. Если исходный запрос относительно сложен и имеет несколько таблиц-источников с "не линейной" схемой объединения, то подключение еще одного источника по Exists Join может привести к тому, что запросу "снесет крышу". Результат может оказаться парадоксальным.
X++: boolean findSuperCust(Query _srcQuery, CustAccount _custAccount = "КлиентАБВ") { Query q; QueryRun qr; QueryBuildDataSource qbds,qbds2; ; q = new Query(_srcQuery); qbds = q.dataSourceTable(tablenum(custTable)); qbds2 = qbds.addDataSource(tablenum(custTable)); qbds2.fields().addField(fieldNum(custTable, AccountNum)); qbds2.fetchMode(JoinMode::INNERJOIN); qbds2.relations(true); qbds2.addRange(fieldNum(custTable, AccountNum)).value(_custAccount); qr = new QueryRun(q); return qr.next(); } |
|
|
За это сообщение автора поблагодарили: S.Kuskov (1). |
13.09.2011, 11:38 | #7 |
Участник
|
Вы, наверное, с JoinMode() перепутали?
Но FetchMode() тоже надо будет настраивать, если есть другие датасорсы, подключенные к CustTable. А основная проблема с этим запросом - не может Аксапта обрабатывать большое количество вложенных подзапросов. Особенно, если они находятся на одном уровне вложенности
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: dn (1), Aquarius (1). |
13.09.2011, 11:55 | #8 |
Участник
|
Цитата:
qbds2.joinMode(JoinMode::innerJoin); qbds2.fetchMode(QueryFetchMode::One2One); Возможно. Но на практике, когда не проходил ExistJoin, вариант с InnerJoin проходил. Хотя согласен, что в общем случае это видимо не всегда будет работать. |
|
13.09.2011, 12:37 | #9 |
Участник
|
Цитата:
только в случае нескольких вложенных таблиц Аксапта возвращает записи ПОСЛЕДОВАТЕЛЬНО. я уже ссылку давал Цитата:
Сообщение от mazzy
вариант S.Kuskov: приджойнить exist join тоже не подойдет. поскольку в запросе уже может быть join. http://msdn.microsoft.com/en-us/libr...36(AX.10).aspx
|
|
13.09.2011, 14:14 | #10 |
Участник
|
Цитата:
Цитата:
Проблема с "exists join" связана с тем, что если такой exists будет вставлен между двумя таблицами уже соеденёнными при помощи "inner join", то результирующий запрос получится не всегда ожиданным. Например, такой гипотетический код: Код: select Table1 exists join Table3 where Table3.id == Table1.id inner join Table2 where Table2.id == Table1.id Код: table1 where exists (select Table3 join table2) |
|
|
За это сообщение автора поблагодарили: dn (1). |
13.09.2011, 11:45 | #11 |
Участник
|
Ну это легко проверить простым дополнительным запросом.
Цитата:
X++: void init() { super(); selectRangeRecords = this.query().dataSourceNo(1).addRange(fieldnum(TmpSysQuery, RangeStatus)); selectRangeRecords.value('!' + enum2str(RangeStatus::Hidden)); selectRangeRecords.status(RangeStatus::Locked); // <<-- } О! Здравая идея. А для оптимизации тогда ещё можно задать addSelectionField со значением SelectionField:atabase по какому-нибудь полю индекса |
|
12.09.2011, 11:20 | #12 |
Участник
|
Цитата:
X++: boolean findSuperCust(Query _srcQuery, CustAccount _custAccount = "КлиентАБВ") { Query q = new Query(q); // создаем копию QueryRun qr; // устанавливаем новый критерий или меняем существующий findOrCreateRange_W(q.dataSourceTable(tablenum(custTable)),fieldnum(custTable, AccountNum), QueryValue(_custAccount)); // выполняем запрос qr = new QueryRun(q); return qr.next(); } |
|
12.09.2011, 11:37 | #13 |
Участник
|
Цитата:
Вы о каких эфектах? В случае если пользовательский фильтр по полю accountNum имеет место быть, то тогда просто взять и заменить его на "КлиентАБВ" можно только если истинно Global::inRange(Range.value, "КлиентАБВ"). В общем случае пользовательских фильтров может быть несколько, значит проверить прийдётся их все, т.е. нужен цикл. ИМХО добавить ещё одну связку по exists join будет проще. Ещё раз. Все эти извращения нужны если "пользовательский фильтр по полю accountNum имеет место быть". Если нет, то достаточно совета Jorj и mazzy |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
12.09.2011, 13:32 | #14 |
Участник
|
Если ровно один, то findOrCreateRange_W изменит критерий на 'КлиентАБВ' по полю.
Если меньше одного, то findOrCreateRange_W добавит критерий 'КлиентАБВ' по полю. А вот если больше одного, то findOrCreateRange_W изменит один из критериев (первый попавшийся) на 'КлиентАБВ'. Все критерии по этому полю будут действовать через ИЛИ. Цитата:
А ведь точно. |
|
12.09.2011, 14:31 | #15 |
Участник
|
мне кажется что достаточно будет добавить ",КлиентАБВ", если критерий уже существует.
X++: boolean findSuperCust(Query _srcQuery, CustAccount _custAccount = "КлиентАБВ") { Query q = new Query(_srcQuery); // создаем копию QueryBuildRange qbr; QueryRun qr; // устанавливаем новый критерий или меняем существующий qbr = SysQuery::findOrCreateRange(q.dataSourceTable(tablenum(custTable)),fieldnum(custTable, AccountNum)); qbr.value( queryRangeConcat(qbr.value(), _custAccount) ); // добавляем к уже существующему или устанавливаем вместо пустого // выполняем запрос qr = new QueryRun(q); return qr.next(); } |
|
12.09.2011, 15:18 | #16 |
Участник
|
похоже вот такой способ будет работать всегда.
но если на код клиента уже был установлен критерий, то этот способ будет работать медленно. X++: boolean findSuperCust(Query _srcQuery, CustAccount _custAccount = "КлиентАБВ") { Query q = new Query(_srcQuery); // создаем копию QueryBuildRange qbr; QueryRun qr; CustTable custTable; // устанавливаем новый критерий только если раньше он был не установлен. qbr = SysQuery::findOrCreateRange(q.dataSourceTable(tablenum(custTable)),fieldnum(custTable, AccountNum)); if( !qbr.value() ) qbr.value(queryValue(_custAccount)); // выполняем запрос qr = new QueryRun(q); while( qr.next() ) { custTable = qr.get(tablenum(CustTable)); if( custTable.accountNum == _custAccount ) return true; } return false; } |
|
12.09.2011, 14:39 | #17 |
Участник
|
queryRangeConcat() - это условие 'или'
Кроме того, кто может поручиться, что ранее добавленное условие - это не расширенный фильтр?
__________________
Axapta v.3.0 sp5 kr2 |
|
12.09.2011, 14:48 | #18 |
Участник
|
ок. убедили.
вариант S.Kuskov: приджойнить exist join тоже не подойдет. поскольку в запросе уже может быть join. http://msdn.microsoft.com/en-us/libr...36(AX.10).aspx тогда остается только перебор записей в случае, если критерий на accountNum установлен? |
|
12.09.2011, 15:44 | #19 |
Участник
|
Цитата:
X++: boolean findSuperCust(Query _srcQuery, CustAccount _custAccount = "КлиентАБВ") { Query q = new Query(_srcQuery); // создаем копию QueryBuildDataSource qbds = q.dataSourceTable(tablenum(custTable)); QueryBuildRange qbr; QueryRun qr; Range SuperCustRange = strfmt("(%1.%2 == %3)", qbds.name(), fieldStr(custTable, AccountNum)); // устанавливаем новый критерий или меняем существующий qbr = SysQuery::findOrCreateRange(qbds ,fieldnum(custTable, DataAreaId)); if (qbr.value()) qbr.value(strfmt("(%1 && %2)", SuperCustRange, qbr.value())); else qbr.value(SuperCustRange); // выполняем запрос qr = new QueryRun(q); return qr.next(); } |
|
12.09.2011, 16:06 | #20 |
Участник
|
Хм.
А что, уже стало возможно использовать совместно расширенный синтаксис фильтров и обычный?
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: S.Kuskov (1). |
Теги |
query, как правильно |
|
Похожие темы | ||||
Тема | Ответов | |||
Очередной вопрос про Query | 45 | |||
Вопрос по query? | 1 | |||
Вопрос по query и join | 2 | |||
Вопрос по запросу (query) | 2 | |||
Вопрос знатокам QBE и Query в AXAPTA | 6 |
|