30.01.2017, 14:15 | #1 |
Участник
|
Упрямые DataSource не дают построить прайс-лист
Помогите пож-та, у меня есть таблица номенклатур на которую очень редко и вразнобой меняется цена, поэтому решили хранить не прейкурант полностью, а таблицу, что цена на такой-то ItemId меняется с такой-то даты на такую-то (иногда на 0). Неожиданно для себя не удалось справиться с проблемой, как на заданную дату вывести полный прайс номенклатур с ненулевыми ценами на форму.
Сперва думали обойтись одним датасорсом, но никак не удается заставить его выводить только ОДНУ последнюю строку измененной цены, выводятся и все предыдущие. Тогда решили действовать двумя датасорсами (таблица одна и та же) Первый датасорс выводит на форму ItemId и дату последнего изменения цены на эту номенклатуру public void init() { QueryBuildDatasource qbds; ; super(); qbds = this.query().dataSourceTable(tableNum(SOPCenaTable)); qbds.addGroupByField(fieldNum(SOPCenaTable, ItemId)); qbds.addOrderByField(fieldNum(SOPCenaTable, ItemId),SortOrder::Ascending); qbds.addOrderByField(fieldNum(SOPCenaTable, TransDate),SortOrder:: Descending); qbds.addSelectionField(fieldNum(SOPCenaTable, TransDate), SelectionField::Max); qbds.addSelectionField(fieldNum(SOPCenaTable, ItemId), SelectionField:: Database); qbrCenaTransDate=qbds.addRange(fieldNum(SOPCenaTable, TransDate)); //будет ограничен датой поиска return; } А второй датасорс, который должен отобразить цену и остальные подробности, никак не приделать. Если его присоединять без GroupBy, то почему-то вся информация второго датасорса выводятся как пустая. Попробовали с GroupBy public void init() { QueryBuildDatasource qbds; ; super(); qbds = this.query().dataSourceTable(tableNum(SOPCenaTableView)); qbds.addGroupByField(fieldNum(SOPCenaTableView, ItemId)); qbds.addGroupByField(fieldNum(SOPCenaTableView, TransDate)); qbds.addSelectionField(fieldNum(SOPCenaTableView, TransDate), SelectionField::Max); qbds.addSelectionField(fieldNum(SOPCenaTableView, ItemId), SelectionField::Max); qbds.addSelectionField(fieldNum(SOPCenaTableView, Price), SelectionField::Max); qbds.addLink(fieldNum(SOPCenaTableView, ItemId),fieldNum(SOPCenaTable, ItemId)); qbds.addLink(fieldNum(SOPCenaTableView, TransDate),fieldNum(SOPCenaTable, TransDate)); //здесь еще хотели добавить проверку, что цена ненулевая } Тип связи InnerJoin, чтобы фильтровать ненулевые цены. Но Аксапта не хочет взять только те строки, для которых в первом датасорсе уже найден TransDate, а подтягивает и все предыдущие даты с ценами, а если поглядеть на запрос, так он получается невообразимый, вторая таблица попала в условие Group bу вместе с первой SELECT FIRSTFAST MAX(TransDate), ItemId FROM SOPCenaTable GROUP BY SOPCenaTable.ItemId, SOPCenaTableView.ItemId, SOPCenaTableView.TransDate ORDER BY SOPCenaTable.ItemId ASC, SOPCenaTable.TransDate DESC WHERE ((Department = N'ВП-52-29')) AND ((TransDate<={ts '2017-01-27 00:00:00.000'})) JOIN FIRSTFAST MAX(TransDate), MAX(ItemIdMAX(Price) FROM SOPCenaTableView WHERE SOPCenaTable.ItemId = SOPCenaTableView.ItemId AND SOPCenaTable.TransDate = SOPCenaTableView.TransDate при этом при всем в SQL Management Studio запросы построили легко и правильно выдавали прейскурант (здесь немного по-другому названы таблицы) select * from Invent inner join (select Max(TransDate) As mtd, Item as mItem from Price WHERE (TransDate <= CONVERT(DATETIME, '2016-09-08 00:00:00', 102)) group by Item) As td on Invent.Item=mItem inner join Price as prend on (prend.Item=mItem) and (prend.TransDate=mtd) where prend.Price >0 order by Invent.Item Поскажите пожалуйста, как получить форму в Аксапте с прейскурантом на заданную дату, наверное мы действуем неграмотно? Запасные аэродромы, типа вообще не отфильтровывать ненулевые строки, цены выводить dysplay-функциями и т.п. - это все так неудобно Axapta 2009 MS SQL Server 2008 |
|
30.01.2017, 15:01 | #2 |
Участник
|
Можно предварительно собрать данные во временную таблицу и выводить ее в качестве датасорса.
|
|
30.01.2017, 15:12 | #3 |
----------------
|
Вот так можно
X++: SELECT * FROM MyPrice P1 WHERE P1.TransDate <= [] AND NOT EXISTS(SELECT 1 FROM MyPrice P2 WHERE P2.ItemId = P1.ItemId AND P2.TransDate <= [] AND P2.TransDate > P1.TransDate) |
|
30.01.2017, 15:39 | #4 |
Участник
|
Пока не понятно. Что такое 1: AND NOT EXISTS(SELECT 1 FROM MyPrice P2
И ведь именно что существуют изменения цен и до и после заданной даты _DATE Так-то в X++ для любого _ItemId написать бы функцию SELECT FIRSTONLY MyPrice1 ORDER BY ItemId, TransDate DESC WHERE MyPrice1.ItemId==_ItemId && MyPrice1.Transdate<=_DATE потом по цене отфильтровать Но как бы сделать такой поиск в датасорсе на форме. Даже если Ваш select верный, на форму-то как его засунуть Последний раз редактировалось Яга1; 30.01.2017 в 15:48. |
|
30.01.2017, 15:55 | #5 |
Участник
|
Вам же Wamr привел пример. Этот запрос можно реализовать в Query. Не пробовали запускать в SQL? Будет возвращать то, что нужно, даже если есть цены и после _DAET
Может, в таком виде будет нагляднее X++: SELECT * FROM MyPrice P1 WHERE P1.TransDate <= _DATE AND NOT EXISTS(SELECT 1 FROM MyPrice P2 WHERE P2.ItemId = P1.ItemId AND P2.TransDate <= _DATE AND P2.TransDate > P1.TransDate)
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
|
За это сообщение автора поблагодарили: Товарищ ♂uatr (1). |
30.01.2017, 15:56 | #6 |
Участник
|
Кажется дошло! Спасибо большое, пойду пробовать
|
|
31.01.2017, 09:09 | #7 |
Участник
|
Цитата:
А с числами обычно быстрее работается. Но в данном случае разница в пару тактов процессора.
__________________
// no comments |
|
31.01.2017, 10:34 | #8 |
Участник
|
Здравствуйте! Запрос безусловно правильный, но не удается дойти до победного
В форме в executeQuery получается запрос SELECT FIRSTFAST * FROM SOPCenaTable ORDER BY SOPCenaTable.ItemId ASC, SOPCenaTable.TransDate DESC WHERE ((TransDate<={ts '2016-09-01 00:00:00.000'})) NOTEXISTS JOIN FIRSTFAST * FROM SOPCenaTableView WHERE SOPCenaTable.ItemId = SOPCenaTableView.ItemId AND ((((SOPCenaTable.TransDate<SOPCenaTableView.TransDate)) AND ((SOPCenaTableView.TransDate<={ts '2016-09-01 00:00:00.000'})))) Проблема с AND ((SOPCenaTableView.TransDate<={ts '2016-09-01 00:00:00.000'})))) это условие не отрабатывает Первое условие ((TransDate<={ts '2016-09-01 00:00:00.000'})) я строю как qbrCena1TransDate.value( date2str(datenull(),123,2,2,2,2,4)+ ".." +date2str(_DATE,123,2,2,2,2,4) ); и оно работает нормально А второе как qbrCena2TransDate.value('((SOPCenaTable.TransDate<SOPCenaTableView.TransDate)) AND ((SOPCenaTableView.TransDate<=' +sqlSystem.sqlLiteral(_DATE) +'))'); и датовая константа в запросе получается вроде похожа на правильную, но фильтрация по ней не происходит. Проверяли, заменяя NotExists Join на Inner Join Учитываются строки с более поздними датами Может быть подскажете как справиться с этой проблемой? Возможно дело в скобочках? Последний раз редактировалось Яга1; 31.01.2017 в 10:55. |
|
31.01.2017, 10:53 | #9 |
Участник
|
X++: qbrCena1TransDate.value(queryRange(dateNull(), SOPCenaDoc.TransDate)); qbrCena2TransDate.value(queryRange(SOPCenaTable.TransDate + 1, dateNull())); qbrCena2TransDate.value(queryRange(dateNull(), SOPCenaDoc.TransDate));
__________________
// no comments |
|
31.01.2017, 11:10 | #10 |
----------------
|
X++: qbrCena2TransDate.value('((SOPCenaTable.TransDate<SOPCenaTableView.TransDate) && (SOPCenaTableView.TransDate<='+date2StrXpp(_DATE)+'))'); Последний раз редактировалось Wamr; 31.01.2017 в 11:14. |
|
31.01.2017, 11:15 | #11 |
Участник
|
1)qbrCena2TransDate.value(queryRange(dateNull(), SOPCenaDoc.TransDate)) вообще убивает предыдущее условие, остается только ((TransDate<={ts '2017-01-27 00:00:00.000'})), а надо AND
2)qbrCena2TransDate.value(queryRange(SOPCenaTable.TransDate + 1, dateNull())); в запросе превращается в ((TransDate>={ts '1900-01-02 00:00:00.000'})) |
|
31.01.2017, 11:21 | #12 |
Участник
|
qbrCena2TransDate.value('((SOPCenaTable.TransDate<SOPCenaTableView.TransDate) && (SOPCenaTableView.TransDate<='+date2StrXpp(_DATE)+'))');
в запросе превратилось в ((((SOPCenaTable.TransDate<SOPCenaTableView.TransDate) && (SOPCenaTableView.TransDate<=01\09\2016)))) здесь AND написано не так и дата тоже, но условие почему-то сработало это не случайность? огромное спасибо! |
|
31.01.2017, 11:57 | #13 |
Участник
|
Цитата:
Сообщение от Яга1
1)qbrCena2TransDate.value(queryRange(dateNull(), SOPCenaDoc.TransDate)) вообще убивает предыдущее условие, остается только ((TransDate<={ts '2017-01-27 00:00:00.000'})), а надо AND
2)qbrCena2TransDate.value(queryRange(SOPCenaTable.TransDate + 1, dateNull())); в запросе превращается в ((TransDate>={ts '1900-01-02 00:00:00.000'})) X++: qbrCena2TransDate.value('(SOPCenaTable.TransDate < SOPCenaTableView.TransDate)') qbrCenaTableTransDate = qbds.addRange(fieldNum(SOPCenaTableView, TransDate)); qbrCenaTableTransDate.value(queryRange(datenull(), _DATE));
__________________
// no comments |
|
31.01.2017, 12:37 | #14 |
Участник
|
Тогда условия связались по OR:
(((SOPCenaTable.TransDate < SOPCenaTableView.TransDate)) OR (TransDate<={ts '2016-09-01 00:00:00.000'})) |
|
31.01.2017, 23:01 | #15 |
Участник
|
Цитата:
решили хранить не прейкурант полностью, а таблицу, что цена на такой-то ItemId меняется с такой-то даты на такую-то (иногда на 0).
Добавить поле даты окончания срока действия цены ValidTo, заполнять его по умолчанию максимальной датой, а при создании строки с последующей датой заполнять его соответстующим значением окончания срока действия. Тогда запросы по актуальным ценам упростятся до [дата цены]>=ValidFrom AND [дата цены] <= ValidTo.
__________________
Ален ноби, ностра алис. Что означает - если один человек построил, другой завсегда разобрать может. |
|
01.02.2017, 09:25 | #16 |
Злыдни
|
C ценами нельзя использовать такую модель, т.к. полноценная реализация запрещает иметь пересекающиеся диапазоны. В реально жизни очень часто встречаются маркетинговые акции, действующие короткий срок и с ценами ниже обычной. После окончания маркетинговой акции опять создавать нормальные цены? В свою бытность, помню, даже мутили специальную модификацию, которая выполняла последовательное создание ценового соглашения, если в журнале цен у записи была указана дата окончания действия: закрыть предыдущую датой начала и создать копию с датой начала равной дате окончания + 1.
__________________
люди...считают, что если техника не ломается, то ее не нужно ремонтировать. Инженеры считают, что если она не ломается, то нуждается в совершенствовании. |
|
01.02.2017, 09:35 | #17 |
Участник
|
Цитата:
Сообщение от KiselevSA
C ценами нельзя использовать такую модель, т.к. полноценная реализация запрещает иметь пересекающиеся диапазоны. В реально жизни очень часто встречаются маркетинговые акции, действующие короткий срок и с ценами ниже обычной. После окончания маркетинговой акции опять создавать нормальные цены?
|
|
01.02.2017, 09:43 | #18 |
Участник
|
Цитата:
Сообщение от KiselevSA
очень часто встречаются маркетинговые акции, действующие короткий срок и с ценами ниже обычной. После окончания маркетинговой акции опять создавать нормальные цены?
В свою бытность, помню, даже мутили специальную модификацию, которая выполняла последовательное создание ценового соглашения, если в журнале цен у записи была указана дата окончания действия: закрыть предыдущую датой начала и создать копию с датой начала равной дате окончания + 1. И создавать нормальную цену на последующий период, по идее, нужно не при прекращении действия краткосрочного ценового соглашения, а в тот момент когда становится известным срок окончания акции. Как правило, это бывает уже в начале действия акции.
__________________
Ален ноби, ностра алис. Что означает - если один человек построил, другой завсегда разобрать может. |
|
|
За это сообщение автора поблагодарили: dn (1). |
01.02.2017, 11:56 | #19 |
Участник
|
Цитата:
Сообщение от AlGol
Может проще будет доработать таблицу с ценами до полноценной реализации модели ValidFrom-ValidTo?
Добавить поле даты окончания срока действия цены ValidTo, заполнять его по умолчанию максимальной датой, а при создании строки с последующей датой заполнять его соответстующим значением окончания срока действия. Тогда запросы по актуальным ценам упростятся до [дата цены]>=ValidFrom AND [дата цены] <= ValidTo. И не максимальную, а какую-то там еще, если удалилась не последняя строка И при изменении ValidFrom, если в результате строка перескакивает через несколько строк назад, тоже исправить ValidTo. Мы уже делали так. Впечатление, что ПРОЩЕ все-таки qbrCena2TransDate.value('((SOPCenaTable.TransDate<SOPCenaTableView.TransDate) && (SOPCenaTableView.TransDate<='+date2StrXpp(_DATE)+'))'); как предложил Wamr Наглядно и точно нет ошибок при пересчете ValidTo |
|
01.02.2017, 13:06 | #20 |
Участник
|
Есть два связанных между собой процесса
Вы выбрали структуру данных, оптимальную для первого процесса (запись и модификация данных). Но, как Вы видите в данной теме, эта структура создает проблемы при выборке и анализе данных. Однако если модифицировать структуру данных для оптимальной выборки и анализа, то она будет не оптимальна для модификации и хранения. И тут вопрос в том, что для Вас важнее? Что чаще придется делать? Ведь, в конце концов, написать код триггерров на insert/update/delete придется только один раз. Пусть даже он будет достаточно сложным. А вот выборки цен Вы будете делать еще много раз и по разным поводам. И каждый раз Вы будете мучительно долго вспоминать, как же это все "упихать" в один запрос, причем в синтаксисе Axapta.
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|