SELECT SysRoleId 
FROM SysUserInRole 
WHERE SysUserId = '7F3B869F-34F3-4F20-AB4D-7480A5FDF647'
UNION SELECT '7F3B869F-34F3-4F20-AB4D-7480A5FDF647'

Как реализовать это в Terrasoft.Core.DB.Select ??

Нравится

1 комментарий
Лучший ответ

Евгений, в Select можно использовать Union, в который передавать другой Select.

Пример такого использования есть в функции DeactivateProcessEntryPoint схемы ProcessUserTaskUtilities:

var processListenersSelect =
	new Select(userConnection)
		.Column("Id")
	.From("SysEntityCommonPrcEl")
	.Where("RecordId").IsEqual(entityIdParameter)
	.And("RecordChangeType").IsEqual(Column.Parameter(EntityChangeType.Updated));
var select =
	new Select(userConnection)
		.Column("Id")
	.From("EntryPoint")
	.Where("EntityId").IsEqual(entityIdParameter)
	.And("IsActive").IsEqual(Column.Parameter(true))
	.Union(processListenersSelect);

А получить во втором колонку-константу можно при помощи:

Column.Parameter(new Guid("7F3B869F-34F3-4F20-AB4D-7480A5FDF647"))

 

Евгений, в Select можно использовать Union, в который передавать другой Select.

Пример такого использования есть в функции DeactivateProcessEntryPoint схемы ProcessUserTaskUtilities:

var processListenersSelect =
	new Select(userConnection)
		.Column("Id")
	.From("SysEntityCommonPrcEl")
	.Where("RecordId").IsEqual(entityIdParameter)
	.And("RecordChangeType").IsEqual(Column.Parameter(EntityChangeType.Updated));
var select =
	new Select(userConnection)
		.Column("Id")
	.From("EntryPoint")
	.Where("EntityId").IsEqual(entityIdParameter)
	.And("IsActive").IsEqual(Column.Parameter(true))
	.Union(processListenersSelect);

А получить во втором колонку-константу можно при помощи:

Column.Parameter(new Guid("7F3B869F-34F3-4F20-AB4D-7480A5FDF647"))

 

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

Добрый день

 

Есть интересная задача. Надо в БП по событию подключится к стороннему серверу MS-SQL и выполнить хранимую процедуру с двумя параметрами, которая вернет нам таблицу данных. Далее эту таблицу данных сохранить в кастомном разделе в системе и настроить связи с другими объектами.

Затык именно в подключении к стороннему серверу БД и вызове процедуры.

В MSSMS это выглядит как:

EXECUTE [dbo].[GetDataForCRM] @nomer,@date 

 

Нравится

8 комментариев
Лучший ответ

Добрый день!

Пробовали ли настраивать линкованные серверы? Если получится подключить сторонний сервер, то можно будет отправлять на него sql-запросы.

Добрый день!

Пробовали ли настраивать линкованные серверы? Если получится подключить сторонний сервер, то можно будет отправлять на него sql-запросы.

Указать имя сервера

Логин и пароль

Разрешить RPC для вызова ХП

Спасибо, помогло, частично. в MSSMS могу вызвать как

EXEC [LinkedServer].[LinkedDB].[dbo].[GetDataForCRM] @nomer,@date 

Как передать что надо вызывать на связанном сервере? Если я указываю полный путь процедуры, а не только имя, то получаю

EXEC [dbo].[[LinkedServer].[LinkedDB].[dbo].[GetDataForCRM]] @nomer,@date 

 

Алексей, функцию на связанном сервере запускают специальным образом, через OPENQUERY или sp_executesql, см. примеры тут.

SELECT SomeField
    FROM OPENQUERY([YOURSERVER], 'SELECT * FROM [SOMEDB].dbo.fn_SomeRemoteFunction(NULL)') tst

 

Зверев Александр,

т.е. через

StoredProcedure getDataForCRM = new StoredProcedure(UserConnection, "GetDataForCRM")
				.WithParameter("nomer", numberDoc)
				.WithParameter("date", dateDoc) as StoredProcedure;

не выйдет получить данные?

Так запускается хранимка на локальном сервере БД, а вопрос был о запуске на другом, связанном.

 

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

 

Также не очень понятно, Вам нужно хранимую процедуру или таки функцию? Таблицу с результатами может возвращать последняя.

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

Коллеги, добрый день,

 

Пожалуйста, поделитесь информацией о всевозможных причинах появления этой ошибки "При выполнении запроса произошла ошибка!" при выполнении запросов в SQL консоли.

 

Дело в том, что периодически один и тот же запрос то выполняется, то нет, в разное время нагрузки на сервер (даже когда она минимальная).

Нравится

1 комментарий
Лучший ответ

Добрый день.

Так бывает, тогда выполняется 'тяжелый' запрос и, на самом деле, даже, когда нагрузка на сервер минимальная, такая ситуация может возникнуть из-за того, что клиент ждет от сервера ответ какое-то время, если ответ не поступает, то появляется эта ошибка.

Однако подробнее ответить на Ваш вопрос сможет только разработчик данного дополнения. Его контакты есть на маркетплейсе https://marketplace.terrasoft.ua/app/sql-executor-creatio.

Добрый день.

Так бывает, тогда выполняется 'тяжелый' запрос и, на самом деле, даже, когда нагрузка на сервер минимальная, такая ситуация может возникнуть из-за того, что клиент ждет от сервера ответ какое-то время, если ответ не поступает, то появляется эта ошибка.

Однако подробнее ответить на Ваш вопрос сможет только разработчик данного дополнения. Его контакты есть на маркетплейсе https://marketplace.terrasoft.ua/app/sql-executor-creatio.

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

Добрый день!

Планирую интеграцию одного приложения с Creatio CRM через внешний API на OData4.
Читаю документацию, и вижу, что любую сущность можно получить через отдельный запрос. Но изначально система не знает какие сущности могут быть в системе, и чтобы получить их список нужно выполнить SQL-запрос (по инстркуции):
 

для MySQL
SELECT * FROM INFORMATION_SCHEMA.TABLES
для Oracle
SELECT * FROM ALL_TABLES
для PostgreSQL
SELECT table_name FROM information_schema.tables

 

У меня есть только данные для авторизации юзера через OData4, и этот запрос мне никак не выполнить.

Есть ли какой-то способ через внешний API получить список всех таблиц?

Нравится

4 комментария
Лучший ответ

Павел, можно ещё

https://{server}/0/odata/

Так будет результат в  JSON.

 

Либо воспользоваться информацией из таблицы SysSchema, которая тоже доступна по OData. За объекты отвечает EntitySchemaManager, получить список можно с таким фильтром:

https://{server}/0/odata/SysSchema?$filter=ManagerName%20eq%20%27EntitySchemaManager%27

А вариант с выбором из базы не очень подходит, поскольку зависит от платформы, плюс не все таблицы в базе связаны с объектами конфигурации и могут быть доступны по OData. И наоборот, часть объектов сделаны не на основе table, а по view.

Нашел сам

Запрос на 

https://{server}/0/odata/$metadata

возвращает внутри <Schema Namespace="Terrasoft.Configuration.OData" xmlns="http://docs.oasis-open.org/odata/ns/edm"> все сущности

Павел, можно ещё

https://{server}/0/odata/

Так будет результат в  JSON.

 

Либо воспользоваться информацией из таблицы SysSchema, которая тоже доступна по OData. За объекты отвечает EntitySchemaManager, получить список можно с таким фильтром:

https://{server}/0/odata/SysSchema?$filter=ManagerName%20eq%20%27EntitySchemaManager%27

А вариант с выбором из базы не очень подходит, поскольку зависит от платформы, плюс не все таблицы в базе связаны с объектами конфигурации и могут быть доступны по OData. И наоборот, часть объектов сделаны не на основе table, а по view.

Зверев Александр, Спасибо! А метадату конкретной сущности как получить? 
Требуется узнать какие поля у модели.

В https://{server}/0/odata/$metadata всё есть.

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

Добрый день, подскажите, как можно написать подобный запрос на esq? Желательно на стороне сервера. Смотрел в сторону addAggregationSchemaColumn, но там можно либо посчитать количество, либо применить min, max и т.п, а эти функции работают только с числовыми полями.

select
(select top 1 Number from ContactCommunication where ContactId = c.Id and CommunicationTypeId = 'EE1C85C3-CFCB-DF11-9B2A-001D60E938C6')
from Contact c

Нравится

3 комментария
Лучший ответ

 Запрос в скобках:

var esq = new EntitySchemaQuery(userConn.EntitySchemaManager, "ContactCommunication");
esq.AddColumn("Number");
esq.RowCount = 1;
esq.Filters.Add(esq.CreateFilterWithParameters(
	FilterComparisonType.Equal,
	"ContactId ", contactId));
esq.Filters.Add(esq.CreateFilterWithParameters(
	FilterComparisonType.Equal,
	"CommunicationTypeId", "EE1C85C3-CFCB-DF11-9B2A-001D60E938C6"));
var collection = esq.GetEntityCollection(userConn);

 

 Запрос в скобках:

var esq = new EntitySchemaQuery(userConn.EntitySchemaManager, "ContactCommunication");
esq.AddColumn("Number");
esq.RowCount = 1;
esq.Filters.Add(esq.CreateFilterWithParameters(
	FilterComparisonType.Equal,
	"ContactId ", contactId));
esq.Filters.Add(esq.CreateFilterWithParameters(
	FilterComparisonType.Equal,
	"CommunicationTypeId", "EE1C85C3-CFCB-DF11-9B2A-001D60E938C6"));
var collection = esq.GetEntityCollection(userConn);

 

Литвинко Павел, спасибо! Попробую. Только не разбираясь в деталях, увидел проблему, что в таком случае в выборку не по попадут контакты с незаполненным полем, потому запрос не подойдет.

В идеале нужно получить поля из Контакта и поле из средств связи (в том числе пустое)

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

В Вашем случае нужно будет действовать наоборот, выбирать из таблицы Contact с добавлением колонок из таблицы детали средств связи, таких как [ContactCommunication:Contact:Id].CommunicationType и [ContactCommunication:Contact:Id].Number, по таким же колонкам можно и накладывать фильтр.

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

Добрый день, при попытке слияния двух контаков, если у проигравшего есть заказ, то процесс дедупликации падает с ошибкой: 

System.Data.SqlClient.SqlException (0x80131904): Конфликт инструкции DELETE с ограничением REFERENCE "FKZt10BIrUNMXjPYmQeSNwKs8aI". 
Конфликт произошел в базе данных "ClarinsInstall", таблица "dbo.Order", column 'ContactId'.

При отладке не увидела ContactId, среди ключей таблицы Order, по которым идет перепривязка данных (есть только CreatedById и OwnerId),
подскажите, с чем это может быть вызвано?

Нравится

3 комментария
Лучший ответ

Зверев Александр пишет:
Проверьте Order в дизайнере объектов, там должно быть такое справочное поле. А если нет, то было и его удалили в объекте, но по какой-то причине осталось в базе (возможно, объект не скомпилировали).

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

Иначе проблема может воспроизвестись при следующем слиянии дублей.

Это как раз и значит наличие записи в Order, ссылающееся полем ContactId на сливаемый контакт. Проверьте Order в дизайнере объектов, там должно быть такое справочное поле. А если нет, то было и его удалили в объекте, но по какой-то причине осталось в базе (возможно, объект не скомпилировали). В таком случае, если есть возможность, подключитесь к БД, найдите эту запись и замените значение ContactId на null.

Зверев Александр пишет:
Проверьте Order в дизайнере объектов, там должно быть такое справочное поле. А если нет, то было и его удалили в объекте, но по какой-то причине осталось в базе (возможно, объект не скомпилировали).

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

Иначе проблема может воспроизвестись при следующем слиянии дублей.

Кстати, не уверен, что повторное добавление поля в объект пройдёт успешно без последствий. У колонки в базе прописывается GUID в Extended Properties, он будет уже другим.

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

Всем привет.

Ввиду не внятности документации прошу помощи.

Как составить такой запрос используя ESQ на клиенте?

SELECT account.UsrINN,(SELECT TOP(1) ModifiedOn FROM Activity activity WHERE activity.AccountId=account.Id ORDER BY ModifiedOn) AS LastComunicationDate  FROM Account account  WHERE account.UsrINN = '7730616959' 

Собственно запрос выводит дату последней измененной активности по контрагенту.

 

 

Нравится

9 комментариев
Лучший ответ

Последовский Роман,

А так?

var esq = Ext.create("Terrasoft.EntitySchemaQuery", { rootSchemaName: "Account" });
esq.addColumn("UsrINN");
esq.addAggregationSchemaColumn("[Activity:Account].ModifiedOn", 5, "MaxModifiedOn");
esq.filters.addItem(esq.createColumnFilterWithParameter(3, "UsrINN", "7730616959"));
esq.getEntityCollection(function (result) {
	if (result.success &amp;&amp; result.collection.getCount() &gt; 0) {
		result.collection.collection.each(function(item) {
			//some logic
		}, this);
	}
}, this);

 

Роман, приветствую,

А у вас проблема в подзапросе или в сортировке?

Есть в сортировке, то возможно вам поможет вот это: https://community.terrasoft.ru/questions/entityschemaquery-order

Если в подзапросе, то можно

1. Сделать один запрос, а в его колбэке (или в цикле foreach, если это C# сделать еще один запрос).

2. Судя по всему подзапрос можно заменить обычным джойном.

На клиенте? Тогда как-то так:

var esq = Ext.create("Terrasoft.EntitySchemaQuery", { rootSchemaName: "Activity" });
 
esq.addColumn("Account.UsrINN", "AccountINN");
var column = esq.addColumn("ModifiedOn");
column.orderDirection = Terrasoft.OrderDirection.ASC; //asc по modifiedOn
 
esq.rowCount=1; //top1
 
esq.filters.addItem(esq.createColumnFilterWithParameter(3, "Account.UsrINN", "7730616959")); //фильтр по INN
 
esq.getEntityCollection(function (result) {
	if (result.success &amp;&amp; result.collection.getCount() &gt; 0) {
		var item = result.collection.getByIndex(0);
		var INN = item.get("AccountINN");
		var modOn = item.get("ModifiedOn");
	}
}, this);

 

Мне кажется, тут сам SQL-запрос надо вывернуть наизнанку:

SELECT max(ModifiedOn) FROM Activity activity
left join Account account on activity.AccountId=account.Id 
WHERE account.UsrINN = '7730616959'

То есть получим запрос с одной колонкой и одним фильтром по прямой связи. Заодно вместо TOP 1 можно просто максимум при помощи addAggregationSchemaColumn. А затем фильтруем, применив createColumnFilterWithParameter к колонке «Account.UsrINN».

 

Спасибо большое, вариант Данилы работает, но

Затык как раз в том что мне нужно вывести именно список контрагентов(UsrINN), а уже к каждому из них привязать дату последней работы с активностью.

 

 

 

Судя по исходному вопросу, код контрагента известен и равен 7730616959, следовательно, в списке всегда будет одна строка. Если таких номеров несколько, можно получить этим кодом в цикле для каждого.

Это часть системы поиска дублей.

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

ИНН:123

10 контрагентов, но у 5ти из них есть активности.

Если я сначала полезу в таблицу активности, то мне выведется на экран только 5ть контрагентов, а реально их 10.

В самом начале я указал именно тот SQL запрос, который нужно изобразить с помощью ESQ.

Конечно можно это сделать двумя циклами, но хочется понять можно ли это сделать одним запросом

 

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

Последовский Роман,

А так?

var esq = Ext.create("Terrasoft.EntitySchemaQuery", { rootSchemaName: "Account" });
esq.addColumn("UsrINN");
esq.addAggregationSchemaColumn("[Activity:Account].ModifiedOn", 5, "MaxModifiedOn");
esq.filters.addItem(esq.createColumnFilterWithParameter(3, "UsrINN", "7730616959"));
esq.getEntityCollection(function (result) {
	if (result.success &amp;&amp; result.collection.getCount() &gt; 0) {
		result.collection.collection.each(function(item) {
			//some logic
		}, this);
	}
}, this);

 

Варфоломеев Данила,

Памятник поставить мало! Спасибо)

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

Интересует возможность разнесения базы на файловые группы для разнесения групп на разные жёсткие диски (разный объем и скорость).

Есть ли в ВРМ встроенная возможность создавать определенные таблицы, например xxxFile на определенной файловой группе? И вообще, есть ли возможность управлять созданием таблиц на файловых группах?

Есть ли у кого-то положительный опыт разнесения базы на ФГ?

Нравится

1 комментарий

Добрый день, Алексей. К сожалению, в данный момент этот функционал не поддерживается. Мы учтем возможность его реализации в будущих версиях приложения.

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

Всем доброго времени суток.
Хотелось бы узнать если ли возможность в элемент бизнес-процесса "Задание-сценарий" вставить запрос к базе данных и если есть, то можно хоть самый простой пример c Select.

Нравится

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

Сергей, такая возможность есть.
Пример можно посмотреть на академии в этой статье.

Спасибо Наталия. Буду разбираться.

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

Зачастую приложение разработанное на BPMOnline, как и последующие его доработки требуется предоставлять в виде "all inclusive", т.е. всё что нужно, в т.ч. и настройки и бэкраунд-данные должны быть в Ваших пакетах, и единственное что остается сделать клиенту или его специалистам - это установить их. Но в части механизма переноса данных в самой архитектуре приложения есть проблемные моменты, в основном часто встречающиеся юзкейсы:

1) Перенос/инсталляция элементов организационной структуры, или изменений в ней через пакет.
проблематика: Через "Данные" не переносятся, подключить их в пакет у Вас конечно получится с кучей связей, но при установке вас ожидает фиаско, в первую очередь потому, что требуется четкое соблюдение порядка добавления записей, в таблице SysAdminUnits есть внутренние связи FK между колонками Id и ParentId (значение одной колонки ссылается на значение из другой колонки этой же таблицы). Этими значениями определяется иерархия, по этому Вам необходимо добавлять записи в соответствии с их положением в дереве: от корня в глубь.
совет: организовывайте вашу структуру в мастере в том числе проставьте галочки на "Есть руководители" (т.к. создается отельный орг.юнит), после чего в любом удобном инструменте просмотра таблиц БД, в таблице SysAdminUnits отсортируйте записи по колонке "CreatedOn" в порядке возрастания, и в таком виде экспортируйте INSERT инструкции для каждой записи отдельным блоком.
PS: так же зачастую требуется перенос значений из таблиц SysAdminUnitsInRole (пользователи включенные в орг.юнит), и SysFuncRoleInOrgRole (связи орг.юнитов друг с другом),
а перед инсталяцией орг.юнитов пользователей - сначала инсталируйте контакты с которыми они будут связаны.

2) Перенос/инсталляция настроек рабочих мест, или изменений в них через пакет.
проблематика: По той причине что организационную структуру Вы через данные не переносите, настройка рабочих мест просто не даст Вам создать данные так, как вы не включаете в виде данных необходимые значения для орг.юнитов.
PS: Сами рабочие места, и даже разделы вы можете перенести через данные,
но если вам нужны данные о пользователях группах пользователей в раб.местах - тут только SQL-скрипт. Так что лучше уж тогда все переносить в скрипте.

3) Перенос/инсталляция настройки колонок в реестрах, деталях, окнах выбора и т.д.
проблематика: Через "Данные" не переносятся, т.к. содержат бинарные данные в 2-х колонках (ObjectData, ObjectDifference) таблицы SysProfileData по какой-то причине не включаются в пакет вместе с данными, и аналогично предыдущему пункту вы можете создать такие "Данные", но при инсталляции получите записи с пустыми вышеупомянутыми колонками.
совет: Опять же настраиваем "ручками в системе" потом отлавливаем новые записи в любом SQL-view, дампим записи (их будет 2-3 на каждый элемент настройки колонок) в блоках INSERT, но в данном случае не забудьте, что вам при инсталляции надо будет проверить существуют ли в таблице записи с такими же соотношениями колонок "Key" - "ContactId" и удалить их (не обновлять) если они есть после чего вставлять Ваши записи.

Вообщем у Вас может получиться объемистая часть данных которые надо включать в пакеты в виде SQL-скрипта. И идеальный подход для этого конечно использование подхода "Вставить, а если существует - обновить".
Так вот TSQL не предлагает каких либо "сахарных" инструкций aka MySQL UPSERT или PostgreSQL ON DUPLIKATE KEY
В TSQL ближайший аналог это монстроузный MERGE который можно использовать для реализации вышеупомянутого подхода, ну конечно можно еще c использование IF EXISTS писать еще более "монструозные простыни", и даже делать SELECT->FOR-IF-UPDATE/INSERT но это уж простите меня - совсем "нубство".
Первый взгляд на MERGE вас конечно немного испугает и заставит пропотеть, особенно учитывая тот факт что вам не обойтись без многократного описания колонок и их соотношений, а в большинстве таблиц с которыми Вам его надо будет применять - более 10-ти колонок. :) а Вам понадобится их перечисление в 4-х местах в разном виде.
Но на помощь нам приходят современные средства разработки !
Итак... вот вам пошаговый рецепт составления такого скрипта на примере составления MERGE для переноса/обновления из таблицы SysAdminUnits в среде Jetbrains DataGrip (если вы или Ваши друзья коллеги студенты IT-смежной специальности, Вы без проблем получите ключ на год на весь пантеон из продуктов, WebStrom просто незаменим для JavaScript, особенно в свете того что с 7.10 версии наконец-то можно работать с пакетами в файловом режиме)
PS: наверняка в SQL Managment Studio можно обвеситься плагинами и получить аналогичную функциональность (я сейчас про некоторые хоткеи и мультикурсорность, которые и есть суть - все сильно упрощают), если это так - прошу знатоков отписаться в комментариях что для этого потребуется, или же подтвердить что там так не выйдет.
Итак поехали:
Копируем шаблон конструкции

DROP TABLE IF EXISTS #Temp;
CREATE TABLE #Temp
(

);

MERGE TargetTable AS dst
USING #Temp AS src
ON (dst.Id=src.Id)
WHEN MATCHED THEN
    UPDATE SET

WHEN NOT MATCHED BY TARGET THEN
    INSERT
    (

    )
    VALUES (

    );
GO

ДАЛЬНЕЙШИЕ ИЗОБРАЖЕНИЯ ЭТО GIF-АНИМАЦИЯ, CLICKайте и ПРОСМАТРИВАЙТЕ
Открываем необходимую таблицу в структуре и переходим к ее определению (вкладка DDL)
поиск в боковой схеме - простой набор символов с клавиатуры при выделении любой таблицы
открытие таблицы - F4


и копируем определение колонок в определение колонок временной таблицы шаблона, даем временной таблице осмысленное имя,

и вот тут начнется "магия" мультиселекта это IDE
Alt+j (установка мультикурсорности на обнаруженных паттернах выделенного фрагмента)
Нам необходимо избавиться в обявлении колонок от значений "по умолчанию" (не знаю почему, но с этим иногда бывают проблемы, в контексте нашей задачи, проще избавиться, чтобы наверняка).
А так же удалем FK определения они нам само собой тоже не нужны

При помощи мощи мультиселекта и хоткеев паттерного выделения (выделить слово, добить/убрать из выделения символ) "творим магию"

обратите внимание на то, что лишние запятые изначально оставляются чтобы все строки соответствовали паттерну, а потом удаляются, остается установить имя целевой таблицы.
и вот мы получили вот такой вот скрипт
DROP TABLE IF EXISTS #TempSysAdminUnits;
CREATE TABLE #TempSysAdminUnits
(
  Id UNIQUEIDENTIFIER PRIMARY KEY NOT NULL,
  CreatedOn DATETIME2,
  CreatedById UNIQUEIDENTIFIER,
  ModifiedOn DATETIME2,
  ModifiedById UNIQUEIDENTIFIER,
  Name NVARCHAR(250) NOT NULL,
  Description NVARCHAR(250) NOT NULL,
  ParentRoleId UNIQUEIDENTIFIER,
  ContactId UNIQUEIDENTIFIER,
  TimeZoneId NVARCHAR(250) NOT NULL,
  UserPassword NVARCHAR(250) NOT NULL,
  SysAdminUnitTypeValue INT NOT NULL,
  AccountId UNIQUEIDENTIFIER,
  Active BIT NOT NULL,
  LoggedIn BIT NOT NULL,
  SynchronizeWithLDAP BIT NOT NULL,
  LDAPEntry NVARCHAR(250) NOT NULL,
  LDAPEntryId NVARCHAR(250) NOT NULL,
  LDAPEntryDN NVARCHAR(500) NOT NULL,
  IsDirectoryEntry BIT NOT NULL,
  ProcessListeners INT NOT NULL,
  SysCultureId UNIQUEIDENTIFIER,
  LoginAttemptCount INT NOT NULL,
  SourceControlLogin NVARCHAR(250) NOT NULL,
  SourceControlPassword NVARCHAR(250) NOT NULL,
  PasswordExpireDate DATETIME2,
  HomePageId UNIQUEIDENTIFIER,
  ConnectionType INT NOT NULL,
  UnblockTime DATETIME2,
  ForceChangePassword BIT NOT NULL,
  LDAPElementId UNIQUEIDENTIFIER,
  DateTimeFormatId UNIQUEIDENTIFIER,
);

MERGE SysAdminUnits AS dst
USING #TempSysAdminUnits AS src
ON (dst.Id=src.Id)
WHEN MATCHED THEN
    UPDATE SET
      dst.CreatedOn=src.CreatedOn,
      dst.CreatedById=src.CreatedById,
      dst.ModifiedOn=src.ModifiedOn,
      dst.ModifiedById=src.ModifiedById,
      dst.Name=src.Name,
      dst.Description=src.Description,
      dst.ParentRoleId=src.ParentRoleId,
      dst.ContactId=src.ContactId,
      dst.TimeZoneId=src.TimeZoneId,
      dst.UserPassword=src.UserPassword,
      dst.SysAdminUnitTypeValue=src.SysAdminUnitTypeValue,
      dst.AccountId=src.AccountId,
      dst.Active=src.Active,
      dst.LoggedIn=src.LoggedIn,
      dst.SynchronizeWithLDAP=src.SynchronizeWithLDAP,
      dst.LDAPEntry=src.LDAPEntry,
      dst.LDAPEntryId=src.LDAPEntryId,
      dst.LDAPEntryDN=src.LDAPEntryDN,
      dst.IsDirectoryEntry=src.IsDirectoryEntry,
      dst.ProcessListeners=src.ProcessListeners,
      dst.SysCultureId=src.SysCultureId,
      dst.LoginAttemptCount=src.LoginAttemptCount,
      dst.SourceControlLogin=src.SourceControlLogin,
      dst.SourceControlPassword=src.SourceControlPassword,
      dst.PasswordExpireDate=src.PasswordExpireDate,
      dst.HomePageId=src.HomePageId,
      dst.ConnectionType=src.ConnectionType,
      dst.UnblockTime=src.UnblockTime,
      dst.ForceChangePassword=src.ForceChangePassword,
      dst.LDAPElementId=src.LDAPElementId,
      dst.DateTimeFormatId=src.DateTimeFormatId
WHEN NOT MATCHED BY TARGET THEN
    INSERT
    (
      Id,
      CreatedOn,
      CreatedById,
      ModifiedOn,
      ModifiedById,
      Name,
      Description,
      ParentRoleId,
      ContactId,
      TimeZoneId,
      UserPassword,
      SysAdminUnitTypeValue,
      AccountId,
      Active,
      LoggedIn,
      SynchronizeWithLDAP,
      LDAPEntry,
      LDAPEntryId,
      LDAPEntryDN,
      IsDirectoryEntry,
      ProcessListeners,
      SysCultureId,
      LoginAttemptCount,
      SourceControlLogin,
      SourceControlPassword,
      PasswordExpireDate,
      HomePageId,
      ConnectionType,
      UnblockTime,
      ForceChangePassword,
      LDAPElementId,
      DateTimeFormatId
    )
    VALUES (
      src.Id,
      src.CreatedOn,
      src.CreatedById,
      src.ModifiedOn,
      src.ModifiedById,
      src.Name,
      src.Description,
      src.ParentRoleId,
      src.ContactId,
      src.TimeZoneId,
      src.UserPassword,
      src.SysAdminUnitTypeValue,
      src.AccountId,
      src.Active,
      src.LoggedIn,
      src.SynchronizeWithLDAP,
      src.LDAPEntry,
      src.LDAPEntryId,
      src.LDAPEntryDN,
      src.IsDirectoryEntry,
      src.ProcessListeners,
      src.SysCultureId,
      src.LoginAttemptCount,
      src.SourceControlLogin,
      src.SourceControlPassword,
      src.PasswordExpireDate,
      src.HomePageId,
      src.ConnectionType,
      src.UnblockTime,
      src.ForceChangePassword,
      src.LDAPElementId,
      src.DateTimeFormatId
    );
GO

потратив на его составление менее 5-ти минут :)
Ну а далее в этот скрипт перед операцией MERGE перенесите INSERT конструкциями, то что требуется изменив назначение на временную таблицу

Вуаля... итого 5-7 минут и готово.
Другими способами и копипастой, такой скриптик писать с таким огромным разношерстным объявлением - минут 20-30 :)
Так что юзайте возможности современных средств разработки коллеги.

Нравится

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

мы на 79 переносили оргструктуру, пользователей и даже права на объекты через данные
там есть два момента:
1) надо сбросить, а после установки пакета вернуть, триггер TRSysAdminUnitRoot (без триггера не было проблем с установкой SysAdminUnit - я так понимаю механизм установки следит за порядком SAME REFERENCE записей)
2) (не уверен надо ли это вообще)) следить за порядком установки (он алфавитный) - контакты перед пользователями, роли перед пользователями и вхождениями ролей в роли и пользователей в роли (SysUserInRole, т.к. SysAdminUnitsInRole - заполняется в процессе актуализации оргструктуры см сленд пункт)
3) после установки пакета выполнить tsp_ActualizeAdminUnitInRole, просто чтобы не забыть сделать это вручную
4) осторожнее с корневыми ролями и Supervisor'ом

и вроде с SysProfileData тоже не было проблем

"Андросов Дмитрий" написал:3) после установки пакета выполнить tsp_ActualizeAdminUnitInRole, просто чтобы не забыть сделать это вручную

Да, это важное и полезное дополнение :)
Спасибо.

"Андросов Дмитрий" написал:(не уверен надо ли это вообще)) следить за порядком установки (он алфавитный)

ну вот это прям сомнительное утверждение :)
Ну если я конечно понимаю о чем идет речь.
Так как у потомка в колонке ParentId идет FK на Id этой-же таблицы, т.е. порядок просто ОБЯЗАН быть соблюден, иначе лови ошибки FK инсерта строк на уровне таблицы SysAdminUnit, при установке через данные именно в этом и проблема:
Мы профилировали запросы и там INSERT идет через перечисление VALUES а не каждая строка отдельной инструкцией... (так что тут фактически повезет / не повезет как сортируются значения в перечислении мне не ведомо да и мы в любом случае не можем это контролировать)

"Севостьянов Илья Сергеевич" написал:да и мы в любом случае не можем это контролировать

мы можем контролировать порядок иначе:
создаем данные на корневые роли, называем "SysAdminUnit1Roles"
далее на роли второго уровня, называем "SysAdminUnit2Roles"
далее на третий, называем "SysAdminUnit3Roles" и т.д.
[странно, но мы переносили 5ти уровневую оргструктуру в одних данных, поэтому и сомневался - не разбирает ли установщик данных, что в рамках одних данных установить сначала?]
после этого на самих пользователей "SysAdminUnit999Users" (контакты пользователей установили ранее)
после этого на SysUserInRole для переноса вхождения пользователей в группы

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

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