"Внезапно" (как это часто бывает) обнаружилось, что элементы базы требующие отображения в отсортированном виде отображаются не так как хотелось бы пользователям.
Дело всё в том, что когда пользователь, например, вводит какие-нибудь товары, в названии которых есть и буквенная часть и числовая (представляющая порядковый номер)
ручка
резинка
ножницы
карандаш красный №1
карандаш красный №2
карандаш красный №3
...
карандаш красный №9
карандаш красный №10
и этот пользователь хочет отсортировать продукты по алфавиту, то он получит:
карандаш красный №1
карандаш красный №10
карандаш красный №2
карандаш красный №3
...
карандаш красный №9
ножницы
резинка
ручка
И вот тут на примере карандашей видим проблему:
После номера 1 идёт 10, а не 2 так как сравниваются коды символов стоящих под соответствующими номерами,
Так как поле текстовое, то другой сортировки ожидать и не приходится
Суть вопроса: есть-ли шанс переписать механизмы сортировки, так, чтобы текстовое поле парсилось на наличие цифр, или сортировка идёт в ядре, "куда простым смертным путь заказан"?
Можно создать в SelectQuery колонку типа "SQL Text" и там написать произвольную формулу или вызов SQL-функции, оперирующие значением другой колонки. Потом создать колонку в Dataset-е. И сортировать по ней.
Мне кажется, автору темы этого будет недостаточно. Ведь "карандаш красный №1" должен быть ниже, чем "карандаш зелёный №2". Тут надо делить строку на буквы и цифры, цифры дополнять слева нулями до одинаковой длины и соединять обратно.
"Зверев Александр" написал:
Мне кажется, автору темы этого будет недостаточно. Ведь "карандаш красный №1" должен быть ниже, чем "карандаш зелёный №2". Тут надо делить строку на буквы и цифры, цифры дополнять слева нулями до одинаковой длины и соединять обратно.
да, был невнимателен)
в таком случае лучше поле писать на клиенте (в террасофт, а не в БД) на стадии insert\update, а не выбирать в запросе
брать числа после "№" и дописывать их нулями до одинаковой длины.
Но, это не панацея - что если пользователь напишет номер после "#" или вообще просто так "карандаш 2"? тогда, конечно, можно регэкспами решить, но что, если он напишет карандаш версия 2 зеленый №3? т.е. "версия 2" обрабатывать не надо, а "№3" надо...
Здравтсвуйте, Дмитрий!
Думаю, что в данном случае, когда речь идет о наименовании товаров, не должно возникать ситуаций, когда пользователь обзывает товар, как хочет.
Должны быть четкие регламенты по определению названия для товаров, чтобы было проще ориентироваться.
Если речь идет о версиях товара, то можно, например, добавить поле для ввода версии, а потом назавние будет дополняться содержимым этого поля автоматически.
Недавно, не побоюсь этого слова, столкнулся с пожеланием клиента жестко сортировать записи в гриде по двум полям дат - в первую очередь по одной (приезд), потом по другой (отъезд). Персонал долго и плотно сидел на экселе, так что желание было вполне понятно. Сначала показалось, что никаких проблем с этим не может возникнуть, но как выяснилось, без тех поддержки обойтись не удалось .
Первая мысль была - взять да отсортировать датасет в настройках SelectQuery, благо там есть прекрасная возможность настройки сортировки хоть по всем полям сразу. Что и было сделано.
Однако, результата это не дало - все осталось удивительно так же, как и было
Безуспешно пошаманив над запросом, я переключился на настройки грида.
Однако , вопреки ожиданиям... все осталось еще более удивительно так же, как и было
Убив довольно много времени на вариации настроек и чесание затылка, я решил наконец посмотреть что-же там в запросе, собственно, запрашивается...
Как и следовало ожидать никаких мной настроенных сортировок там не было. Профайлер показывал следующее:
exec sp_executesql N'SELECT TOP 40
/*№1*/ [tbl_Opportunity].[ID] AS [ID],
[tbl_Opportunity].[CustomerID] AS [CustomerID],
[tbl_Account].[Name] AS [CustomerName],
[tbl_Opportunity].[Probability] AS [Probability],
CAST(CONVERT(VARCHAR(8), [tbl_Opportunity].[ActualCloseDate], 112) AS DATETIME) AS [ActualCloseDate],
[tbl_Opportunity].[TypeID] AS [TypeID],
[tbl_Opportunity].[StatusID] AS [StatusID],
[tbl_Opportunity].[CreatedOn] AS [CreatedOn],
[tbl_Opportunity].[CreatedByID] AS [CreatedByID],
[CreatedBy].[Name] AS [CreatedByName],
[tbl_Opportunity].[WorkflowItemID] AS [WorkflowItemID],
[tbl_OpportunityStatus].[IsFinish] AS [IsFinish],
[tbl_Opportunity].[Revenue] AS [Revenue],
/*№14*/ CAST(CONVERT(VARCHAR(8), [tbl_Opportunity].[DateArrival], 112) AS DATETIME) AS [DateArrival],
CAST(CONVERT(VARCHAR(8), [tbl_Opportunity].[DateDeparture], 112) AS DATETIME) AS [DateDeparture],
[tbl_Opportunity].[MemberQuantity] AS [MemberQuantity],
[tbl_Opportunity].[PlaceArrival] AS [PlaceArrival],
[tbl_Opportunity].[PlaceDeparture] AS [PlaceDeparture],
[tbl_Opportunity].[WaterType] AS [WaterType],
[tbl_Opportunity].[BusType] AS [BusType],
[tbl_Opportunity].[Additionally] AS [Additionally],
[tbl_Opportunity].[TourGuidesFIO] AS [TourGuidesFIO],
[tbl_Opportunity].[TourHotelReserves] AS [TourHotelReserves],
[tbl_Opportunity].[TourCreditSum] AS [TourCreditSum],
[tbl_Opportunity].[Profit] AS [Profit]
FROM
[dbo].[tbl_Opportunity] AS [tbl_Opportunity]
LEFT OUTER JOIN
[dbo].[tbl_Account] AS [tbl_Account] ON [tbl_Account].[ID] = [tbl_Opportunity].[CustomerID]
LEFT OUTER JOIN
[dbo].[tbl_OpportunityStatus] AS [tbl_OpportunityStatus] ON [tbl_OpportunityStatus].[ID] = [tbl_Opportunity].[StatusID]
LEFT OUTER JOIN
[dbo].[tbl_Contact] AS [CreatedBy] ON [CreatedBy].[ID] = [tbl_Opportunity].[CreatedByID]
WHERE(((([tbl_Opportunity].[DateArrival] >= @P1 OR
[tbl_Opportunity].[DateDeparture] >= @P2) AND
([tbl_Opportunity].[DateArrival] @P3 OR
[tbl_Opportunity].[DateDeparture] @P4))))
ORDER BY
14 ASC,
1 ASC',N'@P1 datetime2(7),@P2 datetime2(7),@P3 datetime2(7),@P4 datetime2(7)','2013-04-01 00:00:00','2013-04-01 00:00:00','2013-05-01 00:00:00','2013-05-01 00:00:00'
Вот это меня убило: 1 ASC
Вторая сортировка - это сортировка пользователя по колонке (кликом)
Никаких настроенных сортировок и в помине...
На всякий случай просмотрев разные варианты профайлером, я начал сомневаться, что занимаю свое место... и написал-таки в техподдержку.
Ответ немного удивил, но было уже не важно
Цитата:
Сортировка реестра работает следующим образом:
по умолчанию устанавливается сортировка по полю ID. Если пользователь применил сортировку по колонке реестра - к сортировке по ID добавляется сортировка по данной колонке.
Установленная для колонок сортировка в запросе при открытии датасета реестра снимается и не учитывается.
Чтобы накладывать сортировку на нужные Вам колонки, необходимо:
1. Перед открытием датасета реестра программно устанавливать сортировку.
2. Скриптом сортировка устанавливается таким образом:
Где константа otAsc – определяет сортировку по возрастанию, а otDesc – сортировку по убыванию.
Сделал, как написано - и ура, оно заработало, но с одним "но" - возможность сортировать по столбцам (кликом) теперь какая-то странная такая возможность:
при открытии
ORDERBY 18ASC, 19ASC, 1ASC'
при сортировке по полю №27
ORDERBY 18ASC, 19ASC, 27ASC, 1ASC
при сортировке по полю №4
ORDERBY 18ASC, 4ASC, 19ASC, 1ASC'
Я тогда не стал дальше разбираться, т.к. и так много времени на это потратил, поэтому спрошу здесь:
- работают ли настройки сортировки в сервисе грида и, если да, то как их использовать?
- зачем-зачем-зачем сортировка в датасете сбрасывается на сортировку по ID?
ПС. Если создавать датасет так:
var Dataset = SelectQuery.Open();
то сортировка точно работает, скорее всего сбрасывание происходит где-то на связке грида с датасетом
"Maxim Gritsenko" написал:
1. OrderPosition - 0 = не сортировать. Выставляйте позиции с 1-цы в настройках грида и будет сортировать.
---речь о 3.4.0.164---
Сортировать будет, но есть нюансы:
- если у DataGrid установлено CanChangeSort - true, пользователь может поменять сортировку - и она такой и останется (в профайлдата)
- если у DataGrid установлено CanChangeSort - false и очищены настройки пользователя, пользователь не может поменять сортировку, но и она не работает)
- если у DataGrid установлено CanChangeSort - false и НЕ очищены настройки пользователя, пользователь сортировку менять не может, а сама сортировка остается такой, какой была в последний раз.
Кроме того, CanChangeSort относится сразу ко всем представлениям (DataGridView), что не очень удобно.
ПС. Я наверно, опять не прочитал мануал, но оказывается можно сортировать по нескольким\многим колонкам - через Shift. В нашем случае, правда, это не выход - почему-то считается, что не надо оставлять это на пользователя (т.к. настройки гридов сохраняются в настройках пользователя, и, поменяв, он может не вспомнить, как вернуть назад)
Это действительно самый простой способ. Формирование списка происходит на уровне ядра. В него можно вмешаться программно, но для этого нужно чистить список, и заново добавлять значения в том порядке, в каком необходимо.
Очистить список:
Page.MyComboBoxEdit.ListPrepared=false;
Добавить пункты в список:
Page.MyComboBoxEdit.AddItem(type.Id, type.Name);
Кроме того, рекомендую обратить внимание на признак сортировки в карточке для данного контрола:
Добрый день! Ситуация такая: В древовидном реестре отображаются группы статей и статьи бюджета с названием, содержащим номер (1., 1.01., 1.01.01 и т.д.). Сортировка установлена по названию в запросе. Так вот в гриде сортируются по возрастанию либо корневые группы, либо все подчиненные (при нажатии на название колонки). Есть необходимость, чтобы все статьи и группы шли по возрастанию. В версии 3.4. такой проблемы не возникало!
Версия CRM 3.2.0.10
Буду признателен за помощь!
Иван, Вам нужно переписать запрос добавив туда union, в котором будут выбираться подчиненные элементы (по аналогии с разделом "Проекты" - sq_Project), а в этом запросе добавить сортировку по названию.
Сталкивались ли Вы с необходимостью отсортировать значения справочника, отображаемого в виде выпадающего списка? Например, при выборке по какому-либо критерию или полю, скажем, порядковому номеру. А если Вы вносите значения в одном порядке, а сортировку хотите установить в отличном? Тогда предлагаю следующую небольшую доработку.
Введите в таблицу справочника (который Вы используете в качестве поля с выпадающим списком), новое целочисленное поле, Order (размерностью 1 или 2, думаю, хватит).
Добавьте это поле в sq_ сервис справочника, датасет ds_ и карточку редактирования самого справочника wnd_ , так чтобы Вы вместе со значениями справочника могли и это поле задавать.
Для того чтобы выборка сортировалась по этому полу, в сервисе справочника для поля в параметрах включите сортировку и задайти позицию сортировки. По умолчанию значения справочников сортируются по имени. Укажите для поля Name позицию сортировки = 2, а для поля Order = 1.
Я у себя данный алгоритм проверил на примере справочника Города:
Сервисы tbl_City, sq_city, ds_city, wnd_cityEdit:
Порядок сортировки: Descending – убывающий, Ascending – возрастающий.
Вывел поле Order в карточку редактирования записи города. И, для проверки, в справочнике городов присвоил пару значений: для Астаны = 111, для Караганды = 110:
На следующем скриншоте видно, что сортировка прошла корректно
В справочнике Отрасли контрагентов были созданы 2 аналогичные отрасли. Как удалить одну из этих отраслей, если с ней уже созданы записи о контрагентах, при этом эта отрасль не является основной (то есть модуль фильтрации не выводит в реестр записи искомых контрагентов с данной отраслью)?
Вариант вручную перебрать все записи, естественно, не приветствуется.
В случае, если у Вас 3.4.0. вы можете настроить поиск дублей по отрасли и произвести слияние. В случае, если у Вас более ранняя версия, произвести слияние дублей можно вручную (если их мало) или скриптом (если много).
Для того, чтобы попробовать слить их вручную, рекомендуем одну из отраслей в справочнике переименовать, отфильтровать записи по той и другой и выяснить, каких меньше, а зтем перепривязать.
Тогда примерно так. Вот адаптированный для отраслей код:
--основная отрасль
UPDATE "tbl_Account" SET "FieldID"=(SELECT top 1"ID" FROM "tbl_Field" WHERE "tbl_Field"."Name"='Нужная отрасль')
WHERE "FieldID"=(SELECT top 1"ID" FROM "tbl_Field" WHERE "tbl_Field"."Name"='Ненужная отрасль')--неосновные отрасли
UPDATE "tbl_AccountIndustry" SET "FieldID"=(SELECT top 1"ID" FROM "tbl_Field" WHERE "tbl_Field"."Name"='Нужная отрасль')
WHERE "FieldID"=(SELECT top 1"ID" FROM "tbl_Field" WHERE "tbl_Field"."Name"='Ненужная отрасль')
Вводите туда свои названия отраслей. Если они абсолютно одинаковые, ненужную предварительно переименовываете, чтобы отличались.
Перед выполнением очень желательно сделать бэкап базы и проконсультироваться со службой поддержки.
Фильтровать контрагентов по неосновным отраслям можно также начиная с версии 3.4.0 - там есть подобные фильтры в коробочной версии. Для 3.3.2 фильтры можно добавить на уровне конфигурации.
"Шатная Елена" написал:Исходя из вышеописанного, понимаю, что без программистов вопрос по фильтрации контрагентов по доп. отраслям мне не решить.
Для этого нужно в "администраторе Terrasoft" нужно сконструировать нужный фильтр. Не совсем программирование, но лучше силами программистов или поддержки.
А в чём состоит основная задача, в чистке от ненужной отрасли или в доработке для возможности фильтра по дополнительным отраслям? Ведь если настроить такой фильтр и он выдаст несколько десятков результатов, то всё равно придётся менять отрасль вручную.
"Alimova Anna" написал:Фильтровать контрагентов по неосновным отраслям можно также начиная с версии 3.4.0 - там есть подобные фильтры в коробочной версии.
Анна, при формировании фильтра в разделе Контрагенты Отрасль = нужная отрасль (указанная как неосновная у контрагента Test) не приводит к тому, чтобы в реестре отобразился контрагент Test. Данная операция была произведена в 3.4.0.38.
Да, действительно, если указать запрос фильтра Контрагент-Отрасли (а не Отрасль, как я делала раньше :confused:), то все контрагенты с искомой отраслью отображаются. И дописывать ничего не нужно :smile:
Настраивая механизм поиска дублей в проекте на Terrasoft 3.X, работающем на базе Oracle, для таблицы контактов получал ошибку при попытке объединить дубли. При автоматической генерации хранимых процедур для объединения дублей возникло несоответствие порядка двух полей в таблице, что и привело к проблемам. В сгенерированной серверной логике они были в одном порядке, а на стороне клиентского приложения они перебирались в другом. Поля назывались, для примера, Com1 и Communication.
При этом ошибка воспроизводилась, если поиск дублей настраивали из клиента Terrasoft на одном компьютере и не воспроизводилась, если настраивали на другом. К сожалению или к счастью, первым компьютером был мой.
Как оказалось, причина возникновения проблемы была в этом фрагменте хранимой процедуры:
SELECT column_name,
data_type,
data_precision,
data_scale,
char_length,
char_used FROM all_tab_columns WHERE table_name ='tbl_Contact' AND owner = SYS_CONTEXT ('USERENV','CURRENT_SCHEMA') ORDERBY column_name;
На первый взгляд безобидная строка
ORDERBY column_name
сортирует различно в зависимости от локали на компьютере, откуда запускаем TSClient или Toad!
На моём ПК, если посмотреть:
SELECT*FROM nls_session_parameters;
Получим:
Цитата:
NLS_LANGUAGE RUSSIAN
NLS_TERRITORY RUSSIA
NLS_CURRENCY р.
NLS_ISO_CURRENCY RUSSIA
NLS_NUMERIC_CHARACTERS ,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD.MM.RR
NLS_DATE_LANGUAGE RUSSIAN NLS_SORT RUSSIAN
NLS_TIME_FORMAT HH24:MI:SSXFF
NLS_TIMESTAMP_FORMAT DD.MM.RR HH24:MI:SSXFF
NLS_TIME_TZ_FORMAT HH24:MI:SSXFF TZR
NLS_TIMESTAMP_TZ_FORMAT DD.MM.RR HH24:MI:SSXFF TZR
NLS_DUAL_CURRENCY р.
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
Вот из-за чего проблема! Надо использовать вместо RUSSIAN режим сортировки BINARY, где цифры расположены впереди латинских букв. А в RUSSIAN – позади. И в хранимке для объединения дублей передаются поля таблицы не в том порядке, что в ядре программы-клиента. Использование функции NLSSORT принудительно сортирует нужным образом.
Проблемная строка была заменена на:
ORDERBY NLSSORT(column_name,'NLS_SORT=BINARY');
Модификации подверглись tsp_GenerateSearchSP, tsp_GenerateLiveSearchSP, tsp_GenerateDuplicateSearch, и tsp_GenerateDuplicateMerge. В новых версиях Terrasoft 3.X изменение внесено, остальным рекомендую исправить самостоятельно.
Добрый день! Что-то никак не могу понять, по какому принципу работает сортировка проектов. По какому бы столбцу не пытался отсортировать, что-то меняется, но сортировкой назвать это сложно. Это какая-то общая проблема или это в нашей конфигурации что-то не так? И в каком направлении причины искать?
В проектах используется древовидный грид, поэтому обычная сортировка для них не выполняется. Для того, чтобы отсортировать проекты в удобном для Вас порядке используйте соответсвующие кнопки древовидного грида(см. вложение).
Данный функционал может быть изменен в конфигурации.
Для раздела Проектов сортировка всегда делается по скрытому полю SortOrder. Это сделано специально, чтобы не нарушался порядок проектов. А изменить порядок, как уже написали выше, можно с помощью кнопок "Вверх"-"Вниз" на панеле инструментов над реестром.
Поле должно рассчитываться при создании счета, изменении даты оплаты и ежедневно перерассчитываться. Для ежедневно расчета можно использовать функционал хранимых процедур вызывая их с необходимой периодичностью job-ом.
Данную проблему мы пытались решить в рамках инцидента, тогда я полное решение предоставить не смог. В переписке Вы предлагали следующий код:
Date1 =new Date (Dataset.Values('ContractPayDate'));
Date2 =new Date (Dataset.Values('PaymentDate'));
Date3 =new Date();
res =0;if(Date1.getTime()==0){
res =0}else{if(Date2.getTime()==0){
res = Math.floor((Date3.getTime()- Date1.getTime())/(1000*60*60*24))}else{
res = Math.floor((Date2.getTime()- Date1.getTime())/(1000*60*60*24))}};
Предлагаю данную логику реализовать по средствам job на MS SQL Server 2008, как советует Олег
Как это сделать:
1. Логике описанной в блоке javascript соответсвует следующий Update
update tbl_invoice
set DelayCalc = ISNULL(DateDiff(minute,ContractPayDate,ISNULL(PaymentDate,{fn NOW()})),0)
Пояснения:
Если ContractPayDate не заполнено тогда Просрочка DelayCalc = 0
ContractPayDate -> Null ->DateDiff(minute,ContractPayDate,ISNULL(PaymentDate,{fn NOW()} )) = NULL
ISNULL(DateDiff(minute,ContractPayDate,ISNULL(PaymentDate,{fn NOW()} ))) = 0
Если PaymentDate не заполнено, тогда берем текущую дату высчитывем DelayCalc = ContractPayDate - ТекущаяДата в минутах
ISNULL(PaymentDate,{fn NOW()} )
{fn NOW()} - взять текущую дату
ISNULL - функция отработки в случае если 1й аргумент NULL (в данном случае PaymentDate), если не NULL функция возвращает само значение 1го аргумента (в данном случае PaymentDate)
Если заполено ContractPayDate и DelayCalc вычисляем DelayCalc = ContractPayDate - PaymentDate в минутах
В случае если в MemoryDataset'e используется сортировка, то при сохранении записи, если изменение повлекло изменение порядка происходит "внутренняя сортировка", т.е. происходит сортировка записей всего грида/датасета, а перерисовывается только текущая строчка грида и в ней одной отображаются корректные данные. Каким образом можно обойти данную проблему.
Может можно отключить автоматическую сортировку перед сохранением.
Или волшебная команда типа grdData.Repaint (пробовал, не помогло).
Или может есть бинарные файлы с корректной работой этой связки, у нас версия 3.3.2.203.
Помогите уважаемые гуру!
Здравствуйте Валерий,
Пытался воспроизвести данную проблему, возникили вопросы.
Как вы заполняете MemoryDataset ?
Выводится ли все корректно если сделать обновление реестра?
Здравствуйте, Алексей!
Прикрепляю файл с отредактированными сервисами.
При запуске после добавление нескольких строк ->Сортировка -> редактирование даты, которое приводит к изменению порядка строк и проблема воспроизведется.
..\Bin\TSClient.exe /wnd=wnd_mdsvihGridArea1
MemoryDataset заполняется либо через dlData.Dataset.Append(), затем исправляется дата на необходимую.
Если предварительно сделать обновление реестра , то проблема исчезает.
В реестре после редактирования ячейки (после Post) происходит обновление текущей строки, но не предусмотрено, что из-за сортировки MemoryDataset сам изменит не только текущую, но и все остальные записи. Т.е. самым простым решением будет обновление датасета.
Но на самом деле хватило бы просто обновление реестра (без переоткрытия датасета). Поэтому в вашем случае можете применить обходное решение: на AfterPost делайте выключение/включение событий датасета. Выглядеть это будет так:
function dlDataOnDatasetAfterPost(Dataset){
Dataset.DisableEvents();
Dataset.EnableEvents();}