Почему 1С трактует id = 0 в ключевом поле внешнего источника данных как пустую ссылку и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В нашей повседневной работе с 1С:Предприятием мы часто сталкиваемся с необходимостью интеграции с внешними системами и базами данных. Для этого в платформе предусмотрен механизм Внешних Источников Данных (ВИД). Мы можем подключать таблицы из внешних баз данных и работать с ними как с обычными объектами 1С. Однако, как показывает практика, при работе с объектными таблицами ВИД могут возникнуть неочевидные нюансы, особенно когда речь идет о ключевых полях. Сегодня мы подробно разберем одну из таких проблем: почему платформа 1С трактует значение 0 (ноль) в ключевом поле внешней таблицы как "пустую ссылку" и какие есть способы решения этой ситуации.

Суть проблемы: id = 0 как пустая ссылка

Представим ситуацию: мы подключили внешнюю базу данных (например, MSSQL) к нашей базе 1С как внешний источник данных. Во внешней БД существует таблица, которая содержит первичный ключ с автоинкрементом, и этот ключ по каким-то причинам начинает нумерацию с 0. Мы настроили эту таблицу в метаданных 1С как объектные данные, указав поле ключа (например, id) и установив для него свойство "Только чтение" в истину.

Допустим, в нашей внешней таблице есть следующие записи:

  1. id = 0, price = 100
  2. id = 1, price = 200

Теперь, если мы попытаемся найти эти записи программно, используя метод НайтиПоПолю(), мы обнаружим неожиданное поведение. Рассмотрим пример кода:


ИмяВИДа = "DWH"; // Например, имя вашего Внешнего Источника Данных
ИмяТаблицыВИДа = "Table1"; // Имя таблицы во Внешнем Источнике Данных
ИмяПоляСДанными = "price"; // Имя поля, по которому ищем

ТаблицаВИД = ВнешниеИсточникиДанных[ИмяВИДа].Таблицы[ИмяТаблицыВИДа];

// Поиск записи с price = 100 (которая имеет id = 0)
ЗначениеДляПоиска1 = 100;
ЗаписьВИДСсылка1 = ТаблицаВИД.НайтиПоПолю(ИмяПоляСДанными, ЗначениеДляПоиска1);
Сообщить("Пустая ссылка (id=0): " + ЗаписьВИДСсылка1.Пустая());

// Поиск записи с price = 200 (которая имеет id = 1)
ЗначениеДляПоиска2 = 200;
ЗаписьВИДСсылка2 = ТаблицаВИД.НайтиПоПолю(ИмяПоляСДанными, ЗначениеДляПоиска2);
Сообщить("Пустая ссылка (id=1): " + ЗаписьВИДСсылка2.Пустая());

// Поиск несуществующей записи с price = 777
ЗначениеДляПоиска3 = 777;
ЗаписьВИДСсылка3 = ТаблицаВИД.НайтиПоПолю(ИмяПоляСДанными, ЗначениеДляПоиска3);
Сообщить("Пустая ссылка (id=777): " + ЗаписьВИДСсылка3.Пустая());

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

  1. Пустая ссылка (id=0): Да
  2. Пустая ссылка (id=1): Нет
  3. Пустая ссылка (id=777): Да

Мы ожидаем, что первая запись (с id = 0) будет найдена и не будет трактоваться как пустая ссылка. Однако платформа 1С возвращает "Да" для поля Пустая(), как если бы запись не была найдена вовсе, что вводит в заблуждение и не позволяет корректно работать с данными. Это происходит из-за внутренней логики 1С.

Почему 1С так себя ведет? Механизм пустых ссылок

Чтобы понять причину такого поведения, давайте разберем, как 1С работает с пустыми ссылками для объектных данных.

В 1С для большинства примитивных типов данных существуют значения по умолчанию, которые трактуются как "пустые". Например:

  1. Для числового типа таким значением является 0.
  2. Для строкового типа — пустая строка "".
  3. Для даты — 01.01.0001 00:00:00.

Когда мы объявляем таблицу внешнего источника данных как "объектные данные", платформа 1С ожидает, что ее ключевое поле будет вести себя как "ссылочный тип" 1С. Пустая ссылка является неотъемлемой частью концепции ссылочных типов в 1С, и платформа реализует ее именно через пустое значение типа ключевого поля. Таким образом, если поле ключа имеет числовой тип и содержит значение 0, 1С интерпретирует его как пустую ссылку.

Важно не путать "пустую ссылку" в 1С с NULL в базах данных. NULL в СУБД обозначает отсутствие значения, тогда как пустая ссылка в 1С — это конкретное, хоть и "пустое" по значению, представление ссылки. Платформа 1С иногда может некорректно обрабатывать NULL значения из внешних источников, заменяя их на 0 для числовых полей или 01.01.1753 для даты в некоторых случаях.

Контракт метода НайтиПоПолю(), который мы использовали, фактически возвращает пустую ссылку в двух случаях:

  1. Если запись по заданным критериям не найдена.
  2. Если запись найдена, но значение ее ключевого поля соответствует "пустому значению" (например, 0 для числа), которое задано для этого ключа в метаданных конфигурации.

Решения проблемы: как работать с id = 0

Разберем несколько подходов, которые помогут нам корректно работать с записями, имеющими id = 0, во внешних источниках данных.

1. Изменение поля ключа в метаданных

Это один из наиболее эффективных способов, если мы хотим сохранить возможность работать с таблицей ВИД как с объектными данными и использовать стандартные механизмы 1С, такие как НайтиПоПолю(), ссылки на эти объекты в других объектах конфигурации, а также интерактивное редактирование через формы.

  1. Создайте дополнительную колонку во внешней БД: Мы добавляем новую колонку в нашу внешнюю таблицу (например, GUID, ExternalID или просто другой числовой столбец, гарантированно не содержащий 0). Заполните ее уникальными значениями для всех записей.
  2. Измените настройку ключа в метаданных 1С: В метаданных конфигурации, для нашей таблицы внешнего источника данных, вместо колонки id укажите в качестве поля ключа новую созданную колонку.

Преимущества:

  1. Код, использующий НайтиПоПолю(), начнет работать корректно, так как новое поле ключа не будет содержать значения, трактуемые как "пустая ссылка".
  2. Мы сможем ссылаться на эти объекты в реквизитах других объектов конфигурации.
  3. Интерактивное редактирование записей (двойной клик в динамическом списке) будет работать правильно, открывая форму редактирования существующей записи, а не форму создания новой.
  4. Поддерживается программное получение, редактирование и обновление "проблемных" записей через объектную технику 1С.

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

Мы должны помнить, что самовозрастающий первичный числовой ключ в MSSQL по умолчанию нумеруется с 1 при создании новой таблицы. Ситуация с id = 0 чаще всего возникает, когда этот параметр специально изменен (identity seed = 0).

2. Использование явных запросов 1С

Если модификация внешней таблицы невозможна или нежелательна, мы можем использовать явные запросы 1С для чтения данных из внешнего источника. Этот подход позволяет обойти проблемы объектной модели при поиске, но имеет ограничения при манипулировании данными.

  1. Создание запроса: Мы используем объект Запрос для выборки данных.
  2. Обработка результата: Результат запроса будет содержать все записи, включая те, у которых id = 0.

Рассмотрим пример, как мы можем получить данные с помощью запроса:


Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    ТаблицаВИД.id КАК id,
    |    ТаблицаВИД.price КАК price
    |ИЗ
    |    ВнешнийИсточникДанных.DWH.Таблица1 КАК ТаблицаВИД
    |ГДЕ
    |    ТаблицаВИД.price = &ЗначениеДляПоиска";

Запрос.УстановитьПараметр("ЗначениеДляПоиска", 100); // Поиск записи с price = 100

РезультатЗапроса = Запрос.Выполнить().Выгрузить();

Если РезультатЗапроса.Количество() > 0 Тогда
    Сообщить("Запись найдена через запрос. id = " + РезультатЗапроса[0].id + ", price = " + РезультатЗапроса[0].price);
Иначе
    Сообщить("Запись не найдена через запрос.");
КонецЕсли;

// Поиск записи с price = 200
Запрос.УстановитьПараметр("ЗначениеДляПоиска", 200);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Если РезультатЗапроса.Количество() > 0 Тогда
    Сообщить("Запись найдена через запрос. id = " + РезультатЗапроса[0].id + ", price = " + РезультатЗапроса[0].price);
Иначе
    Сообщить("Запись не найдена через запрос.");
КонецЕсли;

// Поиск несуществующей записи с price = 777
Запрос.УстановитьПараметр("ЗначениеДляПоиска", 777);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Если РезультатЗапроса.Количество() > 0 Тогда
    Сообщить("Запись найдена через запрос. id = " + РезультатЗапроса[0].id + ", price = " + РезультатЗапроса[0].price);
Иначе
    Сообщить("Запись не найдена через запрос.");
КонецЕсли;

Преимущества:

  1. Позволяет корректно находить записи, включая те, у которых id = 0.
  2. Не требует изменения структуры внешней БД.
  3. Универсален для чтения данных.

Недостатки:

  1. Только чтение: Этот подход решает только задачу поиска и проверки существования записи. Он не предоставляет возможности прямого манипулирования (получения объекта, редактирования и обновления) такой записи через объектную модель 1С. Мы не сможем получить "объект ВИД" для записи с id = 0, чтобы, например, изменить ее поле price и записать обратно.
  2. Отсутствие ссылочной целостности: Мы не сможем использовать ссылки на эти "объекты" в других объектах конфигурации 1С.
  3. Ограничения запросов к ВИД: Мы должны помнить, что в одном запросе 1С нельзя использовать одновременно внешний источник данных и данные информационной базы 1С. Также не поддерживается использование временных таблиц в запросах к внешним источникам данных.

Таким образом, если нам требуется лишь получить данные, включая записи с id = 0, и не нужно их модифицировать через объектную модель 1С, то явные запросы — это хороший выбор. Если же нам нужна возможность манипулировать данными и использовать ссылочную модель, то следует рассмотреть первый вариант.

3. Объявление таблицы как необъектной

Если функционал "объектных данных" нам не критичен, и мы готовы работать с таблицей ВИД как с набором данных, не имеющим в 1С прямой "ссылочной" сущности, мы можем изменить тип таблицы в метаданных.

  1. Измените свойство "Тип таблицы" в метаданных: В конфигураторе для нашей таблицы внешнего источника данных снимите галочку "Объектные данные".

Преимущества:

  1. Избавляет от проблемы трактовки id = 0 как пустой ссылки, поскольку понятие "пустой ссылки" перестает быть применимым к такой таблице.
  2. Позволяет напрямую работать с данными, включая id = 0, через запросы или другие методы чтения.

Недостатки:

  1. Переработка кода: Придется переписать весь код, который использовал объектную модель для работы с этой таблицей (добавление, изменение, удаление записей), поскольку методы типа НайтиПоПолю() и создание объекта таблицы больше не будут доступны или будут работать иначе.
  2. Отсутствие ссылок: Невозможно будет ссылаться на записи этой таблицы из других объектов конфигурации 1С.
  3. Отсутствие форм: Автоматические формы для просмотра и редактирования записей перестанут работать.

Этот подход может быть приемлем, если таблица ВИД используется исключительно для импорта данных или для отображения информации, без необходимости создания ссылок на ее записи или сложной объектной манипуляции в рамках 1С.

Дополнительные соображения при работе с ВИД

При работе с внешними источниками данных мы также должны учитывать следующие моменты:

  1. Добавление и обновление данных: Возможность добавления, изменения и удаления данных во внешних источниках программными средствами 1С появилась начиная с версии платформы 8.3.5.1068. При этом для полей таблицы можно настроить свойства ТолькоЧтение, РазрешитьNull и ЗначениеЗаполнения.
  2. Получение ID после записи: Иногда при записи данных во внешний источник, особенно с автоинкрементным ключевым полем, 1С может некорректно получать сгенерированный ID после записи, возвращая 0 или "объект не найден", даже если запись в БД фактически создана. Это еще один аспект, подчеркивающий сложности работы с ключевыми полями.
  3. "Костыли" для расширенного функционала: Для преодоления некоторых ограничений ВИД (например, отсутствие прямого вызова хранимых процедур с возвратом значений или выполнения произвольных SQL-скриптов) иногда используются обходные пути, такие как преобразование результатов в XML. Однако мы рекомендуем использовать такие подходы с осторожностью, поскольку они могут усложнить поддержку и отладку.

Заключение

Проблема трактовки id = 0 как пустой ссылки в объектных таблицах внешних источников данных в 1С является важным нюансом, который необходимо учитывать при проектировании интеграций. Мы рассмотрели несколько решений:

  1. Изменение поля ключа в метаданных 1С — наиболее полный подход, если требуется полноценная объектная работа.
  2. Использование явных запросов — для чтения данных, когда модификация не требуется или выполняется другими способами.
  3. Объявление таблицы как необъектной — если объектный функционал не нужен вовсе.

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

← На главную