Помогите пожалуйста решить мою головоломку при разработке дополнительных деталей.

Есть Раздел Операции, есть мой разработанный раздел Зарплата Payment.
Задача сделать специальную деталь, в которой можно указать отношение многие ко многим для записей из этих таблиц и некую характеристику этих отношений.
Я сделала несколько новых объектов в администраторе
1) таблицу tbl_CashflowInPayment (ID, PaymentID, CashflowID, PrincipleID).
2 ) два запроса по таблице tbl_CashflowInPayment - отдельно для информации , которую я отражу в детали Операции: ID, PaymentID, CashflowID, PrincipleID , и второй более подробный для информации, которую я отражу в детали Зарплата: ID, PaymentID, CashflowID, PrincipleID, ActualDate, BasicSum, PayerID, OwnerID и еще несколько других. Последние колонки в запрос вытягиваются из присоединенной таблицы tbl_Cashflow. В запросе также есть 2 параметра PaymentID, CashflowID и сравнение с этими параметрами.
3) два датасета, соответствующих этим запросам.
4) окно для добавления данных в деталь.

Также я добавила 2 детали в разделах Зарплата и Операции, и прописала обновление этих деталей.

НО: в разделе Зарплата (где был более сложный запрос) все работает корректно, деталь видна, обновляется, данные добавляются, удаляются.
А в разделе Операции при открытии детали не получается открыть источник данных.
Ошибка возникает в строке ChildDataset.Open(); функции RefreshDetailDataByParentID.
Датасет и запрос я проверила несколько раз, но ошибку в создании запроса или датасета не нашла.

Насколько я понимаю - технически можно из одной и тоже таблицы отражать данные в разных деталях, с использованием разных датасетов? Так например, реализована деталь воздействия и целевая аудитория в воздействиях.
Где еще можно искать ошибку?

Нравится

2 комментария

Здравствуйте.

Да, если Вы используете несколько датасетов, Вы можете отображать данные в разных деталях с одной и той же таблицы.
По поводу ошибки - здесь, к сожалению сложно чем либо помочь. Установите отладчик (debugger;) и посмотрите текст ошибки. Также интересным является значение свойства ChildDataset.SelectQuery.SQLText;

Спасибо, появилась уверенность , что оно так все же должно работать. Буду пытаться найти ошибку

Показать все комментарии

Подскажите пожалуйста, возможно ли оформить в построителе TS Administrator запрос вида:
Select A,Sum(B*C) As field1
from ... join...
Group by A ?

Нравится

4 комментария

А вы такой запрос пробовали выполнять на базе данных? Насколько я знаю, нельзя в select-е выбирать те поля, по которые не используются в блоке group by. Если сможете выполнить его успешно на БД, то думаю можно и в ТС создать такой.

"Кошкаров Андрей" написал:
Насколько я знаю, нельзя в select-е выбирать те поля, по которые не используются в блоке group by.

Второе поле-агрегатная функция.Разве ее нужно указывать в Group by?

Подскажите пожалуйста,как оформить запрос в построителе?

Здравствуйте, Татьяна.

Можно сделать такой запрос следующим образом:

1. выбираем в sq колонки А и B, как основные колонки;
2. добавляем колонку с текстом SQL и считаем в ней сумму произведений B и С;
3. по колонке B делаем итог = сумма (строго говоря, нам это нужно чтобы обмануть систему, так как когда мы включаем суммирование по одной колонке, по остальным автоматически создастся группировка), а для колонки с текстом sql мы не будем устанавливать флаг "включить в группировку" и таким образом получим желаемый результат.

Во вложении Вы найдете скриншоты, иллюстрирующие все, написанное выше.

Инна Безверхняя,
II линия службы поддержки Terrasoft.

Спасибо большое,все понятно.

Показать все комментарии

Пишу кратко. Может кому-то следующая информация будет полезна и позволит сэкономить время.
Ниже я опишу как с помощью SQL Profiler я смог увидеть какие сервисы и в какой последовательности загружаются при запуске CRM с активным разделом - "Контрагенты". Также я смог получить информацию о времени потраченном при выполнении действий клиентской частью Terrasoft CRM и времени на выполнение запросов к БД.
Действия выполнялись на Terrasoft XRM 3.3.1.146 MS SQL.

Примечание: SQL Profiler-а нет в версии Express MS SQL Server

Шаги по подготовке:
1. CRM должна быть закрыта
2. Чистим папку с кэшем, чтобы увидеть все подтягиваемые сервисы из БД в профайлере.
Путь к папке для версии 3.3.1:
%appdata%\Terrasoft\3.3.1\Cache
3. Запускаем SQL Profiler

Получение трассировки запросов, выполняемых при загрузке CRM:
1. В SQL-Profiler создаем новую трассировку

2. Подключаемся к нашему серверу MS SQL

3. Запускаем трассировку со стандартными настройками:


4. Запускаем CRM и дожидаемся ее полной загрузки
5. Останавливаем трассировку
6. Сохраняем полученную трасировку в таблицу БД:

Подключаемся к базе и указываем базу и таблицу, в которую будут сохранены данные по трасировке.
Я использовал БД "master", владельца "dbo" и таблицу "tbl_trace1":

Во время сохранения эта таблица будет создана с нужными колонками и в нее будут выгружены данные.

Очистка таблицы от мусора.
Далее, для очистки таблицы от ненужных для меня данных я выполнил следующие запросы на БД "master" в MS SQL Server Management Studio:
1. Удалил записи у которых TextData пустое, ИЛИ поле ApplicationName не равно "TSClient.exe", или Duration пустое, ИЛИ LoginName не равно вашему логину (в моем случае это был 'Supervisor'):

DELETE FROM tbl_trace1
WHERE TextData IS NULL OR Duration IS NULL OR ApplicationName > 'TSClient.exe' OR LoginName > 'Supervisor'


2. Удалил не нужные для анализа колонки - EventClass, ApplicationName, NTUserName, LoginName, ClientProcessID, SPID, BinaryData:

 

ALTER TABLE tbl_trace1
DROP COLUMN EventClass, ApplicationName, NTUserName, LoginName, ClientProcessID, SPID, BinaryData

 

 


Извлечение из таблицы трассировки полезной информации для дальнейшего анализа:
1. Добавляем колонки в таблицу:
DiffBetweenSteps - поле будет содержать значение времени в милисекундах, которое прошло между временем окончания выполнения предыдущего запроса и временем начала выполнения текущего запроса. Значение этого поля вмещает в себя время потраченное клиентской частью программы (выполнение скриптов, прорисовка окон и т.д. - все, что выполняется без обращения к серверу БД);
ServiceID и ServiceCode - поля содержащие ID и код сервиса, данные по которому подтягиваются из БД. По этим полям видно в какой последовательности выполняется загрузка сервисов во время запуска CRM;
PrevRowNumber - значение предыдущего по последовательности выполнения запроса, нужно для связки, если будет выполняться сортировка по какому-то полю.

ALTER TABLE tbl_trace1 ADD DiffBetweenSteps int, ServiceID uniqueidentifier, ServiceCode nvarchar(250), PrevRowNumber int


2. Создаем функцию для проверки того, что передаваемое как аргумент значение является GUID-значением и выполняем ее на базе данных "master" (в которой находиться наша таблица трассировки):

 

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
go

CREATE FUNCTION [dbo].[IsGuid] ( @testString varchar(38))
returns int
AS
begin
    declare @ret int
    SELECT  @ret = 0,
            @testString = REPLACE(REPLACE(@testString, '{', ''), '}', '')
    IF len(isnull(@testString, '')) = 36 AND
       @testString NOT LIKE '%[^0-9A-Fa-f-]%' AND
       -- check for proper positions of hyphens (-)  
       charindex('-', @testString) = 9 AND
       charindex('-', @testString, 10) = 14 AND
       charindex('-', @testString, 15) = 19 AND
       charindex('-', @testString, 20) = 24 AND
       charindex('-', @testString, 25) = 0
          SET @ret = 1
   
    RETURN @ret
end


Эта функция понадобиться в дальнейшем
3. Заполняем значение поля DiffBetweenSteps с помощью следующего sql кода:

 

 

-- Проставляем разницу во времени между окончанием одного запроса и началом выполнения следующего
declare @EndTimePrev datetime
declare @StartTimeNext datetime
declare @EndTimeNext datetime
declare @RowNumber int
declare @DiffBetweenSteps int

SET @EndTimePrev = NULL

declare c_Rec cursor FOR
  SELECT RowNumber, StartTime, EndTime FROM tbl_trace1

OPEN c_Rec
FETCH NEXT FROM c_Rec INTO @RowNumber, @StartTimeNext, @EndTimeNext
WHILE @@FETCH_STATUS=0
BEGIN
        SET @DiffBetweenSteps = DATEDIFF(millisecond, @EndTimePrev, @StartTimeNext)  

        IF (@DiffBetweenSteps IS NULL) SET @DiffBetweenSteps = 0

    UPDATE tbl_trace1
    SET DiffBetweenSteps = @DiffBetweenSteps  
    WHERE RowNumber = @RowNumber
   
    SET @EndTimePrev = @EndTimeNext

    FETCH NEXT FROM c_Rec INTO @RowNumber, @StartTimeNext, @EndTimeNext
END
CLOSE c_Rec
DEALLOCATE c_Rec


4. Заполняем полей ServiceID и ServiceCode с помощью следующего sql кода:

 

 

declare @RowNumber int
declare @ServiceGUID nchar(38)

-- Начиная с 205 позиции вырезаем 38 символов и сохраняем их в переменную @ServiceID
declare c_Rec cursor FOR
  SELECT RowNumber, substring(TextData, 205, 38) FROM tbl_trace1

OPEN c_Rec
FETCH NEXT FROM c_Rec INTO @RowNumber, @ServiceGUID
WHILE @@FETCH_STATUS=0
BEGIN
    -- Проверяем является ли вырезанный кусок текста GUID-значением
    IF (SELECT dbo.IsGuid(@ServiceGUID)) = 1
                BEGIN
                        -- Есди - да, то вытягиваем по полученому ID сервиса его код из рабочей базы CRM
                        UPDATE tbl_trace1
                        SET ServiceID = @ServiceGUID, ServiceCode = (SELECT Code FROM XRM.dbo.tbl_Service WHERE ID = @ServiceGUID)
                        WHERE RowNumber = @RowNumber           
                END
        ELSE
                BEGIN
                        UPDATE tbl_trace1
                        SET ServiceID = NULL, ServiceCode = ''
                        WHERE RowNumber = @RowNumber           
                END

    FETCH NEXT FROM c_Rec INTO @RowNumber, @ServiceGUID
END
CLOSE c_Rec
DEALLOCATE c_Rec

 

 

В коде

SELECT Code FROM XRM.dbo.tbl_Service WHERE ID = @ServiceGUID


вместо названия БД 'XRM' вам нужно указать название вашей рабочей CRM базы.

 

Поскольку запрос на полученние данных сервиса выглядит следующим образом:

exec sp_executesql N'SELECT TOP 1
        [tbl_Service].[ID] AS [ID],
        [tbl_Service].[XMLData] AS [XMLData]
FROM
        [dbo].[tbl_Service] AS [tbl_Service]
WHERE([tbl_Service].[ID] = @P1)'
,N'@P1 nvarchar(38)',N'{64A6CED0-5C42-481F-97CA-C9632144B101}'


, где в конце указывается значение ID сервиса, то по этому ID мы можем получать необходимые данные о загружаемом сервисе, такие как код сервиса, его местонахождение в дереве сервисов и т.д. Для себя мы возьмем только код сервиса, чтобы наглядно было видно какой сервис запрашивается из БД.
5. Проставляем значение предыдущего шага в поле PrevRowNumber

 

-- Простановка предыдущего шага
declare @PrevRowNumber int
declare @RowNumber int

SET @PrevRowNumber = 0

declare c_Rec cursor FOR
  SELECT RowNumber FROM tbl_trace1

OPEN c_Rec
FETCH NEXT FROM c_Rec INTO @RowNumber
WHILE @@FETCH_STATUS=0
BEGIN
    UPDATE tbl_trace1
    SET PrevRowNUmber = @PrevRowNumber  
    WHERE RowNumber = @RowNumber
   
    SET @PrevRowNumber = @RowNumber

    FETCH NEXT FROM c_Rec INTO @RowNumber
END
CLOSE c_Rec
DEALLOCATE c_Rec


6. В итоге, с помощью SQL-запроса

 

 

SELECT RowNumber, TextData, CPU, Reads, Writes, Duration/(1000) AS Duration_t, StartTime, EndTime, ServiceCode, DiffBetweenSteps, PrevRowNumber
FROM tbl_trace1


выводим следующую таблицу:

7. Также можно выполнить сортировки по разным колонкам и увидеть:

 

 

 

SELECT RowNumber, TextData, CPU, Reads, Writes, Duration/(1000) AS Duration_t, StartTime, EndTime, ServiceCode, DiffBetweenSteps, PrevRowNumber
FROM tbl_trace1
ORDER BY CPU DESC

 

SELECT RowNumber, TextData, CPU, Reads, Writes, Duration/(1000) AS Duration_t, StartTime, EndTime, ServiceCode, DiffBetweenSteps, PrevRowNumber
FROM tbl_trace1
ORDER BY Duration_t DESC

 

SELECT RowNumber, TextData, CPU, Reads, Writes, Duration/(1000) AS Duration_t, StartTime, EndTime, ServiceCode, DiffBetweenSteps, PrevRowNumber
FROM tbl_trace1
ORDER BY DiffBetweenSteps DESC
  • каке запросы сколько используют процессорного времени в милисекундах на их выполнение

  • Выводимая таблица:

     

  • какие запросы дольше всего выполнялись, отсортировав по полю Duration. Значение в поле Duration храниться в микросекундах, делю на 1000, чтобы выводилось в милисекундах:

  • Выводимая таблица:

     

  • увидеть между какими запросами дольше всего происходили операции на стороне клиента, что поможет локализировать место в работе клиентской части Террасофт, которое требует больше всего времени на выполнение:

  • Выводимая таблица:

     

  • поля Reads и Writes показывают количество логических чтений и записей с жесткого диска на сервере во время выполнения события (запроса)

8. Также можно подсчитать общее время на выполнение запросов (ServerDuration), общее время на выполнение клиентской частью (ClientDuration), общее время на запуск CRM (OverallDuration):

SELECT Sum(Duration)/1000 AS ServerDuration, Sum(DiffBetweenSteps) AS ClientDuration, Sum(Duration/1000+DiffBetweenSteps) AS OverallDuration
FROM tbl_trace1


Выводимое значение:

 

Аналогичным образом можно анализировать не только процесс запуска CRM, а любое выполняемое действие с CRM, например, открытие окна редактирования.

Прикрепил SQL код, используемый мною - SLQ_code.txt
В архиве прикрепил картинки в их исходном размере - Images.zip.

Будет желание, делитесь полезными замечаниями :smile:

Нравится

Поделиться

0 комментариев
Показать все комментарии

Здравствуйте.

У меня есть двухуровневый запрос (создаю в постоителе запросов): проекты и работы этих проектов. И проекты, и его работы мне надо отфильтровать определенным образом, для этого я использвую фильтры каждой детали. Можно ли каким-то образом не отражать в результате запроса те проекты, для которых результат фильтрации работ равен нулю?
Сейчас у меня получается, что результат фильтрации не пересекается. Поэтому получается, что выводятся некоторые проекты уже без детализации по работам. Мне такие проекты вообще не нужно отражать.

Нравится

23 комментария

Виктория, возможно, Вам подойдёт следующий способ.

Для начала дублируем все колонки из детали второго уровня (работы), по значениям которых происходит фильтрация, на первый уровень (название колонок необходимо изменить, чтобы различать колонки из разных деталей). Плюс добавляем колонку "Название" из детали "Работы".

Потом в основную деталь добавляем фильтры по колонкам детали "Работы" плюс фильтр "Название работы Не пусто".

В свойствах всех добавленных колонок необходимо снять галочку "Отображать".

Наконец, чтобы избежать дублирования значений в детали первого уровня (за счёт присоединённых колонок из подчинённой детали), нужно добавить строчку Dataset.SelectQuery.Items(0).IsDistinct = true; в функцию ApplyFilters скрипта wnd_UserReportSectionScript:

function ApplyFilters() {
	var Dataset = fbcFilters.DatasetLink.Dataset;
	if (Dataset) {
		if (!IsEnabledSelectQueryColumnExists(Dataset.SelectQuery)) {
		    return;
		}
		try {
			fbcFilters.ApplyFilter();
		} catch (e) {
			ShowErrorDialog(e.message);
			return false;
		}
		if (Assigned(Dataset) && (Dataset.State != dstInactive)) {
			Dataset.Close();
		}
		Dataset.DisableEvents();
		// Чтобы Grid не включал колонки Dataset'а
		try {
			Dataset.SelectQuery.Items(0).IsDistinct = true;
			Dataset.Open();
		} finally {
			Dataset.EnableEvents(); 
		}
 
	}
}

Попробовала выполнить Ваши указания.
Сначала я исправила запрос, там действительно появились дуюлирующие записи.
Потом исправила скрипт. Но теперь при выполнении запроса появляется сообщение об ошибке
"Ошибка выполнения метода 'wnd_UserReportSectionOnPrepare'. Ошибка открытия источника данных "".
Оригинальное сообщение об ошибке: ORDER BY items must appear in the select list if SELECT DISTINCT is specified «Call Stack»"

Видимо, Вы включили сортировку по колонке (колонкам), которая не отображается. Необходимо её отключить (CTRL + клик левой кнопкой мыши по заголовку колонки).

Да, была сортировка по скрытому полю. Но даже после того, как я это исправила, ошибка все равно появляется

Проверьте, пожалуйста, возможно, есть ещё сортировки по скрытым полям. Проверьте, какой запрос генерируется профайлером, там сразу будет видно, если есть сортировка.

Сортировка была в детали. Теперь в профайлере все открывается, но результат такой же как и был: выводятся и проекты, в которых нет никакого результата в детали. А отчет, сформированный по результатам такого запроса, зависает при формировании.

Виктория, можете выложить скриншоты запроса в дизайнере, а также фильтров основной и подчинённой детали? Попробую воспроизвести ситуацию.

Еще раз здравствуйте. Выкладываю скриншоты для иллюстрации ситуации.

Виктория, если вывод записей, по которым нет подчинённых, критичен только в отчёте, можем Вам предложить следующие решения (в присоединённых файлах, для отчётов MS Excel и MS Word). Независимо от результатов предпросмотра запроса, в отчёт будут попадать только те записи, для которых существуют связанные с ними на подчинённых деталях. При этом из запроса можно убрать те колонки, которые Вы скрывали (и соответственно фильтры по ним).

Обратите внимание, что в случае применения предложенных решений, подобным образом будут выводиться все пользовательские отчёты в MS Excel и MS Word. Для того, чтобы иметь возможность управлять этим процессом, необходимо добавить в свойства отчёта некий признак (например, "Не отображать записи с пустыми деталями") и в предложенные изменения добавить проверку на значение этого признака.

Я сделала, как Вы подсказали отчет Excel. Все получилось так,как и надо. Но при выводе в файл получилось, что заголовки колонок отражаются не всегда. Я красным в файле выделила заголовок, который раньше воникал каждый раз для каждой сроки, где отражается информация по проекту.

В таком случае попробуйте в добавленном Вами куске кода закомментировать условие

if (BandCaptionsAreNeeded) {

и закрывающуюся скобку, которая ему соответствует.

Прекрасно, так работает. Правда, в самом начале отчета заголовок отразился 2 раза. :smile:

Тогда вместо закомментированной проверки можно вставить другую:

if (RowNumber != StartRowNumber + 1) {
...
}

Думаю, теперь точно всё :).

Да, действительно. Огромное спасибо

Здравствуйте! Аналогично не работает фильтр в основной детали созданного запроса. Выбираю продукты по всем договорам, кроме договоров с типом "Сопровождение". Основная таблица "Договор", в таблице вынесены данные из таблицы "Продукт в договоре", проссумированы колонки "Количество", "Реальное количество".

Вынести колонку "Тип" пробовала. Назвала ее "Тип договора", но по ней также фильтр не срабатывает.

Создать Excel отчет - это единственный выход?

Сборка 3.3.2.120

Татьяна, описанная Вами ситуация не воспроизводится в базовой версии. Проверьте, пожалуйста, правильность созданного запроса (например, с помощью профайлера). Если определить причину некорректной работы не получится, её нужно будет определять посредством удалённого подключения. Для этого отправьте запрос в Службу поддержки Terrasoft, указав в нём ссылку на данную тему и, если возможно, параметры удалённого доступа.

Выяснилось, что речь шла об отображении запроса на детали "Запросы" раздела. В указанной версии при отображении запроса на детали действительно не применялись фильтры, добавленные в дизайнере пользовательского запроса. В версии 3.3.2.165 данный функционал уже работает корректно.

Для решения необходимо добавить включение фильтров (строка SetSectionUserFilters(RootSection);) в функцию формирования датасета для детали "Фильтры" (функция GetQueryDetailDataset скрипта wnd_QueriesDetailScript). Весь текст функции после изменения должен выглядеть так:

function GetQueryDetailDataset(QueryID) {
	if (IsEmptyValue(QueryID)) {
		return null;
	}
	var QueryDataset = GetOpenedDatasetByUSIWithFilter('ds_Query', 'ID', 
		QueryID, 'QueriesDetail');	
	try {
		var XMLStorage = GetNewXMLStorage();
	    XMLStorage.LoadFromDataset(QueryDataset, rdfnQueryData);
	    var UserReportData = new Object();
	    var ReadXMLResult = DeserializeXMLData(UserReportData, XMLStorage);
	    if (!ReadXMLResult) {
	        return null;
	    }
		var RootSection = GetRootSection(ReadXMLResult);
		if (!RootSection) {
	        return null;
	    }
		var Dataset = GetQueriesDatasetBySectionAndSetupDatasetLink(RootSection, 
			UserReportData.Sections);
		if (!Dataset) {
			return null;
		}
		SetSectionUserFilters(RootSection);  
	}
	catch (e) {
		return null;
	} 		
	return Dataset;
}

Для версий, начиная с 3.3.2.130, этого должно быть достаточно. Для более ранних скорее всего нужно дополнительно заменить текст функции AddSelectColumnsForLookupFields скрипта scr_UserReportCommon на такой:

function AddSelectColumnsForLookupFields(Select, DataFields) {
	var Column, 
		ColumnAlias,
		ColumnField,
		DoCreate, 
		DataFieldToDelete, 
		Join, 
		LeftTableUSI, 
		LookupDataField,
		LookupDataset,		
		TableUSI,
		Count = Select.Columns.Count;		
	for (var i = 0; i < Count; i++) {
		Column = Select.Columns.Items(i);
		if (Column.SummaryType != stNone) {
			continue;
		}
		Join = Column.ParentJoin;					
		DoCreate = false;
		if (Join) {				
			if (IsPrimaryDisplayField(Column.Field)) {
			   	LeftTableUSI = ExtractUSICodeEx(
				   	Join.LeftField.ParentFields.ParentTable.USI);				
				if (!Assigned(Services.InformationsByUSI(
						'ds' + LeftTableUSI.substring(3)))) {
					continue;                                            
				}
				LookupDataset =  
					GetSingleItemByCode('ds' + LeftTableUSI.substring(3));
				ColumnField = Join.LeftField.ParentFields.ParentTable.Fields.ItemsByName('ID');
				ColumnAlias = Column.ColumnAlias + Join.LeftField.SQLName;
				DoCreate = true;
			}
		} else 
		if (Column.Field.ParentFields.ParentTable.UID == Select.FromTable.UID) {
			if (IsPrimaryDisplayField(Column.Field)) {
				TableUSI = ExtractUSICodeEx(Select.FromTable.USI);
				if (!Assigned(Services.InformationsByUSI(
						'ds' + TableUSI.substring(3)))) {
					continue;
				}				
				LookupDataset = GetSingleItemByCode('ds' + TableUSI.substring(3));
				ColumnField = Select.FromTable.Fields.ItemsByName('ID');
				ColumnAlias = Column.ColumnAlias + 'ID';				
				DoCreate = true;			
			}
		}
		if (!DoCreate) {	
			continue;
		}
		if (DataFields.ItemsByName(Column.KeyValue)) {
			if (!DataFields.ItemsByName(Column.KeyValue).IsEnabled) {
				continue;
			}				
		}	
		DataFieldToDelete = DataFields.ItemsByName(Column.KeyValue);		
		AddSelectQueryColumn(Select, ColumnField, ColumnAlias, Join, Column.IsEnabled);						    		
		LookupDataField = DataFields.CreateLookupDataField();
		LookupDataField.Name = ColumnAlias;			
		LookupDataField.Caption = DataFieldToDelete ?
			DataFieldToDelete.Caption : Column.Field.Caption;		
		LookupDataField.IsDisplayField = DataFieldToDelete ?
			DataFieldToDelete.IsDisplayField : true;		
		LookupDataField.LookupDataset = LookupDataset;
		LookupDataField.DisplayColumn = Column;			
		if (DataFieldToDelete) {
			LookupDataField.IsEnabled = DataFieldToDelete.IsEnabled;
			LookupDataField.Tag = DataFieldToDelete.Name;
			DataFields.InsertItem(
				DataFields.RemoveItem(DataFieldToDelete), LookupDataField);
			DataFieldToDelete.Tag = '';				
			DataFields.Add(DataFieldToDelete);						
		} else {
			LookupDataField.Tag = Column.Field.UID;
			DataFields.Add(LookupDataField);
		}
	}
}

Добрый день. Теперь такая выявилась проблема: установленные фильтры в пользовательском запросе (на вкладке «Фильтры» построителя запроса) игнорируются при формировании отчета Excel по данному запросу.
Фильтр по дробному числу.

Версия: 3.3.2.174

В построитиле запросов фильтрация проходит корректно ?
Галочки стоят ?

Пример:

"Яворский Алексей" написал:В построитиле запросов фильтрация проходит корректно ?
Галочки стоят ?

Да, корректно. Галочки стоят.



Единственное отличие в том, что в свойствах отчета Excel дополнительно используется "Фильтр для выделенных записей", но, думаю, это не должно было мешать.

Татьяна, решил снять видео, где отображается последовательность действий, на сколько я понял моделирующих Ваш запрос, проверьте пожалуйста, все ли вы делаете так как показано на видео.

Для просмотра формата Camtasia необходим Camtasia Player
http://download.techsmith.com/camtasiastudio/player/camplay.zip

Если данное видео не поможет, опишите пожалуйста последовательность действий которые вы выполняли для построения запроса, так как к сожелению по скриншотам до конца этого понять не удалось.

Здравствуйте, Алексей!
Создала аналогичный запрос и отчет. Все работает. Единственная разница в том, что если в отчете установить Тип фильтрации = "Для выделенных записей", то выводятся абсолютно все выделенные задачи с продуктами и суммами, даже если суммы = 0 или продукт не указан вовсе.
Попробуйте, у Вас аналогичным образом работает?

Обойти данную проблему удалось наложением на запрос фильтров пользователя, пусть самостоятельно выбирают период и ответственного. Для отчета указала Тип фильтрации = "Для всех записей".

Если вы выделете задачу по которой нет продукта, то в БД поле продукта у вас будет NULL, следовательно запрос сравнивает NULL > 0 (условие Сумма > 0 ). Обыграть это через условие "Продукт Не пусто" не получится так как на уровне запроса мы используем LEFT OUTER JOIN для связи таблиц Задач и Продуктов следовательно запись с таблички Задач мы получаем всегда.

Для того что бы обойти данное ограничение, можно использовать можно строить отчеты в FastReport

Пример: если мы выберем в разделе Задачи 2 задачи, у одной есть закрепленный продукт, у второй нет мы получим 2 записе, по одной будет отображатся пустота, по второй данные по продукту.

Если вы делали так же как на видео, то в к одной из задач я прикрепил группу продуктов, визуально это выглядело так :

На самом деле, это

Следовательно мы получили больше записей чем возможно ожидали. Но запрос работакт корректно

Показать все комментарии
Публикация

Что такое XPath?

XPath - это синтаксисдля адресации частей XML-документа

XPath - использует пути для адресации элементов XML

XPath - является важнейшей частью стандарта XSLT

XPath - не является XML-форматом

XPath - является стандартом W3C

 

XPath - использует адресные выражения для идентификации узлов в XML-документе. Эти адресные выражения очень похожи на выражения, с которыми вы сталкиваетесь при работе с файловой системой компьютера:

w3schools/xpath/default.asp

 Пример XPath

Empire Burlesque Bob Dylan USA Columbia 10.90 1985 Hide your heart Bonnie Tyler UK CBS Records 9.90 1988 /year>

Это выражение XPath:

catalog/cd/price

определяет элементы price, дочерние относительно элементов cd, подэлементов элемента catalog

Пути адресации XPath

Выражения для Путей Адресации

Имя

Описание

Пример

Результат

/

Дочерний элемент узла

/price

элемент price

//

Узел и его дочерние элементы

//cd

элемент cd и его подэлементы

|

список дочерних элементов

price | title

элемент price и title

Оси

Оси служат для определения набора узлов относительно данного узла.

Имя

Описание

Узел

self

сам узел

 

child

Дочерние узлы

 

parent

Родительский узел

 

descendant

Потомки узла

 

descendant-or-self

Узел и его потомки

 

ancestor

Предки узла

 

ancestor-or-self

Сам узел и его предки

 

following

Все узлы после данного

 

following-sibling

Все узлы этого же уровня после данного

 

preceding

Все узлы перед данным

 

preceding-sibling

Все узлы этого же уровня перед данным

 

attribute

Узлы аттрибутов

 

namespace

Узлы пространства имен

 

 

Предикаты Путей Адресации

Предикаты - это выражения, использующиеся для построения конкретных подмножеств узлов на основе данного.

Предикаты записываются внутри квадратных скобок.

Например, child::price[price=9.90] возвращает множество узлов, где элемент price имеет значение 9.90.

Новое множество узлов содержит все узлы, для которых значение предиката верно.

Сокращения для Путей Адресации

При описании путей адресации могут использоваться сокращения.

Сокращение

Значение

 

child::

@

attribute::

.

self::

..

parent::

//

/descendant-or-self/

число

[position()='значение']

Выражения XPath

Числовые Выражения

Числовые выражения используются для выполнения арифметических операций над числами.

Оператор

Описание

Пример

Результат

+

Сложение

6 + 4

10

-

Вычитание

6 - 4

2

*

Умножение

6 * 4

24

div

Деление

8 div 4

2

mod

Остаток от деления

5 mod 2

1

Замечание: XPath всегда преобразует аргумент в число перед выполнением арифметических операций.

Операции равенства

Операции равенства используются для проверки равенства двух значений.

Оператор

Описание

Пример

Результат

=

Равно

price=9.80

'истина' (если значение price равно 9.80)

!=

Не равно

price!=9.80

'ложь'

 

Проверка по Множеству Узлов

В случае, когда тестовое значение проверяется на равенство по множеству узлов, результатом будет 'истина', если множество узлов содержит какой-либо узел со значением, которое соответствует тестовому значению.

Когда тестовое значение проверяется на неравенство по множеству узлов, результатом будет 'истина', если множество узлов содержит хотя бы один узел со значением, отличным от тестового.

В итоге множество узлов может быть равно и не равно одновременно!!!

Операции Сравнения

Эти операции используются для сравнения двух значений.

Оператор

Описание

Пример

Результат

 

Меньше

price9.80

'ложь' (если price равно 9.80)

=

Меньше или равно

price=9.80

'истина'

Больше

price>9.80

'ложь'

>=

Меньше или равно

price>=9.80

'истина'

Замечание: XPath всегда преобразует каждый аргумент в число перед выполнением сравнения.

Булевы Выражения

Оператор

Описание

Пример

Результат

or

или

price=9.80 or price=9.70

'истина' (если price равно 9.80)

and

и

price=9.80 and price=9.70

 

 

Функции XPath

Библиотека Функций XPath

Библиотека функций XPath включает набор базовых функций для преобразования данных.

Числовые Функции

Имя

Описание

Пример

Результат

round

Округляет до ближайшего целого.

round(3.14)

3

ceiling

Округляет до ближайшего целого, которое больше данного.

ceiling(3.14)

4

floor

Округляет до ближайшего целого, которое меньше данного.

floor(3.14)

3

count

Возвращает количество узлов.

count(nodeset)

количество узлов в множестве узлов

number

Преобразует аргумент в число.

number(price)

Численное значение элемента price

sum

Возвращает сумму списка чисел.

sum(/cd/price)

Суммарная цена всех CD

 

Строковые Функции

Имя

Описание

Пример

Результат

string

Преобразует аргумент в строку.

string(3.14)

'3,14'

string-length

Возвращает длину строки.

string-lenght('Beatles')

7

substring

Возвращает подстроку.

substring('Beatles',1,4)

'Beat'

substring-after

Возвращает остаток строки после второго аргумента.

substring-after('12:30',':')

'30'

substring-before

Возвращает часть строки перед вторым аргументом.

substring-before('12:30',':')

'12'

contains

Возвращает 'истину', если первая строка содержит вторую.

contains('XML','X')

'истина'

starts-with

Возвращает 'истину', если первая строка начинается второй.

starts-with('XML','X')

'истина'

concat

Возвращает конкатенацию двух строк.

concat('The',' ','Beatles')

'The Beatles'

normalize-space

Удаляет лишние пробелы в строке.

normalize-space('  The    Beatles')

'The Beatles'

translate

Заменяет символы в строке.

translate('12:30',':','.')

'12.30'

 

Булевы Функции

Имя

Описание

Пример

Результат

boolean

Преобразует аргумент к булевому типу.

boolean(3.14)

'истина', если price не равен нулю

false

Вовращает 'ложь'.

number(false())

0

true

Возвращает 'истину'.

number(true())

1

not

Возвращает отрицание.

not(false())

'истина'

Источник: www.w3schools.com/XPath/default.asp

Нравится

Поделиться

0 комментариев
Показать все комментарии