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

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, он будет уже другим.

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

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

function DeleteRecords(Dataset, Dictionary) {
   if (!Assigned(Dataset)) {
      if (!IsEmptyValue(Dataset) &&
         Assigned(Services.InformationsByUSI(Dataset))) {
         Dataset = GetSingleItemByCode(Dataset, 'DeleteRecords');
      } else {
         return;
      }
   }
   if ('DBDataset' == Dataset.ServiceTypeCode) {
      var Table = Dataset.SelectQuery.Items(0).FromTable;
   } else
   if ('Table' == Dataset.ServiceTypeCode) {
      var Table = Dataset;
   } else {
      return;
   }
   var dq = Services.CreateItem('DeleteQuery');
   dq.Table = Table;
   var TableFields = Table.Fields;
   var Filters = dq.Filters;
   var Parameters = dq.Parameters;

   var TableField;
   var FilterFieldType;
   var CompareOperatorType = cotEqual;
   var Parameter;
       
   var Keys = new VBArray(Dictionary.Keys()).toArray();
   var KeysLength = Keys.length;
   for (var i = 0; i KeysLength; i++) {
      TableField = TableFields.ItemsByName(Keys[i]);
      FilterFieldType =
         GetParameterTypeBySQLDataType(TableField.SQLDataType);
      Parameter = AddQueryParameter(Parameters, Keys[i],
         FilterFieldType, Dictionary(Keys[i]));
      AddQueryCompareFilter(Filters, Keys[i], TableField,
         Parameter, CompareOperatorType);
   }
   return dq.Execute();
}

первый параметр - может быть как именем сервиса так и самим объектом Таблица или Датасет;
второй параметр - это набор полей и их значений в виде Словаря.

Функция апдейта:

function UpdateRecord(Dataset, RecordID, Dictionary) {
   if (!Assigned(Dataset)) {
      if (!IsEmptyValue(Dataset) &&
         Assigned(Services.InformationsByUSI(Dataset))) {
         Dataset = GetSingleItemByCode(Dataset, 'UpdateRecord');
      } else {
         return;
      }
   }
   if ('DBDataset' == Dataset.ServiceTypeCode) {
      var Table = Dataset.SelectQuery.Items(0).FromTable;
   } else
   if ('Table' == Dataset.ServiceTypeCode) {
      var Table = Dataset;
   } else {
      return;
   }
   var uq = Services.CreateItem('UpdateQuery');
   uq.Table = Table;
   var TableFields = Table.Fields;
   var Filters = uq.Filters;
   var Parameters = uq.Parameters;
   var Columns = uq.ColumnsValues;

   var TableField;
   var FieldType;
   var CompareOperatorType = cotEqual;
   var Parameter;
   var Column;
       
   TableField = TableFields.ItemsByName('ID');
   Parameter = AddQueryParameter(Parameters, 'ID', sdtGUID, RecordID);
   AddQueryCompareFilter(Filters, 'ID', TableField,
      Parameter, CompareOperatorType);
       
   var Keys = new VBArray(Dictionary.Keys()).toArray();
   var KeysLength = Keys.length;
   for (var i = 0; i KeysLength; i++) {
      TableField = TableFields.ItemsByName(Keys[i]);   
      if (!Assigned(TableField)) {
         continue;
      }
      FieldType = GetParameterTypeBySQLDataType(TableField.SQLDataType);
      AddQueryParameter(Parameters, Keys[i], FieldType,
         Dictionary(Keys[i]));
      Column = Columns.CreateItem();
      Column.ParameterName = Column.KeyValue = Column.Name = Keys[i];
      Column.DataType = FieldType;
      Columns.Add(Column);
   }
   var Result = uq.Execute();
   return Result;
}

первый параметр - может быть как именем сервиса так и самим объектом Таблица или Датасет;
второй параметр - это ID записи в таблице, которую нужно обновить;
третий параметр - это набор полей и их значений в виде Словаря.

Также нужно подключить scr_DB

Пример использования:

var Dictionary = GetNewDictionary();
Dictionary('AccountID') = RecordID;
Dictionary('TypeID') = tOne;
DeleteRecords('tbl_AccountAddress', Dictionary);

var Dictionary = GetNewDictionary();
Dictionary('Name') = 'NewValue';
Dictionary('Name2') = 'NewValue2';
Dictionary('Name3') = 'NewValue3';
UpdateRecord('tbl_Account', RecordID, Dictionary);

Нравится

Поделиться

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

С некоторых пор функция UpdateRecord уже есть "в коробке", в скрипте scr_JobManagerUtils, но с небольшой доработкой, чтобы не пытались обновить значение ID.
После

      TableField = TableFields.ItemsByName(Keys[i]); 

добавлено:

      if (TableField.SQLName == 'ID') {
          continue;		
	  }
Показать все комментарии

Добрый вечер, путник зашедший на мой блог.
Если ты еще не спишь (а завтра рано вставать) вот тебе мой совет по исправлению Delete Query на администрируемую по записям таблицу, который использует Exist фильтры со ссылками на основную таблицу Delete Query.
Если ты, дорогой путник, не заснул после предыдущей фразы, вот тебе функция, которая исправляет данную проблему:

function ReplaceDQTablesAliasesByViewsInExistFilters(DeleteQuery, TableName, ViewName) {
        var IsAdmin = Connector.CurrentUser.IsAdmin;
        if (IsAdmin) {
                return;
        };             
        var Filters, ExistFilter, Filter, LeftTableAlias, RightTableAlias;
        for (var i = 0; i DeleteQuery.Filters.Count; i++) {
                ExistFilter =
                        DeleteQuery.Filters.Items(i);
                if (ExistFilter.FilterType != ftExists) {
                        continue;
                };
                for (var j = 0; j ExistFilter.TestExpression.ExpressionSelectQuery.Count; j++) {
                        Filters = ExistFilter.TestExpression.ExpressionSelectQuery.Items(j).Filters;
                        for (var k = 0; k Filters.Count; k++) {
                                Filter = Filters.Items(k);
                                if (!IsStringInArray(Filter.FilterType, [ftIsNull, ftCompare, ftLike, ftBetween, ftInclude])) {
                                        continue;
                                };
                                LeftTableAlias = Filter.TestExpression.TableAlias;
                                if (!IsEmptyValue(LeftTableAlias)) {
                                        Filter.TestExpression.TableAlias =
                                                LeftTableAlias.replace(TableName, ViewName);
                                };
                                if (Filter.FilterType == ftCompare) {
                                        RightTableAlias = Filter.ValueExpression.TableAlias;
                                        if (!IsEmptyValue(RightTableAlias)) {
                                                Filter.ValueExpression.TableAlias =
                                                        RightTableAlias.replace(TableName, ViewName);
                                        };                                     
                                };                             
                        }
                }
        }
}

Данная проблема может возникнуть где-угодно в версиях вплоть до 3.2.1.17 (а может и выше), но в базовой версии на написание сего меня подвергла операция "Раздать права доступа как у родительского элемента" детали Файлы. Под пользователем данная операция просто валится, т.к. для выполнения использует dq_GiveRightsByParentItem, которая не лишена проблемы описанной выше. Для исправления можно в scr_FilesDetailGridArea подправить ф-ию DeleteExistingFileAccessRecordsByParentItem: перед "DeleteQuery.Execute()" вызвать ф-ию описанную выше как-то так:

ReplaceDQTablesAliasesByViewsInExistFilters(DeleteQuery, 'tbl_FilesRight', 'vw_FilesRight');

Я не сомневаюсь, дорогой путник, что ты давно спишь. Счастливых тебе снов.

Нравится

Поделиться

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