Хорошо, теперь ближе к правде. Не понятно, конечно, почему такое поведение с Text. Ваш метод DoWork ни чего не возвращает (void). Я думаю что он должен делать:
return"some text result, for example";
а если конкретно в вашем случае, то return xml;
И сигнатуру метода надо изменить на public string DoWork.....
Руслан, дело в том, что в методе на самом деле не одна строка. Просто сюда решил не скидывать весь код, т.к. уже на этапе инициализации переменной xml возникает проблема.
Дальнейшая логика в методе парсит содержимое переменной xml и создается запись в разделе. И все это работает если слать через Text.
Методу не обязательно что-то возвращать. Если переделать под ваш вариант - к сожалению ничего не поменяется. Все тот же Status: 400 Bad Request в постмане.
Подозреваю что проблема в параметрах, что над методом. Но уже по моему все перепробовал.
Александр, а что именно за Bad Request? Там же в ответе должна быть более подробная информация о причине. Если при этом самодельный веб-сервис упал с ошибкой, её стек должен быть в логах сайта.
The server encountered an error processing the request. The exception message is 'Error in line 2 position 145. Expecting element 'base64Binary' from namespace 'http://schemas.microsoft.com/2003/10/Serialization/'.. Encountered 'Element' with name 'Event', namespace 'http://schema.broadsoft.com/xsi'. '. See server logs for more details. The exception stack trace is:
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader reader) at System.ServiceModel.Channels.Message.GetBodyCore[T](XmlDictionaryReader reader, XmlObjectSerializer serializer) at Terrasoft.Configuration.BsoCesolutions.UsrNewCallHandler.WebhookHandler() at SyncInvokeWebhookHandler(Object , Object[] , Object[]) at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
Вот аналогичный случай, но, смотрю, Вы совет оттуда уже попробовали. Там ещё советуют приводить сразу к нужному типу класса с такими полями.
И Вы уверены, что в ответе ReadOuterXml не требуемая строка? Может, теряется уже на обратном пути. Попробуйте для теста писать значение куда-то в базу.
Попробуйте ещё советы из этой темы, в частности, создать сервис для теста сначала не в 7.Х, а в отдельной своей программе и проверить, всё ли нормально в конфиге.
Существует деталь "Продукты в заказе". В ней отображены группы, в которых настроены несколько фильтров, и согласно которым подбираются продукты.
После выбора группы и прокрутки отобразившегося списка продуктов, в консоли выводятся запросы, которые система направляет в БД.
По окончанию списка продуктов и прокрутке вниз (в этой же области списка) запросы продолжают отправляться в БД, несмотря на то, что все продукты уже прогружены.
Получается, что система всё-равно пытается найти подходящие продукты, отчего система принимает на 1-2 секунды режим загрузки.
Почему так происходит и должна ли по логике система слать запросы в БД, если все продукты группы уже загружены?
Не совсем понятно, о каких «запросах в БД», которые «выводятся в консоли» речь. Вы имеете в виду «Инструменты разработчика» в браузере или профайлер из MS SQL?
Если речь о запросах в браузере к серверу, то на версии 7.14.3 такого поведения не наблюдаю. Попробуйте и Вы развернуть на сайте демо-версию и воспроизвести в ней, чтобы выяснить, дело в более старой версии софта или, может, в каких-то доработках окна выбора.
На сегодняшний день актуальной текущей версией является 7.14.4, поэтому лучше проверять сразу на ней.
Дарья Сошина пишет:
Версия 7.13.1.769
Используемая Вами версия устаревшая, поэтому рекомендую проверить возникает ли проблема на самой последней версии 7.14.4. Если данная проблема есть и в этой версии, обратитесь в службу поддержки, возможно, что Вы обнаружили не выявленную ранее ошибку.
Попробовала на демке 7.14.4. Индицент не повторился. В действительности, как Александр подметил, была доработка окна выбора.
Зверев Александр пишет:
Не совсем понятно, о каких «запросах в БД», которые «выводятся в консоли» речь. Вы имеете в виду «Инструменты разработчика» в браузере или профайлер из MS SQL?
В консоли браузера при скролле наблюдала формирование запросов.
Могла ли доработка так повлиять на поведение системы и можно ли это как-то проверить?
Могла, конечно. Мы же не знаем, что это за доработка. Проверить можно, оставив Вашу 7.13 без неё (например, подняв старый бекап и подключив сайт к нему) или накатываем доработанных схем на 7.14.
Можно создать view с запросом на выборку, триггерами вместо вставки, изменения и удаления в стороннюю базу. Затем создать привязанную к ней схему с такими же колонками и проставленным признаком «представление» и работать с ней так же, как с обычной.
Версия 7.9.2.2410 Нужно сделать несколько похожих запросов выбора контакта и если какой то вернет ID запустить БП.
Как я понял запросы можно сделать и в БП.
Вот пример запроса:
Выбрать контакта у которого поле1 > значение1
и есть активности где активность.поле1 = значение2
Много запросов я не строил но документации есть полно.
Решил строить и выполнять запрос при нажатии на кнопку,
var esq = Ext.create("Terrasoft.EntitySchemaQuery",{
rootSchemaName:"Contact"});
esq.addAggregationSchemaColumn("Id", Terrasoft.AggregationType.COUNT,"Id");
var exists =this.Terrasoft.createExistsFilter("[Activity:Contact].Name");
esq.filters.addItem(exists);
для проверки Запроса должна работать функция esq.GetSelectQuery(), но выдает - esq.getSelectQuery is not a function
будет 4-8 запросов, меняться будет только значение1 и значение2
К сожалению, GetSelectQuery - это серверная функция, вызвать ее можно только в C# коде. На клиентской стороне получить текст запроса в данный момент нельзя.
SELECT
[Contact].[Name][Name]
FROM
[dbo].[Contact][Contact] WITH(NOLOCK)
WHERE
EXISTS (
SELECT
[Activity].[Id][Id]
FROM
[dbo].[Activity][Activity] WITH(NOLOCK)
WHERE
[Contact].[Id]=[Activity].[ContactId]
AND ([Activity].[ResultId]= @P1
OR [Activity].[ResultId]= @P2
OR [Activity].[ResultId]= @P3
OR [Activity].[ResultId]= @P4
OR [Activity].[ResultId]= @P5))
"Радчук Виталий Владимирович" написал:как объяснить ошибку
если я прописал
Приведите весь код, вероятно ошибка не в esqContact.AddColumn("Id");, а в моменте получения данных по этой колонке, и стоит использовать алиас. var contactIdColumnName = esqContact.AddColumn("Name");
и уже вот имя записанное тут contactIdColumnName использовать при получении.
"Радчук Виталий Владимирович" написал:как объяснить ошибку
если я прописал
Приведите весь код, вероятно ошибка не в esqContact.AddColumn("Id");, а в моменте получения данных по этой колонке, и стоит использовать алиас. var contactIdColumnName = esqContact.AddColumn("Name");
и уже вот имя записанное тут contactIdColumnName использовать при получении.
В карточке детали, есть справочное поле: UsrParametr (который строится к справочнику: UsrRefParameter, с полями: Name, Description и UsrStatus из справочника UsrRefStatus, с полями: Name, Description).
Как отфильтровать поле UsrParametr, по имени статуса, т.е. по полю UsrStatus из Справочника UsrRefStatus.
Так же, если можно, объясните сам процесс построения запросов, если не затруднит, прокомментируйте построчно приведенный ниже код, а так же, хотелось бы подробного объяснения, для чего используется в запросе " : " и для чего " . "
Заранее спасибо.
Код:
При изменении значения в поле ActivityCategory вызывается метод onActivityCategoryChange. Реализация этого метода:
onActivityCategoryChange: function(){
var activityCategory =this.get("ActivityCategory");if(activityCategory && activityCategory.value=== ConfigurationConstants.Activity.ActivityCategory.Meeting){this.set("ShowInScheduler", true);}}
Согласно этому методу в поле ShowInScheduler (Отображать в расписании) устанавливается значение true, если выбранная категория - встреча.
Теперь по второй части кода:
"Result":{
lookupListConfig:{
filters:[
function(){
var type =this.get("ActivityCategory");
var filterGroup = Ext.create("Terrasoft.FilterGroup");
filterGroup.add("ActivityCategory",
Terrasoft.createColumnFilterWithParameter(
Terrasoft.ComparisonType.EQUAL,
"[ActivityCategoryResultEntry:ActivityResult].ActivityCategory",
type.value));
filterGroup.add("BusinessProcessOnly",
Terrasoft.createColumnFilterWithParameter(
Terrasoft.ComparisonType.EQUAL,
"BusinessProcessOnly",
0));return filterGroup;}]}
Данный код фильтрует значения в поле "Результат". Для выбора доступны значения, для которых:
В справочнике ActivityCategoryResultEntry определяются возможные результаты для разных категорий активности. Добавляется фильтрация значений по категории активности
Также не отображаются результаты, для которых уставлено значение "Использовать только в процессах"
Кто-то знает этот запрос (для чего он, зачем и можно ли оптимизировать):
exec sp_executesql N'SELECT
[vw_Account].[ID] AS [ID],
[vw_Account].[Name] AS [Name]
FROM
[dbo].[vw_Account] AS [vw_Account]
WHERE(EXISTS
(SELECT
[vw_Contact].[ID] AS [ID]
FROM
[dbo].[vw_Contact] AS [vw_Contact]
WHERE([vw_Contact].[AccountID] = [vw_Account].[ID] AND
EXISTS
(SELECT
[tbl_AdminUnit].[ID] AS [ID]
FROM
[dbo].[tbl_AdminUnit] AS [tbl_AdminUnit]
WHERE([tbl_AdminUnit].[UserContactID] = [vw_Contact].[ID] AND
[tbl_AdminUnit].[UserIsEnabled] = @P1)))))
ORDER BY
2 ASC',N'@P1 int',1
Спасибо, Александр!
К сожалеюнию, пока нельзя убрать, так как не знаю откуда оно формируется. Но когда узнаю, то постараюсь сделать поле "IsUserAccount", в которое запихивать 1 при создании нового юзера с новым юзерКонтрагентом. тада выборка будет проще
SELECT
[vw_Account].[ID] AS [ID],
[vw_Account].[Name] AS [Name]
FROM
[dbo].[vw_Account] AS [vw_Account]
WHERE IsUserAccount IS NOT NULL
SELECT
[vw_Account].[ID] AS [ID],
[vw_Account].[Name] AS [Name]
FROM
[dbo].[[tbl_AdminUnit] AS [[tbl_AdminUnit]
inner join [vw_Contact] on [tbl_AdminUnit].[UserContactID]=[vw_Contact].[ID]
inner join [vw_Account] on [vw_Contact].[AccountID]=[vw_Account].[ID]
WHERE [tbl_AdminUnit].[UserIsEnabled]= @P1
ORDER BY
2 ASC',N'@P1 int',1
формируется именно из системы, где-то сервис запроса есть, можно его переписать...
а что реально долго работает и тормозит систему???
ps почитал изыскания пытливых умов - вроде как exists в такой ситуации даже быстрее inner'а отработает
"AlexLS" написал:в которое запихивать 1 при создании нового юзера с новым юзерКонтрагентом
не правильно - надо это поле перезаписывать при изменении признака Активен любого пользователя, привязанного к этому контрагенту и выставлять в 0, если активных пользователей не осталось
"Андросов Дмитрий" написал:не правильно - надо это поле перезаписывать при изменении признака Активен любого пользователя, привязанного к этому контрагенту и выставлять в 0, если активных пользователей не осталось
Дмитрий, спасибо за ответ! Остается малое - выяснить откуда этот зловещий код выполняется!
"Александр Кудряшов" написал:а что реально долго работает и тормозит систему???
к сожалению из "самых страшных" в нашей терре он на 5 месте (остальные прорабатываю), а по частоте выполнения лидирует! То есть суммарный дюрейшен у юзеров зашкаливает!!!
а вообще это похоже на запрос для какого-то справочника типа "Контрагенты ответсвенных"
поищите UserIsEnabled с помощью GREP (у меня есть только в wnd_LicenseManagerScript, но там не то)
вот для сравнения запрос для справочника Ответсвенный (поиск по имени)
exec sp_executesql N'SELECT TOP 2
[tbl_Contact].[ID] AS [ID],
[tbl_Contact].[Name] AS [Name]
FROM
[dbo].[vw_Contact] AS [tbl_Contact]
INNER JOIN
[dbo].[tbl_AdminUnit] AS [tbl_AdminUnit] ON [tbl_AdminUnit].[UserContactID] = [tbl_Contact].[ID]
WHERE([tbl_AdminUnit].[UserIsEnabled] = @P1 AND
[tbl_Contact].[Name] LIKE @P2 + ''%'')
ORDER BY
1 ASC',N'@P1 int,@P2 nvarchar(4000)',1,N'слепо'
этот запрос выполняется перед function wnd_MainOnPrepare(Window). Как я понимаю, его и ядро юзает...
Выполняется он при старте перед:
exec sp_executesql N'SELECT
[tbl_SystemSetting].[ID] AS [ID],
[tbl_SystemSetting].[Code] AS [Code],
[tbl_SystemSetting].[ValueTypeID] AS [ValueTypeID],
[tbl_SystemSetting].[StringValue] AS [StringValue],
[tbl_SystemSetting].[FloatValue] AS [FloatValue],
[tbl_SystemSetting].[IntegerValue] AS [IntegerValue],
[tbl_SystemSetting].[BooleanValue] AS [BooleanValue],
[tbl_SystemSetting].[DateTimeValue] AS [DateTimeValue],
[tbl_SystemSetting].[DictionaryRecordID] AS [DictionaryRecordID],
[tbl_SystemSetting].[EnumItemID] AS [EnumItemID],
[tbl_SystemSetting].[IsCaching] AS [IsCaching]
FROM
[dbo].[tbl_SystemSetting] AS [tbl_SystemSetting]
WHERE([tbl_SystemSetting].[Code] = @P1)',N'@P1 nvarchar(4000)',N'UpdateActiveSessionPeriod'
"AlexLS" написал:а есть код выполняющийся перед wnd_MainOnPrepare?
Это уже пограничная область знаний, тут То Самое Ядро может что-то вызывать. Это за пределами моих знаний:smile:
Там раньше окно авторизации и проверка лицензии как раз идет, окно авторизации вроде бы в конфигурации от греха подальше спрятали
"Александр Кудряшов" написал:Там раньше окно авторизации и проверка лицензии как раз идет
она и есть, наверняка, проверка лицензии... Где еще может понадобиться контрагент пользователя? А значит ничего с этим не сделаете. Вам стоит по этому вопросу обращаться напрямую в тех поддержку, причем желательно сразу на кого-нибудь из старожилов с третьей линии выходить :wink: Но, как правило, по всем вопросам, связанным с лицензиями все заканчивается заявлением, что механизм лицензирования закрыт и сакрален :lol:
"Александр Кудряшов" написал:тут То Самое Ядро может что-то вызывать
Александр, вот мне кажется что ядро вызывает банальный sq_ который и поправили под собственные нужды не задумываясь, что оно где-то глобально юзается!!!
Кстати, оно же (запрос) вызывается и Outlook'ом...
"Андросов Дмитрий" написал:по всем вопросам, связанным с лицензиями все заканчивается заявлением, что механизм лицензирования закрыт и сакрален
Дмитрий, мне кажется что лицензии врядли вьюхой проверять будут, скорее всего обращались бы напрямую к tbl_Account и, так же кажется, что разработчики Террасофта, все-таки не настолько ... (тут каждый сам себе додумает), чтобы такой идиотский код. Хотя, возможно, и у них есть отдельные личности, к примеру разработчики лицензирования :smile:
В принципе, в недалекой перспективе, предполагаем переход на TSv7 и эта тайна может так и остаться не разрешенной, хотя кто знает, может на смену мне прийдут новые :biggrin: с этим же вопросом!
Кстати, очередной банальный (уж простите) вопрос: если выгрузить все сервисы в файлы и потом какой-нить утилиткой "поиск текста в xml" пройтись, тогда шансы на поиск увеличатся?
Ход абсолютно верен.
У простых пользователей НЕТ доступа к tbl_Account, а есть доступ только к vw_Account.
И проверка лицензии не то, ради чего стоит ломать имеющийся движок обращения к данным.
Добрый день. Я уже как-то поднимала этот вопрос тут Тогда окончательный я так и не получила. Напомню, что задача была, задавая дату, получить на основе журнала изменений количество продаж в состоянии в работе на эту дату. Эта первоначальная задача решена запросом
SELECT count (ID)FROM tbl_Opportunity WHEREEXISTS (SELECT ID FROM (SELECT top (1) CreatedOn,StatusName, StageStage, Title, ID, RecordID, Cash, Revenue FROM tbl_OpportunityLog WHERE [tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID]AND [tbl_OpportunityLog].[CreatedOn]= :CreatedOn ORDERBY[tbl_OpportunityLog].[CreatedOn]DESC)AS OpportunityTable WHERE[OpportunityTable].[StatusName]= :STATUSAND ([OpportunityTable].[StageStage]=:Stage1 OR [OpportunityTable].[StageStage]= :Stage2 OR [OpportunityTable].[StageStage]= :Stage3)) GROUPBY [tbl_Opportunity].[Title]
Остался открытым вопрос , как изменить этот запрос, чтобы получить ответ на вопрос "какой был потенциал этих продаж на требуемую дату".
У меня получается вытянуть только сегодняшний потенциал - sum ([tbl_Opportunity].[Cash]) , на все другие изменения запроса sql ругается - sum ([tbl_OpportunityLog].[Cash]), sum([OpportunityTable].[Cash]....
А варианты запросов сразу к журналу изменений приводят к дублированию продаж.
Вот навернула тут inner join, чтобы было можно выбирать данные из журнала изменений.
Остался вопрос (написала его в тексте запроса, чтобы было понятнее)
SELECT [tbl_Opportunity].[Title],
CONVERT(DATE, [tbl_Opportunity].[CreatedOn], 102) As CreatedOn,
sum([tbl_Opportunity].[Cash])/count([tbl_OpportunityLog].[id]) as CashToday,
sum([tbl_OpportunityLog].[Cash])/count([tbl_OpportunityLog].[id]) AS [AvgCashLog]
FROM tbl_Opportunity inner join [tbl_OpportunityLog] on
[tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID]and[tbl_OpportunityLog].[CreatedOn]<= CONVERT(DATETIME, '2014-03-31 00:00:00', 102)and[tbl_OpportunityLog].[StatusName]=:STATUS AND
([tbl_OpportunityLog].[StageStage]=:Stage1 OR
[tbl_OpportunityLog].[StageStage]=:Stage2 OR
[tbl_OpportunityLog].[StageStage]=:Stage3)// в ЭТОМ МЕСТЕ НАДО еще добавить условие на выбор записи журнала изменений с МАКСИМАЛЬНОЙ датой CreatedOn. КАК?
WHERE exists
(SELECT ID,StatusName, StageStage, Title, RecordID, Cash, Revenue from
(Select top (1) CreatedOn,StatusName, StageStage, Title, ID, RecordID, Cash, Revenue from tbl_OpportunityLog WHERE
[tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID]and[tbl_OpportunityLog].[CreatedOn]<= CONVERT(DATETIME, '2014-03-31 00:00:00', 102)
ORDER BY [tbl_OpportunityLog].[CreatedOn] Desc) As OpportunityTable
Where [OpportunityTable].[StatusName]=:STATUS AND
([OpportunityTable].[StageStage]=:Stage1 OR
[OpportunityTable].[StageStage]=:Stage2 OR
[OpportunityTable].[StageStage]=:Stage3))
GROUP BY
[tbl_Opportunity].[Title], [tbl_Opportunity].[CreatedOn]
Спасибо за Ваше обращение. Мы зарегистрировали его в Службе технической поддержки и назначили специалиста, который сможет Вас компетентно проконсультировать по данному вопросу
Виктория, я демонстрировал конструкцию.
В вашем запросе она должна располагаться так:
SELECT [tbl_Opportunity].[Title],
CONVERT(DATE, [tbl_Opportunity].[CreatedOn], 102) AS CreatedOn,
sum([tbl_Opportunity].[Cash])/count([tbl_OpportunityLog].[id]) AS CashToday,
sum([tbl_OpportunityLog].[Cash])/count([tbl_OpportunityLog].[id]) AS [AvgCashLog]
FROM tbl_Opportunity INNER JOIN [tbl_OpportunityLog] ON
[tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID] AND
[tbl_OpportunityLog].[CreatedOn]<= CONVERT(DATETIME, '2014-03-31 00:00:00', 102) AND
[tbl_OpportunityLog].[StatusName]=:STATUS AND
([tbl_OpportunityLog].[StageStage]=:Stage1 OR
[tbl_OpportunityLog].[StageStage]=:Stage2 OR
[tbl_OpportunityLog].[StageStage]=:Stage3)
WHERE EXISTS
(SELECT ID,StatusName, StageStage, Title, RecordID, Cash, Revenue FROM
(SELECT top (1) CreatedOn,StatusName, StageStage, Title, ID, RecordID, Cash, Revenue FROM tbl_OpportunityLog WHERE
[tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID]AND
[tbl_OpportunityLog].[CreatedOn]<= CONVERT(DATETIME, '2014-03-31 00:00:00', 102)
ORDER BY [tbl_OpportunityLog].[CreatedOn] DESC) AS OpportunityTable
WHERE [OpportunityTable].[StatusName]=:STATUS AND
([OpportunityTable].[StageStage]=:Stage1 OR
[OpportunityTable].[StageStage]=:Stage2 OR
[OpportunityTable].[StageStage]=:Stage3)) AND
[tbl_OpportunityLog].[CreatedOn]=(SELECT max([CreatedOn]) FROM [tbl_OpportunityLog])
GROUP BY
[tbl_Opportunity].[Title], [tbl_Opportunity].[CreatedOn]
Так, как Вы написали , тоже же получается пустое множество.
Да и не нужна там эта строка.
После WHERE EXISTS очень правильный запрос уже написан. Он одновременно 1) проверяет существуют ли в журнале изменений записи, дата которых меньше, чем указанная, а состояния равны указанным. Таких записей может быть несколько 2) и выбирает с помощью top (1) CreatedOn и сортировки DESC максимальную из дат , которая удовлетворяет требованию.
Проблема в другом. Проблема в том, что никак не получается узнать какая же Сумма потенциала была на тот момент в этой продаже. Не получается обратиться к OpportunityTable, которая сформирована таким образом.
Чтобы это как-то исправить я добавила INNER JOIN. Те. тут я хочу повторить эту же самую выборку, но таким образом, чтобы я могла к ней обращаться. Поэтому 1) я повторила ограничение по дате, по статусу и по состоянию. 2) Но никак не могу выбрать именно максимальную по дате запись журнала изменений.
ПОЛУЧИЛОСЬ! надо было просто не бояться впихнуть еще один селект
SELECT [tbl_Opportunity].[Title],
CONVERT(DATE, [tbl_Opportunity].[CreatedOn], 102) As CreatedOn,
sum([tbl_Opportunity].[Cash])/count([tbl_OpportunityLog].[id]) as CashToday,
sum([tbl_OpportunityLog].[Cash])/count([tbl_OpportunityLog].[id]) AS [CashLogOnDate]
FROM tbl_Opportunity inner join [tbl_OpportunityLog] on
[tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID]and[tbl_OpportunityLog].[CreatedOn]<= CONVERT(DATETIME, '2014-03-31 00:00:00', 102)and[tbl_OpportunityLog].[StatusName]=:STATUS AND
([tbl_OpportunityLog].[StageStage]=:Stage1 OR
[tbl_OpportunityLog].[StageStage]=:Stage2 OR
[tbl_OpportunityLog].[StageStage]=:Stage3)and[tbl_OpportunityLog].[CreatedOn]=(SELECT max([tbl_OpportunityLog1].[CreatedOn]) from [tbl_OpportunityLog] as [tbl_OpportunityLog1]
where
[tbl_OpportunityLog1].[RecordID]=[tbl_Opportunity].[ID]and[tbl_OpportunityLog1].[CreatedOn]<= CONVERT(DATETIME, '2014-03-31 00:00:00', 102)and[tbl_OpportunityLog1].[StatusName]=:STATUS AND
([tbl_OpportunityLog1].[StageStage]=:Stage1 OR
[tbl_OpportunityLog1].[StageStage]=:Stage2 OR
[tbl_OpportunityLog1].[StageStage]=:Stage3))
WHERE exists
(SELECT ID,StatusName, StageStage, Title, RecordID, Cash, Revenue from
(Select top (1) CreatedOn,StatusName, StageStage, Title, ID, RecordID, Cash, Revenue from tbl_OpportunityLog WHERE
[tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID]and[tbl_OpportunityLog].[CreatedOn]<= CONVERT(DATETIME, '2014-03-31 00:00:00', 102)
ORDER BY [tbl_OpportunityLog].[CreatedOn] Desc) As OpportunityTable
Where [OpportunityTable].[StatusName]=:STATUS AND
([OpportunityTable].[StageStage]=:Stage1 OR
[OpportunityTable].[StageStage]=:Stage2 OR
[OpportunityTable].[StageStage]=:Stage3))
GROUP BY
[tbl_Opportunity].[Title], [tbl_Opportunity].[CreatedOn]
В процессе администрирования базы данных возникла необходимость определить причину возникновения ошибки. Определенный объём информации импортируется в базу данных, с которым далее пользователи работают. В процессе заполнения определенного набора полей автоматически высчитывалась итоговая сумма в поле «Итого». Но в определённый промежуток времени использования продукта начали появляться ошибки, связанные с несоответствием значения поля «Итого» сумме полей из которых оно вычисляется («Сумма покупки», «Наценка», «Сбор» и т.д.). Так как ошибку не получалось явно повторить, необходимо было разработать механизм для решения данной проблемы.
Естественно самой реальной и первой причиной возникновения такой ошибки приходила идея о сбоях в работе событий полей окна редактирования (то есть значения в полях изменялись, а события данных полей(-я) не срабатывали).
В основу решения было положено создание двух таблиц в базе данных для ведения логов, что происходят с записью набора данных. Первая таблица WindowLog, а вторая TriggerLog.
Первая таблица WindowLog включает в себя поля «Дата создания»(CreatedOn), «Идентификатор записи» (RecordID), «Ответственный» (WindowsUser), «Имя поля породившего событие»(FieldName), «Итого» и поля из которых оно вычисляется («Сумма покупки», «Наценка», «Сбор» и т.д.). Для наполнения таблицы было использованы события невизуального компонента окна dlData: dlDataOnDatasetDataChange, dlDataOnDatasetBeforePost и dlDataOnDatasetAfterPost. В скрипте в событиях была создана функция, которая формировала SQL запрос к таблице WindowLog базы данных с фиксацией информации по указанным полям на момент срабатывания события.
Вторая таблица TriggerLog включает в себя поля «Дата создания»(CreatedOn), «Идентификатор записи» (RecordID), «Состояние» (до изменения записи и после), «SystemUser», «Итого» и поля из которых оно вычисляется («Сумма покупки», «Наценка», «Сбор» и т.д.). Для заполнения данной таблицы был создан триггер на инструкцию UPDATE проблемной таблицы с двумя запросами вставки значений в таблицу. В одном запросе вставлялись значения до изменений, а во втором после.
Запрос №1:
INSERTINTO TriggerLog (*набор полей*) SELECT(*набор полей*) FROM deleted
Запрос №2:
INSERTINTO TriggerLog (*набор полей*) SELECT(*набор полей*) FROM inserted
Результатом использования данного решения на основе анализа таблицы WindowLog было установлено, что срабатывают все события окна редактирования, влияющие на вычисление значения поля «Итого». В процессе использования окна редактирования и после сохранения записи значения поля «Итого» были корректны.
Проанализировав записи в таблице TriggerLog было установлено, что в результате выполнения инструкции UPDATE было внесено некорректное значение. Сопоставив даты создания записей в таблице TriggerLog и WindowLog было установлено, что инструкция UPDATE была вызвана не в результате манипуляций с окном редактирования, а иным источником. На основании поля «SystemUser» таблицы TriggerLog было установлено что изменения были внесены с помощью импортера данных.
Таблицу TriggerLog возможно расширить, добавив в нее поля, которые помогут ускорить процесс обнаружение источника изменений записи базы данных. Список дополнительных полей может выгладять следующим образом: ApplicationName, LoginName, HostName.
PS: Принимаю предложения на доработку вашей конфигурации!!! Для более детальной информации можно связаться по следующему e-mail адресу: providnui@ukr.net !!!
В случае возникновения дополнительных вопрос по теме могу поделиться более детальной информацией.
Помогите пожалуйста с запросом. Задача такая: на любой момент времени (заданный пользователем) вывести сколько Продаж в состоянии "в работе" на Стадии 1, Стадии 2 или Стадии 3 было.
Таблица Продажи логируется. Соответственно, возникла мысль, что надо делать следующий по логике запрос:
1) выбрать все продажи,
2) выбрать связанные с этими продажами записи журнала изменений,
3) сделать фильтр - дата записи журнала изменений меньше, чем заданная пользователем дата
4) выбрать среди них запись с максимальной датой
5) проверить является ли поле состояние этой записи = "в работе", а также равняется ли поле Стадия одной из трех указанных стадий.
Делаю следующий запрос. В результате получаю неправильный результат: находятся не последние записи журнала изменений, а все, у которых состояние = в работе, а стадия = одной из 3х необходимых стадий.
Как исправить ошибку?
SELECT
[tbl_Opportunity].[Title] AS [Title],
MAX(CAST(CONVERT(VARCHAR(8), [tbl_OpportunityLog].[CreatedOn], 112) AS DATETIME)) AS [CreatedOn],
[tbl_OpportunityLog].[StatusName] AS [StatusName],
[tbl_OpportunityLog].[StageStage] AS [StageStage],
SUM([tbl_OpportunityLog].[Revenue]) AS [Revenue],
SUM([tbl_OpportunityLog].[Cash]) AS [Cash],
CAST(CONVERT(VARCHAR(8), [tbl_Opportunity].[CreatedOn], 112) AS DATETIME) AS [CreatedOnOpportunity]
FROM
[dbo].[tbl_Opportunity] AS [tbl_Opportunity]
LEFT OUTER JOIN
[dbo].[tbl_OpportunityLog] AS [tbl_OpportunityLog] ON [tbl_OpportunityLog].[RecordID] = [tbl_Opportunity].[ID]
WHERE([tbl_Opportunity].[ID] = [tbl_OpportunityLog].[RecordID] AND
[tbl_OpportunityLog].[CreatedOn] = :CreatedOn AND
[tbl_OpportunityLog].[StatusName] = :Status AND
([tbl_OpportunityLog].[StageStage] = :Stage1 OR
[tbl_OpportunityLog].[StageStage] = :Stage2 OR
[tbl_OpportunityLog].[StageStage] = :Stage3))
GROUP BY
[tbl_Opportunity].[Title],
[tbl_OpportunityLog].[StatusName],
[tbl_OpportunityLog].[StageStage],
CAST(CONVERT(VARCHAR(8), [tbl_Opportunity].[CreatedOn], 112) AS DATETIME)
"Тихенко Виктория" написал:Задача такая: на любой момент времени (заданный пользователем) вывести сколько Продаж в состоянии "в работе" на Стадии 1, Стадии 2 или Стадии 3 было.
select count (ID) from tbl_Opportunity
where exist
(select ID from tbl_OpportunityLog where [tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID][tbl_OpportunityLog].[CreatedOn]<=:CreatedOn AND
[tbl_OpportunityLog].[StatusName]=:Status AND
([tbl_OpportunityLog].[StageStage]=:Stage1 OR
[tbl_OpportunityLog].[StageStage]=:Stage2 OR
[tbl_OpportunityLog].[StageStage]=:Stage3))
Увы, но так тоже получится выбрать продажи, у которых просто существует где-то во времени запись в журнале изменений "в работе" и одна из трех стадий.
Например, у меня есть Продажа для Клиента.
Журнал изменений:
дата изменений 01.04.2013, в работе, стадия 1.
15.04.2013, в работе, стадия 2.
30.04.2013, отменена, стадия 2.
10.05.2013, в работе, стадия 3.
Так вот, если делать подобный запрос на 01.05.2013 , то продажа попадет в результат запроса. Ведь существует запись журнала, дата изменения которой меньше 01.05.2013, состояние в работе, стадия 2.
Другое дело что существует и запись на 30.04.2013, которая этому требованию уже не удовлетворяет. И 01.05.2013 Продажа для клиента фактически не была в работе.
И надо как-то именно эту запись проверять на состояние и стадию.
select count (ID) from tbl_Opportunity
where exist
(select top 1 ID from tbl_OpportunityLog where [tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID][tbl_OpportunityLog].[CreatedOn]<=:CreatedOn AND
[tbl_OpportunityLog].[StatusName]=:Status AND
([tbl_OpportunityLog].[StageStage]=:Stage1 OR
[tbl_OpportunityLog].[StageStage]=:Stage2 OR
[tbl_OpportunityLog].[StageStage]=:Stage3) order by [tbl_OpportunityLog].[CreatedOn] desc)
Все ближе к цели :smile:
Теперь по-крайней мере , если у меня для какой-то продажи существовало 3 записи в журнале изменений, считается в результат только 1 продажа.
Но все равно Продажа для Клиента из моего примера в результат попадает.
Даже не зависит от того, поставить в конце сортировку DESC или ASC.
Получается что выбирает все же не первую запись, а лишь бы соответствовала условию про состояния и стадии.
SELECT count (ID) FROM tbl_Opportunity
WHERE exist
(SELECT top 1 ID FROM
(Select ID from tbl_OpportunityLog WHERE [tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID][tbl_OpportunityLog].[CreatedOn]<=:CreatedOn AND
[tbl_OpportunityLog].[StatusName]=:STATUS AND
([tbl_OpportunityLog].[StageStage]=:Stage1 OR
[tbl_OpportunityLog].[StageStage]=:Stage2 OR
[tbl_OpportunityLog].[StageStage]=:Stage3) ORDER BY [tbl_OpportunityLog].[CreatedOn] DESC))
SELECT count (ID) FROM tbl_Opportunity
WHERE exists
(SELECT ID from
(Select top (1) CreatedOn,StatusName, StageStage, Title, ID, RecordID, Cash, Revenue from tbl_OpportunityLog WHERE
[tbl_OpportunityLog].[RecordID]=[tbl_Opportunity].[ID]and[tbl_OpportunityLog].[CreatedOn]<=:CreatedOn
ORDER BY [tbl_OpportunityLog].[CreatedOn] Desc) As OpportunityTable
Where [OpportunityTable].[StatusName]=:STATUS AND
([OpportunityTable].[StageStage]=:Stage1 OR
[OpportunityTable].[StageStage]=:Stage2 OR
[OpportunityTable].[StageStage]=:Stage3))
GROUP BY
[tbl_Opportunity].[Title]
Итак, в запросе получилось наконец-то правильное число продаж, или список этих продаж, если вместо count (ID) написать просто Title. Это хорошо.
Теперь возникает следующая задача - как изменить запрос, чтобы еще и старые данные по сумме потенциала продажи вытянуть?
Продолжая пример: на сегодня Продажа для Клиента имеет Потенциал дохода продажи (поле Cash) = 6000. А в в журнале изменений есть строка 15.04.2013, в работе, стадия 2, Cash =2500.
Те выполняя запрос на 20.04.2013 я хочу получить в списке мою продажу, состояние в работе, стадия 2, и поле Cash = 2500 , а не 6000.
(последний результат я получу, если в первом select написать sum([tbl_Opportunity].[Cash ]).
Пробовала изменить начало запроса так:
SELECT [tbl_Opportunity].[Title],
sum([OpportunityTableLog].[Cash]) AS [CashLog],
[OpportunityTableLog].[StageStage] AS [StageStageLog] ,
[OpportunityTableLog].[StatusName] AS [StatusNameLog] ,
FROM tbl_Opportunity inner join
(select ID, CreatedOn,RecordID, Cash, StageStage, StatusName, Title from tbl_OpportunityLog) As OpportunityTableLog
on [OpportunityTableLog].[RecordID]=[tbl_Opportunity].[ID]
В результате предсказуемо получила все подряд записи журнала изменений.
Пробовала также вставить в блок inner join все, что раньше было в блоке exist, но sql ругается :(
Замучил меня этот запрос.