|  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)  Надо понимать буквально. Для подзапроса Exists не имеет значения, что именно он вернет. Важен сам факт наличия записи, а не ее содержимое. Поэтому и ставят константу. Хотя "ветераны программирования" предпочитают символ "x" по привычке. Вероятно, потому, что 1 символ - это 1 байт (ну, или 2 для UNICODE). А одно число - это 4 байта. Привычка экономить на всем 
				__________________ - Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... | 
|  | |
| За это сообщение автора поблагодарили: Товарищ ♂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. 
				__________________ - Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... | 
|  |