Sunday, June 14, 2015

Ошибка: Не найден идентификатор в справочнике "Идентификаторы объектов метаданных"

Полный текст ошибки:
Для объекта метаданных "ххх.ххх"
"Не найден идентификатор в справочнике "Идентификаторы объектов метаданных". Для разработчика: возможно требуется обновить вспомогательные данные, которые влияют на работу программы. Для выполнения обновления можно:
- воспользоваться внешней обработкой
"Инструменты разработчика: Обновление вспомогательных данных",
- либо запустить программу с параметром командной строки 1С:Предприятия 8 "/С ЗапуститьОбновлениеИнформационнойБазы",
- либо увеличить номер версии конфигурации, чтобы при очередном запуске выполнились процедуры обновления данных информационной базы."

Конфигурация, где у меня (после создания новой роли) возникла  ошибка "Бухгалтерия предприятия 3.0", но подозреваю что-то подобное должно вылезать и на других конфигурациях, написанных на управляемых формах.

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

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

Если совсем не хочется ничего делать можете воспользоваться моей обработкой.

Wednesday, January 09, 2008

Требования в внешней обработке, регистрируемой в качестве дополнительной печатной формы (1с восьмая)

Требования в внешней обработке, регистрируемой в качестве дополнительной печатной формы.
Для того, чтобы внешняя обработка могла использоваться в качестве дополнительной печатной формы, она должна удовлетворять некоторым требованиям. В реквизитах обработки должен существовать реквизит СсылкаНаОбъект, имеющий тип данных, включающий в себя ссылку на объект конфигурации, для которого эта форма может быть зарегистрирована. Например ДокументСсылка.ПоступлениеТоваровУслуг. Через данный реквизит во внешнюю обработку будет передаваться ссылка на объект базы данных, для которого формируется печатная форма. В самом механизме вывода дополнительных печатных форм его значение не используется. Кроме того, обязательным требованием для внешней обработки является наличие в ее модуле экспортной функции Печать(). Возвращаемым результатом выполнения данной функции должен быть сформированный табличный документ. Важно учитывать, что показывать (печатать) этот табличный документ внутри функции нельзя, он будет показан (напечатан) стандартным механизмом конфигурации. Примерный вид функции Печать():

Функция Печать() Экспорт
ТабДок = Новый ТабличныйДокумент;
Запрос = Новый Запрос;
Запрос.Текст = «
|ВЫБРАТЬ …. ИЗ Документ…
|ГДЕ Ссылка = &СсылкаНаДокумент»;
// передача в запрос значения описанного выше реквизита
Запрос.УстановитьПараметр(«СсылкаНаДокумент», СсылкаНаОбъект);
...
// заполнение табличного документа
...
Возврат ТабДок;
КонецФункции

Дополнительно для внешней обработки определены еще некоторые условия, которые не являются обязательными к выполнению. С их помощью возможно выполнение регистрации внешней обработки в базе полностью в автоматическом режиме.
1. В качестве синонима внешней обработки можно указать пользовательское представление данной печатной формы, которое при регистрации будет автоматически помещено в наименование этой формы
2. Для внешней обработки может быть определен дополнительный макет Параметры_Авторегистрации, которые в первом столбце содержит перечень объектов конфигурации, которым данная форма будет принадлежать. Имя объекта должно совпадать по синтаксису с вызовом соответствующего менеджера объекта, например "Документы.ПоступлениеТоваровУслуг" или "Справочники.ОсновныеСредства".

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


Хотел запостить свой самописный пример, но что-то не нашел.....
Найду - дам...

ЗЫ А потом этот epf подключаешь к конфе, как в 7.7 через сервис-доп печатные формы, но в отличии от 7.7 он хранится сразу в БД и поэтому при изменении надо его переподключать...

Tuesday, December 11, 2007

Нетипичное использование компоненты УРБД в системе 1С:Предприятие 7.7

http://www.kb.mista.ru/article.php?id=45

При организации обмена между двумя информационными базами (не обязательно 1С:Предприятие) возникают две сложности: как узнать, а какие же объекты были изменены с момента последней отгрузки информации (чтобы выгружать не все данные, а только необходимое), и как отследить доставку пакетов обмена (если пропал один или два пакета, то это не должно приводить к глобальному сбою: пакеты, по которым не пришло подтверждение доставки, должны быть просто и прозрачно отправлены при следующем сеансе обмена). Автор статьи: romix | Редакторы: Волшебник
Последняя редакция №9 от 14.05.07 | История
URL: http://kb.mista.ru/article.php?id=45


Ключевые слова: УРБД, распределенная база, регистрация изменений, 1SUPDTS, TYPEID, OBJID


Давайте рассмотрим, как технология надежной доставки реализована в компоненте УРБД 1С:Предприятие 7.7 «изнутри» (на уровне исходных таблиц информационной базы). Это особенно интересно, если мы хотим реализовать взаимодействие с другими системами (не обязательно 1С), не отказываясь, в то же время, от удобных средств, предоставляемых программисту компонентой УРБД.

Установка и настройка распределенной базы УРБД «с нуля» описана в статье Работа с компонентой УРБД в 1С 7.7 (пошаговая инструкция+скрипт).

Для просмотра и правки файлов DBF рекомендую программу DBFNavigator от Алексея Долгачева.

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

1SUPDTS.DBF

Таблица хранит записи об измененных объектах 1С:Предприятие, которые подлежат отправке получателям.

DBSIGN (тип: Строка 3) – Код базы УРБД, как он задан в окне настроек в меню Администрирование - Распределенная ИБ – Управление. Это код базы-адресата, куда должен быть отправлен объект (например, пользовательский документ или элемент справочника). Если есть несколько баз-получателей, то в таблице будет несколько записей (строчек) для одного и того же объекта (например, измененного элемента справочника товаров).


TYPEID (тип: Строка 4) – Идентификатор типа объекта 1С, например, документа или справочника. Значение хранится в 36-й системе счисления.
OBJID (тип: Строка 9) – Внутренний числовой идентификатор объекта (например, пользовательского документа или элемента справочника), как он представлен в методе ЗначениеВСтрокуВнутр(). Необходимы ведущие пробелы.


DELETED (тип: Строка 1) – Признак непосредственного (не пометка на удаление!) удаления объекта, 1=да или 0=нет.
DWNLDID (тип: Строка 9) – Идентификатор пакета выгрузки, как в 1SDWNLDS.DBF (см. ниже).

Расшифровка записей об измененных объектах


Основная проблема при расшифровке информации из этой таблицы – как средствами 1С получить ссылку на объект (например, элемент справочника), если известен его TYPEID и OBJID.

Во-первых, преобразование значения TYPEID в/из 36-й системы счисления в десятичную можно произвести при помощи (недокументированных) функций _StrToId() и (обратное преобразование) - _IdToStr().


Во-вторых, пара TYPEID (в десятичной системе) и OBJID фигурирует в методах ЗначениеИзСтрокиВнутр() и ЗначениеВСтрокуВнутр(). Вы можете попытаться выгружать существующие объекты во внутреннее текстовое представление, и смотреть, какое у них будет значение этих параметров.
В-третьих, для разбора и формирования этих строк удобно применять методы работы со списками значений – ВстрокуСРазделителями() и ИзСтрокиСРазделителями().

Пример получения измененных объектов

Ниже приведен пример чтения 1SUPDTS.DBF из 1С, если установлена компонента УРБД и настроена распределенная база данных. Пример позволяет вывести объекты (например, элементы справочника товаров), по которым были зафиксированы изменения.

Чтобы добиться того же результата без компоненты УРБД, потребовалось бы заводить специальный справочник, и вводить в него значения при каждом сохранении, удалении, проведении, отмене проведения, пометке на удаление или снятии такой пометки у объекта, а также изменение времени, подчиненности и т.п.

А поскольку в Глобальном модуле 1С:Предприятие отслеживаются далеко не все подобные ситуации, то это гарантирует существенное дополнение конфигурации «следящим» за действиями пользователя кодом. В случае же использования компоненты УРБД в режиме мониторинга изменений, правка конфигурации не требуется совершенно – все изменения автоматически попадают в файл 1SUPDTS.DBF, и могут быть оттуда извлечены как ссылки на объекты.

///////////////////////////////////////////////////////////////////////
Процедура Выполнить()
dbf=СоздатьОбъект("XBASE");
ИмяФайла=КаталогИБ()+"1SUPDTS.DBF";
Если фс.СуществуетФайл(ИмяФайла)=0 Тогда
Сообщить("Таблица 1SUPDTS.DBF не найдена. Возможно, не установлена компонента УРБД.", "!");
Возврат;
КонецЕсли;
dbf.ОткрытьФайл(ИмяФайла,,1);
Если dbf.Открыта()=0 Тогда
Сообщить("Таблица 1SUPDTS.DBF заблокирована. Запустите 1С в разделенном (не монопольном) режиме.", "!");
Возврат;
КонецЕсли;
Если dbf.Первая()=0 Тогда
Сообщить("Файл 1SUPDTS.DBF пустой.");
Возврат
КонецЕсли;
Пока 1=1 Цикл
TYPEID=dbf.TYPEID;
стр=dbf.OBJID;
//Отделяем идентификатор базы (последние 3 символа)
ИДБазы=Прав(стр,3);
стр=Лев(стр, СтрДлина(стр)-3);
стр=СокрЛП(стр);
//Переводим в формат базы 1С
OBJID=_StrToID(стр);
тип=_StrToId(TYPEID);
//Дополняем пробелами слева
Пока СтрДлина(OBJID)<10 Цикл
OBJID=" "+OBJID;
КонецЦикла;
//Дополняем идентификатором базы
объ=""+OBJID+ИДБазы;
стр="{""B"",""0"",""0"","""+тип+""",""0"",""0"","""+объ+"""}";
о=ЗначениеИзСтрокиВнутр(стр); //справочник
Если ПустоеЗначение(о)=1 Тогда
стр="{""O"",""0"",""0"","""+тип+""",""0"",""0"","""+объ+"""}";
о=ЗначениеИзСтрокиВнутр(стр); //документ
КонецЕсли;
//выводит ссылки на объекты (например, названия товаров)
Сообщить("Объект: "+о);
Если dbf.Следующая()=0 Тогда
Прервать;
КонецЕсли;
КонецЦикла; //по записям DBF
dbf.ЗакрытьФайл();
КонецПроцедуры



Michail Varin пишет:
Приведенный код у меня заработал только после добавления строчки:
dbf.кодоваяСтраница(0);



Пример умеет работать только с документами и справочниками. Буквы (первый параметр ЗначениеИзСтрокиВнутр()) для других ссылочных типов:

B – Справочник
O - Документ
T – Счет
P – План счетов
K – Вид субконто
C – Календарь
A – Вид расчета

Этот пример, как и сам автообмен в УРБД, работает только в разделенном (не монопольном!) режиме 1С:Предприятие 7.7.


1SDWNLDS.DBF

Эта таблица хранит идентификаторы пакетов обмена.

DWNLDID (тип: Строка 9) – Идентификатор пакета выгрузки

DBSIGN (тип: Строка 3) – Код базы - получателя
DIRECT (тип: Строка 1) – Направление (I-входящий пакет или O-исходящий)

ACKNOWL (тип: Строка 1) – Наличие подтверждения доставки (символ А, если есть подтверждение, или пустая строка- если нет подтверждения)


Каждая выгрузка (например, инициированная через меню Администрирование – Распределенная ИБ – Автообмен) пишет в эту таблицу новую строчку с уникальным номером пакета DWNLDID. Этот же ключ будет проставлен всем объектам таблицы 1supdts.dbf, где оно еще пустое. Затем 1С «пройдется» по всем записям 1supdts.dbf, и выгрузит все указанные в нем объекты.

Что происходит, если объекты дошли до получателя

Объекты (элементы справочников и документы) идут в пакете с определенным номером пакета DWNLDID, и этот же номер будет фигурировать в подтверждении доставки. Программа увидит: ага, такой-то DWNLDID успешно дошел, и можно чистить таблицу 1supdts.dbf, в части тех записей об объектах, которые содержат этот же DWNLDID.

Что происходит, если объекты не дошли до получателя

Предположим, что наступил следующий день, и надо отправлять базе-получателю новую порцию свежевбитой пользователями информации. Часть объектов в таблице 1supdts.dbf будут с заполненными полями DWNLDID (это вчерашние объекты), а часть – с пустыми (сегодняшние объекты). УРБД пошлет все старые пакеты, по которым еще не было подтверждения, и один новый пакет (присвоив ему новый номер), в одном большом пакете. Таким образом, размер пакета выгрузки с каждым днем будет постоянно расти, до тех пор, пока не треснет что-либо, либо пока от базы – приемника не начнут приходить подтверждения. Предположим, что были доставлены пакеты с номерами 1 и 2, а всего было послано 3 пакета. Размер выгрузки немедленно уменьшится, поскольку УРБД удалит из 1supdts.dbf все записи об объектах, которые принадлежали пакетам 1 и 2 – ведь посылать их базе-приемнику больше не нужно.

Алгоритм повторной доставки

Вы можете реализовать подобные манипуляции с 1supdts.dbf вручную, если обмен производится, например, средствами XML. Алгоритм работы примерно следующий:

Заполнить все пустые поля DWNLDID значением нового (увеличенного на 1) идентификатора пакета.

Выгрузить все объекты, которые перечислены в 1supdts.dbf, указав в пакете выгрузки, какие номера DWNLDID были выгружены.
Если от базы-получателя приходит подтверждение по определенным DWNLDID, то удалить из 1supdts.dbf все записи, где DWNLDID совпадает с номерами пакетов с подтвержденной доставкой.

Ссылки

http://argat.h11.ru/URBDStructure.html
http://1c.proclub.ru/modules/newbb/viewtopic.php?topic_id=258258&forum=2&viewmode=flat&order=ASC&start=all
http://1c.realnet.ru/kuban/138067.html


Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Форум
© Станислав Митичкин (Волшебник), 2005-2006 | Mista.ru

Thursday, June 14, 2007

Репликация MS SQL Server 2005 для баз 1С:Предприятия v7.7

Репликация MS SQL Server 2005 для баз 1С:Предприятия v7.7

Материал для статьи предоставлен Midnight Ghost


В данной статье речь пойдёт о том, как настроить репликацию в MS SQL Server 2005 для больших по размеру баз данных 1С:Предприятия v7.7. Подобная репликация преследует цель создать копию базы данных 1С:Предприятия v7.7 на другом сервере, предназначенную только для чтения. В данной копии можно будет формировать отчёты и просматривать данные, при этом используя "основную" базу в основном только для ввода документов, что в некоторых случаях позволит неплохо решить проблемы производительности системы. О том, как в принципе заставить работать 1С:Предприятие v7.7 вместе с MS SQL Server 2005, вы можете прочитать здесь.

Итак, мы имеем массивную распределённую базу данных 1С:Предприятия v7.7 на MS SQL Server 2005 и желаем создать копию центральной базы "только для чтения" на другом сервере.

Примечание: на момент написания статьи имеется опыт успешной промышленной (не тестовой) эксплуатации 1С:Предприятия v7.7 с MS SQL 2005. При этом размер mdf-файла рабочей базы данных составляет порядка 20 Гб, имеется копия базы данных "только для чтения", настроенная именно так, как описано в этой статье.

Создадим новую периферийную базу в Конфигураторе с признаком "только получатель". Данная периферийная база будет довольно формальной (мы не будем даже инициализировать её) и послужит "опорой" для проверок некоторых условий в конфигурации. Необходимо средствами конфигурирования и встроенного языка 1С:Предприятия создать в конфигурации механизм, который обеспечивал бы запреты на ввод информации в копии базы, а с другой стороны - обеспечивал бы запреты на запуск формирования отчетов в "основной" базе. При этом желательно сделать такие запреты опциональными (отключаемыми) с тем, чтобы можно было в любой момент разрешить формирование отчётов в "основной" базе в случае недоступности по каким-либо причинам копии и т.п. Следовательно, в коде форм журналов документов, отчётов и т.д. перед запуском определённых действий прежде всего нужно определить, в какой базе мы "находимся" - в копии или в "основной" базе. Здесь и пригодится признак "только получатель": если мы в копии, метод "ТекущаяИБТолькоПолучатель()" вернёт единицу. Конечно, данный метод определения "копия - не копия" является не единственно возможным. Кроме того, если вы желаете реплицировать нераспределённую базу, вам придётся самостоятельно внести некоторые коррективы в дальнейшее изложение, и при этом ваша задача несколько упростится.

Примечания: запуск SQL Server Management Studio, которая потребуется для дальнейшей работы, осуществляется через меню "Пуск" – "Программы" – "Microsoft SQL Server 2005" - "SQL Server Management Studio". В SQL Server Management Studio нам потребуются окна "Object Explorer" и "Registered Servers". Если этих окон нет на экране, их можно отобразить через меню "View".

Обычно настройка репликации сводится к определению публикатора, дистрибьютора и подписчика с автоматической инициализацией подписки, в результате чего и получается копия базы данных. К сожалению, практика показала, что данная процедура не всегда гладко проходит с большими базами данных 1С:Предприятия v7.7. На этапе инициализации подписки вы можете получить сообщение об ошибке с прекращением всего процесса. Поэтому в данном описании мы пойдём более сложным путём, искусственно "инициализируя" базу подписчика путём копирования файлов. Примечание: не исключено, что ваша база данных может быть удачно реплицирована и "стандартным" способом, если немного поэксперементировать с параметрами настройки репликации.

Примем следующие обозначения:
MyMainServer - имя SQL-сервера, на котором расположена наша "основная" база данных.
MyMainDatabase - имя нашей "основной" базы данных.
MySecondServer - имя SQL-сервера, на котором (будет) расположена наша база данных "только для чтения", являющаяся копией "основной" базы данных.
MySecondDatabase - имя нашей базы данных "только для чтения", являющейся копией "основной" базы данных.

Настройка репликации по шагам:
Настройка репликации на SQL-сервере. В окне "Object Explorer" в ветке "Replication" на сервере-источнике (где находится "основная" база данных) необходимо вызвать команду контекстного меню "Configure Distribution...". Если команда контекстного меню "Configure Distribution..." изначально отсутствует, а вместо неё присутствует команда "Disable Publishing and Distribution...", репликация на этом сервере уже настроена, и весь этот пункт можно пропустить. Откроется окно мастера. Далее по шагам:
Шаг "Distributor". Выбрать имя текущего сервера (по умолчанию). Таким образом, сервер назначается дистрибьютором самому себе.
Шаг "Snapshot Folder". Выбрать папку, в которой будут созданы файлы подписки (папка может быть сетевой).
Шаг "Distribution Database". Создание системной базы данных для репликации. Имя базы данных можно оставить по умолчанию – "distribution". Папки для хранения файлов .mdf (файл базы данных) и .ldf (файл лога базы данных) могут совпадать (т.е. может быть указана одна и та же папка). Примечание: файлов distribution.MDF и distribution.LDF в этой папке на данный момент быть не должно (если они есть, их необходимо предварительно удалить, иначе на последнем шаге работы мастера произойдёт ошибка).
Шаг "Publishers". Добавление публикаторов. Здесь ничего добавлять не надо, т.к. текущий сервер уже добавлен по умолчанию.
Шаг "Wizard Actions". Должен стоять флажок "Configure distribution" (по умолчанию).
В результате будет создана системная база данных "distribution", а ветке "Replication" появятся пункты контекстного меню "Publisher Properties...", "Distributor Properties..." и "Disable Publishing and Distribution...", а пункта "Configure Distribution" уже не будет. Примечание: если настройка репликации не получилась или сделана неверно, для повторной настройки следует вначале отменить настройку репликации через пункт контекстного меню "Disable Publishing and Distribution...", на втором шаге открывшегося мастера выбрав "Yes, disable publishing on this server".
Сжатие "основной" базы данных перед копированием. Чтобы в момент сжатия были удалены все записи лог-файла .ldf, которые ещё не попали в резервную копию (а по умолчанию этого не произойдёт), необходимо выполнить запрос, который "скажет" системе, что все записи лог-файла уже якобы попали в резервную копию. Для этого необходимо выполнить следующий запрос:
backup log MyMainDatabase with no_log
Здесь "MyMainDatabase" - имя нашей "основной" базы данных. Далее необходимо собственно сжать нашу "основную" базу данных: в окне "Object Explorer" в ветке "Databases" - "MyMainDatabase" вызвать команду контекстного меню "Tasks" - "Shrink" - "Database". Установить флажок "Reorganize files before releasing...", и нажать "ОК". Сжатие может занять некоторое время (десятки минут).
Устранение проблемы с полями типа IDENTITY. Если в некоторых таблицах автоинкрементные поля с признаком IDENTITY не содержат признака "NOT FOR REPLICATION", при вставке нового элемента в таблицу в базе данных подписчика (например, при добавлении элемента справочника) репликация остановится. 1С:Предприятие автоматически никогда не ставит подобный признак. Чтобы у полей IDENTITY появилось свойство "NOT FOR REPLICATION", необходимо искусственно создать и удалить публикацию.
В окне "Object Explorer" в ветке "Replication" - "Local Publications" текущего сервера необходимо вызвать команду контекстного меню "New Publication...". Откроется окно мастера. Далее по шагам:
Шаг "Publication Database". Выбрать нашу "основную" базу данных.
Шаг "Publication Type". Выбрать тип публикации "Transactional Publication" (второй сверху).
Шаг "Articles". Выбор, что публиковать. Поставьте флажок "Tables", чтобы выбрались все таблицы. Затем раскройте ветку "Tables" и уберите флажки с двух таблиц:
_1SDBSET (список распределённых баз). Эта таблица убирается для того, чтобы копия стала периферийной базой.
_1SDNLOCK (номера новых несохранённых справочников и документов).
Кроме того, автоматически будут не отмечены флажками те таблицы, которые не имеют первичного ключа (их реплицировать нельзя в принципе).
Шаг "Filter Table Rows". Фильтры задавать не надо.
Шаг "Snapshot Agent". Все флажки должны быть сняты, т.к. инициализация этой публикации нам не потребуется.
Шаг "Agent Security". Указываем параметры безопасности (первая кнопка "Security Settings"), под какими учётными записями будет идти репликация. Выбрать "Run under the SQL Server Agent..." и "By impersonating the process account".
"Snapshot Agent" и "Log Reader Agent" настраиваются одинаково, т.е. флажок "Use the security settings from the Snapshot Agent" должен стоять.
Шаг "Wizard Actions". Флажок "Create the publication" должен стоять.
Шаг "Complete the Wizard". Задайте имя публикации, совпадающее с именем нашей "основной" базы данных.
Удалите только что созданную публикацию в дереве Object Explorer командой контекстного меню "Delete".
Отключение баз данных. Необходимо перевести базы в состояние off-line. В окне "Object Explorer" в ветке "MyMainServer" - "Databases" - "MyMainDatabase" необходимо вызвать команду контекстного меню "Tasks" - "Take offline".
Если вы настраиваете репликацию не в первый раз, и копия базы данных уже существует, в окне "Object Explorer" в ветке "MySecondServer" - "Databases" - "MySecondDatabase" также необходимо вызвать команду контекстного меню "Tasks" - "Take offline".
Примечание: если процесс перевода базы в состояние off-line идёт слишком долго (несколько минут), возможно, кто-то "сидит" в базе данных. В этом случае надо найти и удалить нужное подключение через Activity Monitor в ветке Management на нужном сервере, иначе процесс перевода базы в состояние off-line может никогда не завершиться.
Копирование файлов. Необходимо скопировать файлы базы данных с одного сервера на другой. Если файлы большие, копирование может занять продолжительное время.
Удалите MySecondDatabase.mdf и MySecondDatabase_log.ldf в папке назначения, если они существуют.
Скопируйте MyMainDatabase.mdf и MyMainDatabase_log.ldf (в процессе копирования можно выполнять п.6 настоящей инструкции).
Переименуйте скопированные файлы в MySecondDatabase.mdf и MySecondDatabase_log.ldf.
Получение необходимых хранимых процедур для подписчика. Для базы данных подписчика необходимо наличие определённых хранимых процедур, которые были бы созданы автоматически, если бы репликация настраивалась стандартным способом, а не "в обход" (копированием файлов баз данных). Поскольку в данном случае репликация настраивается "в обход", необходимые хранимые процедуры придётся получить искусственно, создав пустые базы публикатора и подписчика с той же конфигурацией (md). Порядок действий:
Необходимо создать две базы данных на sql-сервере MyMainServer: "Pub" и "Sub".
В окне "Object Explorer" в ветке "MyMainServer" - "Databases" необходимо вызвать команду контекстного меню "New Database".
В разделе "General" задаётся "Database name" (задайте "Pub").
В разделе "Options" следует задать свойство "Compatibility Level" как "SQL server 2000 (80)".
Ту же операцию (создание базы данных на sql-сервере) проделайте и для базы "Sub".
Разверните файловую часть базы "Pub":
Создайте произвольный каталог на жестком диске, например C:\Temp\Pub\. Зарегистрируйте его как базу в диалоге "Запуск 1С:Предприятия" и откройте в режиме Конфигуратора (тип базы при запуске укажите как SQL-Server). Создайте пользователя с административными правами.
Задайте параметры соединения с сервером (меню "Администрирование" - "Параметры базы данных SQL..."), сервер - MyMainServer, база данных – Pub, пользователь – sa.
Загрузите конфигурацию (меню "Конфигурация" - "Загрузить измененную конфигурацию..."), выбрав MD-файл вашей конфигурации "основной" базы данных. Сохраните конфигурацию (меню "Файл" - "Сохранить").
Создайте распределённую базу. Меню "Администрирование" - "Распределенная ИБ" - "Управление", кнопка "Центральная ИБ". Задайте произвольный код и сохраните.
Удалите файловую часть базы "Pub" (папку C:\Temp\Pub\).
Настройте публикацию Pub -> Sub, почти как в п.3 "Устранение проблемы с полями типа IDENTITY", но для базы Pub, и в конце не удаляйте созданную публикацию. На шаге "Snapshot Agent" установите флажок "Create a snapshot immediately...".
В окне "Object Explorer" в ветке "MyMainServer" - "Replication" - "Local Publications" - "Pub" вызовите команду контекстного меню "View Snapshot Agent Status" и дождитесь окончания публикации (100%).
Создайте подписку. В окне "Object Explorer" в ветке "MyMainServer" - "Replication" - "Local Subscriptions" вызовите команду контекстного меню "New Subscriptions...". Откроется окно мастера. Далее по шагам:
Шаг "Publication". Выберите сервер MyMainServer и базу-публикатора "Pub".
Шаг "Distribution Agent Location". Выберите вид публикации "pull". Примечание: в варианте "push" (заталкивать) работает сервер дистрибьютера, в варианте "pull" (вытягивать) работает сервер подписчика.
Шаг "Subscribers". Выберите сервер MyMainServer (поставьте галку) и базу-подписчика "Sub".
Шаг "Distribution Agent Security". Задайте параметры безопасности: выберите "Run under the SQL Server Agent...", в группе "Connect to the Distributor" выберите "Using the following SQL Server login", задайте пользователя sa и его пароль.
Шаг "Synchronization Shedule". Оставьте по умолчанию "Run continuously", т.е. выполнять задачу по синхронизации постоянно.
Шаг "Initialize Subscriptions". Инициализацию подписчиков поставьте "Immediately" (с галкой), т.е. немедленно.
Шаг "Wizard Actions". Флажок "Create the subscription(s)" должен стоять.
Количество созданных в результате в базе Sub хранимых процедур должно быть ровно втрое больше, чем таблиц (процедуры для INSERT, UPDATE и DELETE). Дождитесь, пока количество хранимых процедур достигнет нужной величины (это может произойти не мгновенно). Необходимо заскриптовать все хранимые процедуры базы Sub. В окне "Object Explorer" в ветке "MyMainServer" - "Databases" - "Sub" необходимо вызвать команду контекстного меню "Tasks" - "Generate Scripts", откроется окно мастера. Далее по шагам:
Шаг "Select Database". Выберите Sub.
Шаг "Choose Script Options". Всё оставьте по умолчанию.
Шаг "Choose Object Types". Выберите Stored Procedures.
Шаг "Choose Stored Procedures". Выберите все кнопкой "Select All".
Шаг "Output Option". Выберите "Script to file". Выберите файл для сохранения.
В окне "Object Explorer" в ветке "MyMainServer" - "Replication" - "Local Subscriptions" удалите подписку базы Sub. В ветке "Replication" - "Local Publications" удалите публикацию базы Pub. В ветке "Databases" удалите и сами базы Pub и Sub на SQL-сервере MyMainServer.
Подключение баз данных. Необходимо перевести базы в состояние on-line.
В окне "Object Explorer" в ветке "MyMainServer" - "Databases" - "MyMainDatabase" необходимо вызвать команду контекстного меню "Tasks" - "Bring online". В окне "Object Explorer" в ветке "MySecondServer" - "Databases" - "MySecondDatabase" также необходимо вызвать команду контекстного меню "Tasks" - "Bring online".
Необходимо запустить два скрипта на базе MySecondDatabase.
Скрипт, который провешивает хранимые процедуры, нужные для репликации. Это тот скрипт, в который мы заскриптовали все хранимые процедуры базы Sub в конце шага 6.
Скрипт, который сделает базу периферийной:
UPDATE
_1SSYSTEM
SET
DBSIGN = 'MSD'

DELETE FROM
_1SDBSET
WHERE
(DBSIGN <> 'MMD')
AND (DBSIGN <> 'MSD')

UPDATE
_1SDBSET
SET
DBSTATUS = 'P'
WHERE
DBSIGN = 'MMD'

UPDATE
_1SDBSET
SET
DBSTATUS = 'M'
WHERE
DBSIGN = 'MSD'
Важное примечание: "MMD" и "MSD" в вышеприведённом скрипте необходимо заменить на трёхбуквенные коды (заданные в Конфигураторе 1С) ваших центральной и периферийной ИБ соответственно.
Следует создать Job на сервере MySecondServer для установки точки актуальности в базе MySecondDatabase. В окне "Object Explorer" - ветка "MySecondServer" - "SQL Server Agent" - "Jobs". Пусть такой Job раз в час устанавливает точку актуальности в базе MySecondDatabase, равной концу дня текущей даты (для успешного формирования отчетов в базе в любой момент). Job может выполнять примерно такой скрипт:
UPDATE
_1SSYSTEM
SET
CURDATE = CONVERT(DATETIME, RIGHT('0000'+LTRIM(STR(DATEPART(yy, GETDATE()))),4)+
RIGHT('00'+LTRIM(STR(DATEPART(mm, GETDATE()))),2)+
RIGHT('00'+LTRIM(STR(DATEPART(dd, GETDATE()))),2)),
CURTIME = 863999999,
EVENTIDTA = 'ZZZZZZZZZ'
Создание публикации для MyMainDatabase. Необходимо создать публикацию на сервере MyMainServer для базы данных MyMainDatabase, как в п.3 "Устранение проблемы с полями типа IDENTITY".
Создание подписки для MySecondDatabase. Необходимо создать подписку на сервере MySecondServer для базы данных MySecondDatabase, почти так же, как в п.6 ("Получение необходимых хранимых процедур для подписчика"), подпункте "Создайте подписку", но со следующими отличиями:
Шаг "Publication". Выберите сервер MyMainServer и базу-публикатора MyMainDatabase.
Шаг "Subscribers". Выберите сервер MySecondServer (поставьте галку) и базу-подписчика MySecondDatabase.
Шаг "Initialize Subscriptions". Уберите галку инициализации подписчиков, т.к. подписчик уже инициализирован.
Исправление владельца MySecondDatabase. Чтобы ликвидировать последствия ручного переноса файлов базы данных, возможно, придётся откорректировать владельца базы данных MySecondDatabase. Для этого выполните на этой базе запрос, подобный следующему:
sp_changedbowner 'ИмяВладельца'
Запустите базы MyMainDatabase и MySecondDatabase в 1С:Предприятии, убедитесь, что они работают.

***


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

В окне "Object Explorer" в ветке "MyMainServer" - "Replication" - "Local Publications" - "MyMainDatabase" команда контекстного меню "View Log Reader Agent Status" открывает окно, в котором можно остановить или запустить репликацию кнопками Stop и Start.

В случае переноса баз данных MyMainDatabase и/или MySecondDatabase между серверами, всю настройку репликации придётся повторить, за исключением п.3 ("Устранение проблемы с полями типа IDENTITY") и п.6 ("Получение необходимых хранимых процедур для подписчика").

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

Использование MS SQL Server 2005 с 1С:Предприятием v7.7

Использование MS SQL Server 2005 с 1С:Предприятием v7.7

Материал для статьи предоставлен Midnight Ghost


Данная статья является изложением опыта запуска 1С:Предприятия v7.7 на MS SQL Server 2005. К сожалению, "штатно" 1С:Предприятие v7.7 работу с MS SQL Server 2005 не поддерживает, поэтому понадобятся некоторые ухищрения, которые и будут здесь описаны. Примечание: на данный момент имеется опыт успешной промышленной (не тестовой) эксплуатации 1С:Предприятия v7.7 с MS SQL 2005. При этом размер mdf-файла рабочей базы данных составляет порядка 10 Гб, количество одновременных подключений - порядка 60, конфигурация полностью самописная, активно используется технология 1С++ в части "прямых" запросов к базе данных.

При установке SQL сервера необходимо выставить режим авторизации "MixedMode" (т.е. Windows+SQL), и указать пароль для учетной записи "SA". Параметр "Server Collation" следует установить как "Cyrillic_General_CI_AS".

Если SQL сервер "не виден" с других компьютеров (1С выдаёт сообщение "SQL-сервер не существует, или отсутствует доступ."), следует разрешить удаленные соединения в SQL Server Surface Area Configuration. Меню "Пуск" - "Программы" - "Microsoft SQL Server 2005" - "Configuration Tools" - "SQL Server Surface Area Configuration". Выбрать ссылку "Surface Area Configuration for Services and Connections", в дереве выбрать элемент "Remote Connections".

Если у вас MS SQL Server 2005 Express Edition, в 1С в "Параметрах базы данных SQL" нужно указывать не просто имя сервера, а Имя_Сервера\SQLEXPRESS.

В свойствах базы данных на SQL Server необходимо поставить Compatibility Level "SQL Server 2000(80)". Это можно сделать в Microsoft SQL Server Management Studio Express (меню "Пуск" - "Программы" - "Microsoft SQL Server 2005" - "SQL Server Management Studio Express CTP"). Примечание: база данных может быть создана на Microsoft SQL Server 2000, и перенесена на Microsoft SQL Server 2005 через простой Detach - Attach, можно также через Backup - Restore. Однако простого пути для возврата базы назад (на MS SQL 2000) уже не будет.

Если возникают проблемы с подключением к базе MS SQL 2005, просмотрите настройки протоколов в "SQL Server Configuration Manager". Меню "Пуск" - "Программы" - "Microsoft SQL Server 2005" - "Configuration Tools" - "SQL Server Configuration Manager".

Поскольку 1С v7.7 штатно не поддерживает MS SQL 2005, вы получите сообщение "Требуется MS SQL Server 6.5 + Service Pack 5a или более старшая версия!". Чтобы устранить это, необходимо изменить BkEnd.dll следующим образом (для 21-го релиза 1С):
000D9B7A: 83 EB
000D9B7B: E8 15
000DAFE0: 83 EB
000DAFE1: E8 10

Для 25-го релиза 1С:
000D9C4A: 83 EB
000D9C4B: E8 15
000DB0B0: 83 EB
000DB0B1: E8 10

Для 26-го и 27-го релизов 1С:
000D9CCA: 83 EB
000D9CCB: E8 15
000DB130: 83 EB
000DB131: E8 10

Для возможности коллективной работы в базе данных (одновременной работы нескольких пользователей) необходим режим Multiple для базы данных ("Properties - Options - State - Restrict access" - "Multiple").

Возможно, для коллективной работы в базе данных (одновременной работы нескольких пользователей) могут потребоваться дополнительные изменения в BkEnd.dll, которые можно произвести с помощью Unofficial Service Pack v2.13.24 для 24 релиза 1С (он работает и на 25 релизе), установив флажок "Разрешать другим пользователям входить в базу (SQL)" на вкладке "Защита". Эти изменения можно произвести и вручную (для 25-го релиза 1С):
000DA023: 85 40
000DA024: C0 90

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

При работе на базе MS SQL 2005 может возникнуть проблема с автонумерацией документов (при интерактивном создании нового документа номер присваивается некорректно), причём проблема эта воспроизводится не всегда. Чтобы обойти эту проблему, можно поместить в процедуру "ПриЗаписи" всех документов код, подобный следующему:
докДок=СоздатьОбъект("Документ."+Вид());
Если докДок.НайтиПоНомеру(НомерДок,ДатаДок)<>0 Тогда
Если докДок.ТекущийДокумент()<>ТекущийДокумент() Тогда
УстановитьНовыйНомер();
КонецЕсли;
КонецЕсли;

Ссылки для скачивания бесплатных дистрибутивов Microsoft:

Wednesday, April 11, 2007

Описание таблиц 1С V77 и запросы к ним

Описание таблиц 1С V77
http://www.script-coding.info/v77tables.html
Автор статьи - Печеный Василий.

Оригинал статьи расположен по адресу http://www.metaprog.co.ua/secrprog/.


Содержание:
1. Описание общих таблиц 1С V77
1.1. Общее описание хранения данных 1С.
1.1.1. Хранение ID объекта
1.1.2. Хранение даты
1.1.3. Хранение времени
1.2. Описание таблиц 1С
1.2.1. Таблица _1SCONNECT - Сессии (соединения)
1.2.2. Таблица _1SCONST - Константы
1.2.3. Таблица _1SCRDOC - Ссылки документов
1.2.4. Таблица _1SJOURN - Журналы
1.2.5. Таблица _1SDNLOCK - Номера документов
1.2.6. Таблица _1SSTREAM - Последовательность документов
1.2.7. Таблица _1SSYSTEM - Системная
1.2.8. Таблица _1SUIDCTL - Уникальности
1.2.9. Таблица _1SUSERS - Соединения
2. Подсистема справочников и констант
2.1. Предисловие
2.2. Введение
2.3. Описание полей таблиц справочников
2.4. Описание полей таблицы констант и периодических реквизитов справочников
2.5. Примеры запросов
2.5.1. Пример 1. Получение всех записей справочника.
2.5.2. Пример 2. Получение записей только групп справочника.
2.5.3. Пример 3. Получение записей только элементов (не групп) справочника.
2.5.4. Пример 4. Получение записей только непомеченных элементов справочника.
2.5.5. Пример 5. Получение записей элементов с периодическим реквизитом на максимальную дату.
2.5.6. Пример 6. Получение записей элементов с периодическим реквизитом на конкретную дату.
2.5.7. Пример 7. Получение значений непериодических реквизитов агрегатных типов.
2.5.8. Пример 8. Получение записей элементов справочников с их родителями.
2.5.9. Пример 9. Выборка элементов из подчиненного справочника.
3. Подсистема документов и регистров
3.1. Предисловие
3.2. Введение
3.3. Общее описание хранения данных 1С в подсистеме документов
3.3.1. Краткое описание таблиц
3.3.2. Примеры запросов
3.3.2.1. Пример 1. Получение всех документов одного вида со всеми реквизитами шапки.
3.3.2.2. Пример 2. То же, включая все общие и системные реквизиты.
3.3.2.3. Пример 3. То же, плюс все реквизиты табличной части.
3.3.2.4. Пример 4. То же, с фильтром по дате документа (за один день).
3.3.2.5. Пример 5. Получение документов родителей по подчиненному документу.
3.3.2.6. Пример 6. Получение списка подчиненных документов.
3.3.2.7. Пример 7. Получение списка только проведенных документов.
3.3.2.8. Пример 8. Получение списка документов, принадлежащих разным типам учета.
3.3.2.9. Пример 9. Получение списка документов по графе отбора.
3.4. Регистры
3.4.1. Общее описание подсиcтемы регистров
3.4.2. Рассмотрим примеры
3.4.2.1. Структура и описание полей таблиц регистров
3.4.2.1.1. Таблица RGxxx
3.4.2.1.2. Таблица RAxxx
3.4.2.2. Пример 1. Получение итогов на ТА или на конец периода.
3.4.2.3. Пример 2. Получение сумм прихода и расхода за один месяц (оборотов).
3.4.2.4. Пример 3. Получение итогов и остатков на произвольную дату.
3.4.2.5. Пример 4. Получение итогов и остатков на документ.
3.4.2.6. Пример 5. Получение остатков и оборотов за произвольный период.
3.4.2.7. Пример 6. Пересчет итогов по одному из регистров остатков.
1. Описание общих таблиц 1С V77
1.1. Общее описание хранения данных 1С.

Данная статья не является полным описанием и не претендует на оригинальность. Цель - облегчить понимание работы 1С, а также способствовать написанию прямых запросов к таблицам 1С. Статья написанная автором на основе личного опыта, а также той немногочисленной информации, которую можно раздобыть в Internet в свободном (и не очень) доступе. Возможно некоторые поля или таблицы могут измениться с выходом последующих релизов, но пока (до 25 релиза включительно) таких изменений не было замечено.
1.1.1. Хранение ID объекта

ID может иметь 3 представления (уровня) в зависимости от длины (количества значащих символов):
9 символов – определен тип и вид объекта (например «Справочник.Клиенты»), в ID включается только порядковый номер в 36-ричной системе исчисления. Под порядковый номер отводятся первые 6 символов, последние 3 символа зарезервированы под код базы УРБД.
13 символов – определен только тип объекта, вид не задан (например «Справочник»). Первые 4 символа – идентификатор вида (как он задан в метаданных), последующие 9 символов – по аналогии с предыдущим пунктом.
23 символа – не определен тип и вид объекта. В таком случае в первых 2 символах хранится тип объекта (будет рассмотрен ниже), следующие 13 символов формируются аналогично предыдущему пункту.

В некоторых случаях, при указании неопределенного типа объекта (длина ID кода 23 символа) создается дополнительное поле с символом «T» вначале (например, если в справочнике задан реквизит SP235 как неопределенный, то в таблице справочника будет создано еще одно поле TSP235). Рассмотрим поподробнее значения этого поля. Это поле по умолчанию заполняется пустой строкой (3 пробела).
Для невыбранного типа (заполняется по умолчанию): TSP235 = ' ' (3 пробела), при этом связанное неопределенное поле SP235 заполняется значением 'U ' (символ U и 22 пробела), что скорее всего означает «Undefined», т.е. неопределенный.
Для типов «Число»: связанное поле SP235 заполняется строковым значением числа, но с символом «N» в начале строки (например 'N 2' для целого числа 2). Возможные значения TSP235:
'F30' - число с плавающей запятой (float);
'320' - число, состоящее только из десятичных (0.99, т.е. Число(3,2)).
'A00' - целое число (int);
Для типов «Строка»: связанное поле SP235 заполняется строкой, но с символом 'S' слева (например 'SАбвгдежзклимно '). Возможные значения TSP235 в этом случае: количество знаков, определенных для строкового значения, например, '14' - количество знаков в строке, в 36-ричном формате (_IDToStr). Следует отметить, хотя 1С и назначает длину строки больше 22 символов, но фактически в этом поле можно хранить только 22 символа (1 разряд приходиться на обозначение типа поля 'S').
Для типов «Дата»: связанное поле SP235 заполняется строкой, но с символом 'D' слева (например 'D20050303 '). TSP235 - ' ' (3 пробела), т.е. поле пустое.
Для типов 1С (Справочник, Документ, Перечисление, Счет, и т.д.): связанное поле SP235 заполняется строкой включающий полный идентификатор объекта (его тип, вид, внутренний ИД код). Следует отметить, что для типов «Перечисление» нельзя не назначать вид, так как неназначение вида приведет к несовпадению типов и незаполнению неопределенного поля. Возможные значения TSP235:
'0 ' - был назначен только тип объекта, вид не назначен (например «НазначитьТип(«Справочник»)»)
'1 ' - был назначен как тип объекта, так и его вид (например «НазначитьТип(«Справочник.Клиенты»)»)

Примечание

Поскольку для неопределенных реквизитов (полей) длина поля всегда равна 23 символам, то соответственно в такое поле можно записать значение максимум с 22 символьным значением (1 отводится под определение типа, в случае базовых типов, таких как число, строка, дата).
1.1.2. Хранение даты
Дата может храниться как в виде строки, так и в виде типа «Дата».
В случае хранения даты в стандартном формате (тип Дата), при добавлении такого поля в таблицу для базы в формате SQL - поле заполняется значением «1753-01-01 00:00:00.000».
В случае хранения даты в виде строки, дата сохраняется в формате «ГГГГММДД», т.е. 4 символа – год (0000-9999), 2 – месяц (01-12), 3 – день (01-31). Т.е. для даты 29.07.2005 поле будет равным '20050729'.
1.1.3. Хранение времени
Время может храниться в двух форматах: Числовое представление, Строковое представление.
В случае числового хранения времени оно отсчитывается от начала суток в десятитыcячных долях секунды. Т.е. фактически будет получено число: (Часы*3600+Минуты*60+секунды)*10000. Т.е. Для времени 19:05:36 – 687360000 (1С умеет учитывать время до 10000 долей секунды, как в случае с документами).
В случае числового хранения времени время с числового значения (Часы*3600+Минуты*60+секунды)*10000 переводиться в 36-ричный формат. Так, для времени 19:05:36 - BD8IDC.

Дополнительную информацию можно получить здесь:

http://www.mista.ru/articles1c/hare/article.8.html
http://www.mista.ru/articles1c/hare/article.11.html
http://www.mista.ru/articles1c/hare/article.12.html
http://www.mista.ru/articles1c/hare/article.13.html
http://1csql.ru/materials/articles/develop.html
http://www.gendin.ru/budni/b25.html
http://www.gendin.ru/budni/b26.html
http://gendin.ru/budni/files/1ctables.xls
http://web-team.narod.ru/index.html
http://www.sinor.ru/~my1c/knowhow/fields.html
1.2. Описание таблиц 1С
1.2.1. Таблица _1SCONNECT - Сессии (соединения)

Краткое описание: таблица предназначена для хранения уникального идентификатора сессии первого присоединившегося к базе данных пользователя в режиме предприятия (поэтому в таблице всегда только одна запись). Судя по всему, именно по наличию записи в этой таблицы и происходит верификация процедур при первом запуске в SQL.
Название поля Описание
CONNECTUUID GUID (уникальный идентификатор) первого сеанса, соединившегося с 1С в режиме предприятия. Тип - Строка(36).

1.2.2. Таблица _1SCONST - Константы

Краткое описание: таблица предназначена для хранения значений констант и периодических реквизитов справочников.
Название поля Описание
ROW_ID Порядковый номер записи в таблице. Тип - Число(int).
OBJID ID объекта периодического реквизита (справочника) или периодической константы, для непериодических констант это поле равно ‘ 0 ’. Тип - Строка(9).
ID ID (идентификатор) константы или периодического реквизита справочника. Тип представлен в виде десятичного числа (_StrToID()). Тип - Число(int).
DATE Дата установки периодического реквизита. Для базы в формате SQL и для непериодического реквизита (или константы) поле заполняется значением '1753-01-01 00:00:00.000'. Тип - Дата (datetime для SQL).
VALUE Значение константы или периодического реквизита. Для неопределенных типов по умолчанию заполняется «U». Тип- Строка(255).
DOCID ID (идентификатор) документа (поле, связанное с полем IDDOC таблицы журналов (_1SJOURN)). Для значений установленных вручную или непериодических заполняется ' 0 '.
TIME Время установки значения в десятичном формате ((Часы*3600+Минуты*60+секунды)*10000). Тип - Число(int).
ACTNO Номер движения документа (включая каждое движения по регистрам и запись периодических реквизитов за исключением проводок). В случае непериодического значения заполняется нулем. Тип - Число(int).
LINENO_ Номер строки документа (заполняется при вызове метода ПривязыватьСтроку(), если привязка не выполнена или непериодическое значение - заполняется нулем). Тип - Число(tinyint).
TVALUE Заполняется только для неопределенных реквизитов, для типов данных 1С (когда длина ID равна 23 символам). Тип - Строка(3).

1.2.3. Таблица _1SCRDOC - Ссылки документов

Краткое описание: таблица предназначена для хранения ссылок подчиненных документов и граф отборов реквизитов документов, которые не являются общими реквизитами. Для хранения ссылок документов в подчиненном документе должен быть реквизит (поле) в котором будет ссылка на документ-родитель. Без такой ссылки документ не считается подчиненным.
Название поля Описание
ROW_ID Порядковый номер записи в таблице. Номер проставляется автоматически при вводе новой строки. При изменении даты документа, введенного на основании, это поле «переприсваивается». Тип - Число(int).
MDID Номер графы отбора подчиненного документа. Тип - Число(int).
PARENTVAL Полный идентификатор документа родителя. Тип – Строка(23).
CHILD_DATE_TIME_IDDOC Дата + Время (в 36-ричном формате) + ИД подчиненного документа. Тип – Строка(23).
CHILDID ИД подчиненного документа. Тип – Строка(9).
FLAGS ??? Тип – Число(1).

1.2.4. Таблица _1SJOURN - Журналы

Краткое описание: таблица предназначена для хранения списка всех документов. Именно эта таблица является узким местом при работе большого числа пользователей (так как она блокируется полностью каждым пользователем в момент проведения документов).
Название поля Описание
ROW_ID Порядковый номер записи в таблице. Номер присваивается в порядке ввода документов. При изменении даты или времени – не изменяется. Тип - Число(4).
IDJOURNAL ID журнала документов, которому принадлежит документ (из метаданных). Тип - Число(4).
IDDOC ID документа в 36-ричной системе счисления. Тип - Строка(9).
IDDOCDEF ID вида документа. Номер документа из метаданных (номер, а не 36-ричное представление). Тип - Число(4).
APPCODE Число, отражающее принадлежность документа к компонентам. Возможные значения:
0 - не принадлежит никакому учету.
(1) 000001 - опер учет.
(2) 000010 - расчет.
(4) 000100 - бух учет.
(8) 001000 - движения периодических реквизитов.
(16) 010000 - существует документ операция у документа (документ может быть не проведен, но тогда должно быть выбрано "Создавать операцию - Всегда" в окне свойств документа).
(32) 100000 - Выполнен метод Операция.Записать(); в документе, принадлежащем бух. учету.
(20) 010100 - Это документ "Операция".
Возможна их комбинация, например 6 = 2+4 (бух. учет + расчет), 7 = все три компоненты. Тип - Число(2).
DATE_TIME_IDDOC Составное поле, обозначающее позицию документа во времени + ID документа.
8 первых символов - дата документа в виде «ГГГММДД».
6 следующих символов - время документа в 36-ричной системе исчисления, причем время рассчитывается от начала дня в сотых частях миллисекунды (10-5), т.е. время 00:00:01 будет равно 10000, или же в 36-ричной системе «7PS».
9 последних символов - ID документа. Этот ID также составной, 6 символов - порядковый номер введенного документа в систему в 36-ричной системе (причем считаются не только сохраненные документы, а также те, которые не были сохранены), последние 3 символа - идентификатор базы УРИБ (если данные вводятся в распределенной ИБ).
Тип - Строка(23).
DNPREFIX Префикс номера документа. Тип - Строка(18). Для документов, у которых код числовой, это поле равно десятичному ID вида документа. Если нумерация в пределах периода - то также хранится и период в виде ГГГГММДД (например 2006 для нумерации в пределах года).
DOCNO Номер документа. Длина определяется максимальным номером всех документов. Тип - Строка (максимальное значение - 20).
CLOSED Флаг проведения документа. Для каждого набора компонент свое значение (аналогично полю APPCODE). Значение последнего байта отвечает за флаг проведения документа. Тип - Число(1).
ISMARK Флаг пометки на удаление. 0 - не помечен, 1 - помечен. Тип - Число(1,0) (бинарное значение).
ACTCNT Счетчик действий (движения) для документа (один документ может вызывать несколько движений регистров). Фактически хранит информацию о количестве движений по всем регистрам + записи периодических реквизитов (без учета проводок по бух. учету). Тип – Число(4).
VERSTAMP Количество изменений записи таблицы. Изменением считается любое действие "Изменить (открыть)" + действия при изменении структуры. Тип - Число(4).
RFxxx Флаг наличия движений по регистру "xxx". Тип - Число(1,0).
SPyyy Значение общих реквизитов документов, в которых установлено свойство «отбор». Если свойство «отбор» не установлено, то общие реквизиты хранятся в таблицах документов (DHxxx). Тип - Число, Строка, Дата.
TSPyyy Дополнение к общим реквизитам, заполняется только для неопределенных типов значений реквизитов отбора (используется совместно с полем «SPyyy»). Тип - Строка(3).
DSzzz Флаг принадлежности последовательности. ZZZ - десятичный ID последовательности. Принимает 3 значения: 0 - документ не принадлежит последовательности, 1 - документ находится на границе или за границей последовательности (или находился на ней), 2 - документ находится перед границей последовательности (устанавливается в момент проведения).

1.2.5. Таблица _1SDNLOCK - Номера документов

Краткое описание: таблица предназначена для хранения информации о зарезервированных номерах документов, т.е. тех. номерах документов, которые вводятся в данный момент в систему, но еще не сохранены (не записаны в таблицу журналов _1SJOURN)
Название поля Описание
DNPREFIX Идентификатор вида документа или справочника. Для документа также включает время (всегда 0 0). Тип – Строка(28).
DOCNO Зарезервированный номер. Тип – Строка(10) (определяется максимальным номером в системе, максимальное значение - 20).

1.2.6. Таблица _1SSTREAM - Последовательность документов

Краткое описание: таблица предназначена для хранения информации о последовательностях.
Название поля Описание
ID Идентификатор последовательности (числовое представление). Тип – Число(4).
DATE_TIME_DOCID Дата+Время+идентификатор документа, на котором установлена последовательность. Это поле аналогично полю DATE_TIME_IDDOC таблицы _1SJOURN. Тип – Строка(23).

1.2.7. Таблица _1SSYSTEM - Системная

Краткое описание: таблица предназначена для хранения информации о датах точки актуальности и рассчитанного периода бухгалтерских итогов, также в таблице хранится информация о параметрах УРБД.
Название поля Описание
CURDATE Дата точки актуальности. Тип - Дата (для SQL DateTime).
CURTIME Время точки актуальности. Время хранится в десятичном виде: (Часы*3600+Минуты*60+секунды)*10000. Преобразовав значение этого поля в 36-ричное значение, получим вторую подгруппу значений поля DATE_TIME_IDDOC (сред(DATE_TIME_IDDOC ,9,6)) таблицы 1SJOURN. Тип - Число(4).
EVENTIDTA ID документа, на котором установлена ТА. Тип - Строка(9).
DBSIGN Код базы УРИБ (ТекущаяИБКод()). Тип - Строка(3).
DBSETUUID GUID базы УРИБ. Тип - Строка(36).
SNAPSHPER Периодичность итогов регистров остатков (устанавливается в меню Операции - Управление оперативными тогами - Периодичность сохранения остатков). F – 5 дней. C – Декада (10 дней). T – 15 дней. M – месяц. Тип - Строка(1).
ACCDATE Дата актуальности бухгалтерских итогов. Тип - Дата (для SQL DateTime).
FLAGS ??? Тип - Число(4).



Периодичность итогов регистров оборотов: «D» - День, «W» - Неделя, «C» - Декада, «M» - Месяц, «Q» - Квартал, «Y» - Год.
1.2.8. Таблица _1SUIDCTL - Уникальности

Краткое описание: таблица предназначена для хранения информации о последнем ID коде документов и справочников.
Название поля Описание
TYPEID Идентификатор вида справочника, или же 0 для всех документов (у всех документов сквозная нумерация ID). Тип – Число(4).
MAXID Максимальный используемый идентификатор (ID).

1.2.9. Таблица _1SUSERS - Соединения

Краткое описание: таблица предназначена для хранения информации о подключенных пользователях. В таблице всегда одна строка. Именно по наличию строки в этой таблице 1С судит об аварийном завершении программы и предлагает переиндексировать ИБ в случае dbf-версии.
Название поля Описание
USRSCNT Количество подключенных пользователей к 1С в режиме 1С предприятия.
NETCHGCN Счетчик действий пользователей, которые привели к изменению в базе данных (записи в таблицы). Счетчик учитывает количество записей в таблицы (т.е. в случае проведения документа с несколькими движениями учитывается каждое движение).

2. Подсистема справочников и констант
2.1. Предисловие

Данная часть предназначена для тех, кто пытается разобраться в структуре хранения данных в системе 1С версии 7.7. А также покажет, как можно получать данные напрямую из таблиц 1С, минуя программу 1С. Для понимания того, о чем идет речь в статье, необходимо понимать принципы работы 1С версии 7.7 и иметь начальные навыки работы с SQL Server Enterprise Manager и SQL Server Query Analyzer.
2.2. Введение

Что являют собой справочники? В понимании 1С - это объекты для хранения условно постоянной информации (константы - для хранения постоянной или очень редко изменяющейся информации).

С токи зрения же теории баз данных, справочники являются типичными таблицами (по одной на каждый справочник). А вот с константами - гораздо сложнее. Точнее с первого взгляда проще, но это только с первого взгляда. Так, все константы хранятся в одной таблице _1SCONST, но также в этой таблице хранятся и значения всех периодических реквизитов справочников.

Все таблицы справочников имеют почти одинаковые имена, эти таблицы именуются первыми двумя символами SC, далее следует десятичное представление справочника. Это десятичное представление - сквозная нумерация всех объектов внутри конфигурации (включая реквизиты и т.д.). В наших примерах таблица справочника будет иметь имя таблицы SC19, т.е. этот справочник был создан 19-ым по номеру в конфигурации среди всех объектов.
2.3. Описание полей таблиц справочниковНазвание поля Описание
ROW_ID Порядковый номер записи в таблице. Тип - Число(int).
ID ID элемента, тип «строка», по этому полю осуществляется связь с таблицами, где в качестве реквизита выбирается справочник, а также с таблицей констант (для периодических реквизитов). Нумерация сквозная, именно этот код должен быть уникальным в пределах таблицы. Тип - Char(9).
CODE Номер элемента (Код) справочника. Тип - Char(n), где n - длина номера справочника. Если длина кода = 0, это поле отсутствует.
DESCR Наименование элемента. Тип - Char(n), где n - длина номера справочника. Если длина кода = 0, это поле отсутствует.
ISMARK Флаг пометки на удаление элемента. Тип - bit. 0 - не помечен, 1 - помечен.
VERSTAMP Количество изменений записи таблицы. Изменением считается любое действие "Изменить (открыть)" + действия при изменении структуры. Тип - Integer.
SPххx Реквизит справочника(типы: Numeric, DateTime, Char(n), n=1:999).
TSPххx Дополнение к реквизиту, заполняется только для неопределенных типов значений (используется совместно с полем «SPххx»). Тип - Char(3).
PARENTID ID элемента, являющегося родителем (группой) для текущей записи (элемента). Поле связано с полем ID или же если родителя нет - заполнено пустым ID ' 0 '. Тип - Сhar(9). Это поле появляется в таблице справочника лишь тогда, когда справочник имеет больше 1 уровня.
ISFOLDER Флаг того, что запись является элементом или группой. Для групп это поле равно 1, для элементов - 2. Тип - tinyint (0-255).
PARENTEXT ID элемента, являющегося владельцем (этот элемент подчинен владельцу с этим ID). Тип - Сhar(9). Это поле появляется в таблице справочника лишь тогда, когда справочнику установлено значение "Подчинен" одному из справочников системы.



Особое внимание надо уделить полям «TSP». Это поле создается лишь тогда, когда реквизит (измерение, ресурс) имеет неопределенный тип (длина ID кода 23 символа). Опытным путем было установлено, что это поле по умолчанию заполняется пустой строкой (3 пробела).
Для невыбранного типа (заполняется по умолчанию): TSP235 = ' ' (3 пробела), при этом связанное неопределенное поле SP235 заполняется значением 'U ' (символ U и 22 пробела), что скорее всего означает «Undefined», т.е. неопределенный.
Для типов «Число»: связанное поле SP235 заполняется строковым значением числа, но с символом «N» в начале строки (например 'N 2' для целого числа 2). Возможные значения TSP235:
'F30' - число с плавающей запятой (float);
'320' - число, состоящее только из десятичных (0.99, т.е. Число(3,2)).
'A00' - целое число (int);
Для типов «Строка»: связанное поле SP235 заполняется строкой, но с символом 'S' слева (например 'SАбвгдежзклимно '). Возможные значения TSP235 в этом случае: количество знаков, определенных для строкового значения, например, '14' - количество знаков в строке, в 36-ричном формате (_IDToStr). Следует отметить, хотя 1С и назначает длину строки больше 22 символов, но фактически в этом поле можно хранить только 22 символа (1 разряд приходиться на обозначение типа поля 'S').
Для типов «Дата»: связанное поле SP235 заполняется строкой, но с символом 'D' слева (например 'D20050303 '). TSP235 - ' ' (3 пробела), т.е. поле пустое.
Для типов 1С (Справочник, Документ, Перечисление, Счет, и т.д.): связанное поле SP235 заполняется строкой включающий полный идентификатор объекта (его тип, вид, внутренний ИД код). Следует отметить, что для типов «Перечисление» нельзя не назначать вид, так как неназначение вида приведет к несовпадению типов и незаполнению неопределенного поля. Возможные значения TSP235:
'0 ' - был назначен только тип объекта, вид не назначен (например «НазначитьТип(«Справочник»)»)
'1 ' - был назначен как тип объекта, так и его вид (например «НазначитьТип(«Справочник.Клиенты»)»)
2.4. Описание полей таблицы констант и периодических реквизитов справочников

Описание таблицы _1SCONST находится выше.

Необходимо только учесть, что значения периодических реквизитов элементов справочников хранятся в таблице _1SCONST с заполненным полем OBJID, которое равно полю ID таблицы справочника. Для констант же значение поля OBJID всегда равно ' 0 '. Также необходимо учесть, что выбрать все периодические значения всех реквизитов одного элемента (одной записи) невозможно. Дело в том, что в поле OBJID хранится краткий (строка 9 символов) ID, а такое значение ID не подразумевает определения вида справочника. Соответственно чтобы получить значение конкретного периодического реквизита надо знать десятичное значение реквизита (_StrToID). Т.е., выбрав только с условием по OBJID получим периодические реквизиты всех справочников, с таким ID, а не только одного. Но, поскольку нумерация всех ID объектов конфигурации (включая и все реквизиты) сквозная, то не может быть в двух разных справочниках реквизитов с одинаковым ID реквизита. Соответственно, необходимым условием для получения значений периодических реквизитов является как условие по полю OBJID (ID элемента справочника), так и по полю ID (десятичное значение ID реквизита справочника).
2.5. Примеры запросов
2.5.1. Пример 1

Получение всех записей справочника (кроме периодических реквизитов). SC19 - имя таблицы справочника.
SELECT
*
FROM
SC19
2.5.2. Пример 2

Получение записей только групп справочника (кроме периодических реквизитов). ISFOLDER - флаг того, что это элемент или группа элементов.
SELECT
*
FROM
SC19
WHERE
ISFOLDER = 1
2.5.3. Пример 3

Получение записей только элементов (не групп) справочника (кроме периодических реквизитов).
SELECT
*
FROM
SC19
WHERE
ISFOLDER = 0
2.5.4. Пример 4

Получение записей только непомеченных элементов (не групп) справочника (кроме периодических реквизитов). ISMARK - флаг пометки элемента (или группы) на удаление.
SELECT
*
FROM
SC19
WHERE
ISFOLDER = 0 AND ISMARK = 0
2.5.5. Пример 5

Получение записей элементов с периодическим реквизитом, значение которого получается на максимальную дату (возможно и будущую). В данном примере условие TabConst.ID = 101 необходимо для отбора периодических значений только по реквизиту с десятичным ID кодом равным 101.
SELECT
TabSpr.*, ISNULL(TabConst.VALUE,'')
FROM
SC19 As TabSpr
LEFT OUTER JOIN _1SCONST As TabConst
ON ((TabSpr.ID = TabConst.OBJID)
AND (TabConst.ID = 101)
AND (TabConst.DATE =
(SELECT MAX(TabConstl.DATE)
FROM _1SCONST AS TabConstl
WHERE TabConstl.OBJID = TabConst.OBJID
AND TabConstl.ID = TabConst.ID))
)

В этом примере могут возвращаться и значения NULL для тех реквизитов, для которых не были установлены периодические реквизиты:
SELECT
TabSpr.*,
(SELECT
(TabConst.VALUE)
FROM
_1SCONST As TabConst
WHERE
(TabSpr.ID = TabConst.OBJID)
AND (TabConst.ID = 101)
AND (TabConst.DATE =
(SELECT MAX(TabConstl.DATE)
FROM _1SCONST AS TabConstl
WHERE TabConstl.OBJID = TabConst.OBJID
AND TabConstl.ID = TabConst.ID)
)
)
FROM SC19 As TabSpr

Применение конструкции UNION. В первом запросе получаем список только тех записей, для которых есть установленные периодические реквизиты, а во втором - всех остальных:
SELECT
TabSpr.*, ISNULL(TabConst.VALUE,'')
FROM
SC19 As TabSpr
LEFT OUTER JOIN _1SCONST As TabConst
ON ((TabSpr.ID = TabConst.OBJID) AND (TabConst.ID = 101))
WHERE
(TabConst.DATE =
(SELECT MAX(TabConstl.DATE)
FROM _1SCONST AS TabConstl
WHERE (TabConstl.OBJID = TabConst.OBJID)
AND (TabConstl.ID = TabConst.ID)
)
)

UNION ALL
SELECT
TabSpr.*, ''
FROM
SC19 As TabSpr
WHERE
NOT EXISTS(
SELECT *
FROM _1SCONST As TabConst
WHERE (TabSpr.ID = TabConst.OBJID)
AND (TabConst.ID = 101)
)
2.5.6. Пример 6

Получение записей элементов с периодическим реквизитом, на конкретную дату.

Периодический реквизит получается на 11.03.2006. В данном примере условие TabConst.ID = 101 необходимо для отбора периодических значений только по реквизиту с десятичным ID кодом, равным 101.
SELECT
TabSpr.*, ISNULL(TabConst.VALUE,'')
FROM
SC19 As TabSpr
LEFT OUTER JOIN _1SCONST As TabConst
ON ((TabSpr.ID = TabConst.OBJID)
AND (TabConst.ID = 101)
AND (TabConst.DATE =
(SELECT MAX(TabConstl.DATE)
FROM _1SCONST AS TabConstl
WHERE TabConstl.OBJID = TabConst.OBJID
AND TabConstl.ID = TabConst.ID
AND TabConstl.DATE <=
Convert(DateTime,'20060311',112)
)
)
)
2.5.7. Пример 7

Рассмотрим теперь пример получения значений непериодических реквизитов справочников, которые являются документами или элементами справочника. В системе 1С получение значений таких реквизитов осуществляется обращением к реквизитам и их атрибутам. Например, получение значение реквизита "Менеджер" элемента справочника "Контрагенты" (наименование менеджера). В 1С это легко реализуется, например, если СпрКонтрагенты является объектом "Справочник.Контрагенты" и спозиционирован на конкретном элементе (например "НайтиПоНаименованию("Иванов А. А.")") - то получение менеджера этого элемента осуществляется так: "СпрКонтрагенты.Менеджер.Наименование". Но в самой таблице справочника "Контрагенты" (допустим это таблица "SC191") в поле, отвечающем за реквизит "Менеджер" (например "SP10494") будут значение ID элемента справочника "Менеджеры", а не наименование менеджера (и это вполне нормально). Само же наименование менеджера хранится в другой таблице, это таблица "Сотрудники" (SC258).

Итак, для этого примера надо выполнить запрос, выполняющий 2 действия: 1 - позиционирование на элемент с наименованием "Иванов А. А.", 2 - получение наименования менеджера, для спозиционированного элемента справочника Контрагенты.

Первая часть запроса будет выглядеть так:
SELECT
TabSpr.DESCR As Наименование
FROM
SC191 As TabSpr
WHERE
TabSpr.DESCR = 'Иванов А. А.'

Но приведенный выше код содержит ошибку. Дело в том, что поле DESCR определено как Char, и его длина строго задана и равна длине наименования для справочника "Контрагенты". Например, если длина наименования 50 символов, то предыдущий запрос надо было написать так:
SELECT
TabSpr.DESCR As Наименование
FROM
SC191 As TabSpr
WHERE
TabSpr.DESCR = 'Иванов А. А. '

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

Для первого случая пример запроса будет такой:
SELECT
TabSpr.DESCR As Наименование
FROM
SC191 As TabSpr
WHERE
RTRIM(TabSpr.DESCR) = 'Иванов А. А.'

Для второго случая пример запроса будет такой:
DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT
TabSpr.DESCR As Наименование
FROM
SC191 As TabSpr
WHERE
TabSpr.DESCR = @NAIM

Вторая часть запроса, получение имени менеджера. Необходимо сделать выборку из 2 таблиц. Это можно сделать как минимум двумя способами. В первом примере выборка из 2 таблиц с условием:
DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT
TabSpr.DESCR As Наименование, TabManag.DESCR As Менеджер
FROM
SC191 As TabSpr,
SC258 As TabManag
WHERE
TabSpr.DESCR = @NAIM
AND TabSpr.SP10494 = TabManag.ID

Во втором примере выборка из 2 таблиц с объединением. Этот код аналогичен предыдущему, за исключением того, что происходит объединение с условием, а не выборка из таблиц с условием:
DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT
TabSpr.DESCR As Наименование, TabManag.DESCR As Менеджер
FROM
SC191 As TabSpr
INNER JOIN SC258 As TabManag ON TabSpr.SP10494 = TabManag.ID
WHERE
TabSpr.DESCR = @NAIM

Но эти оба примера не совсем корректные. Дело в том, что запросы работают по полном объединении (или полном условии), т.е. в выборку попадут лишь те записи из таблицы Контрагентов, для которых есть записи в таблице Сотрудников (т.е. поле "Менеджер" справочника Контрагенты заполнено). Если же для элемента с наименованием 'Иванов А. А.' поле Менеджер пустое (в таблице находиться или NULL или значение ' 0 ', именно так 1С хранит значения невыбранных реквизитов), то в выборку не попадет строка таблицы, так как не выполняется условие на вхождение в таблицу Сотрудников (в таблице SC258 в столбце ID нет ни одной записи, для которой есть значение ' 0 ').

Первый пример будет выглядеть так:
DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT
TabSpr.DESCR As Наименование, TabManag.DESCR As Менеджер
FROM
SC191 As TabSpr,
SC258 As TabManag
WHERE
TabSpr.DESCR = @NAIM
AND TabSpr.SP10494 *= TabManag.ID

Второй пример выборки со связыванием 2 таблиц:
DECLARE @NAIM CHAR(50)
SET @NAIM = 'Иванов А. А.'
SELECT
TabSpr.DESCR As Наименование, TabManag.DESCR As Менеджер
FROM
SC191 As TabSpr
LEFT OUTER JOIN SC258 As TabManag ON TabSpr.SP10494 = TabManag.ID
WHERE
TabSpr.DESCR = @NAIM
2.5.8. Пример 8

Рассмотрим пример получения значений записей элементов справочников с их родителями (группами).

Для примера возьмем тот же справочник "Контрагенты" (допустим это таблица "SC191"). Самый простой пример - получение родителя для каждого элемента (не для родителей).
SELECT
TabSpr.DESCR As Наименование, TabSprGr1.DESCR As Родитель
FROM
SC191 As TabSpr
LEFT OUTER JOIN SC191 As TabSprGr1 ON TabSpr.PARENTID = TabSprGr1.ID
WHERE
TabSpr.ISFOLDER = 2

В этом коде условие TabSpr.ISFOLDER = 2 необходимо для отбора только элементов (не групп). Левое внешнее связывание применяется для того чтобы вывести все элементы, а не только те, у которых есть выбранные родители (в случае с INNER JOIN).

Рассмотрим теперь более сложный пример. Получим всех родителей для элементов (т.е. включая и родителей родителей). Для получения родителей надо знать максимальное количество родителей. Это число легко узнать с помощью команды "Метаданные.Справочник(х).КоличествоУровней". Приведем пример для значения количества уровней, равного 4.
SELECT
TabSprGr3.DESCR As Родитель3,
TabSprGr2.DESCR As Родитель2,
TabSprGr1.DESCR As Родитель,
TabSpr.DESCR As Наименование
FROM
SC191 As TabSpr
LEFT OUTER JOIN SC191 As TabSprGr1 ON TabSpr.PARENTID = TabSprGr1.ID
LEFT OUTER JOIN SC191 As TabSprGr2 ON TabSprGr1.PARENTID = TabSprGr2.ID
LEFT OUTER JOIN SC191 As TabSprGr3 ON TabSprGr2.PARENTID = TabSprGr3.ID
WHERE
TabSpr.ISFOLDER = 2

В этом коде условие TabSpr.ISFOLDER = 2 необходимо для отбора только элементов (не групп). Количество внешних связываний равно количеству уровней справочника - 1 (в приведенном примере 3).

В результате выполнения данного запроса получаются данные по всем возможным родителям, даже если нет родителей у элемента 1 или 2, то все равно будет 3 колонки с родителями, но в таком случае в качестве родителя будет значение NULL. Для того чтобы в первой колонке всегда был родитель (если есть у элемента родитель), то необходимо выполнить вот такой запрос:
SELECT
CASE WHEN TabSprGr3.DESCR IS NOT NULL THEN TabSprGr3.DESCR
ELSE CASE WHEN TabSprGr2.DESCR IS NOT NULL THEN TabSprGr2.DESCR
ELSE TabSprGr1.DESCR END END As Родитель3,
CASE WHEN (TabSprGr2.DESCR IS NOT NULL)
AND (TabSprGr3.DESCR IS NOT NULL) THEN TabSprGr2.DESCR
ELSE TabSprGr1.DESCR END As Родитель2,
CASE WHEN (TabSprGr2.DESCR IS NOT NULL)
AND (TabSprGr3.DESCR IS NOT NULL) THEN TabSprGr1.DESCR
ELSE NULL END As Родитель,
TabSpr.DESCR As Наименование
FROM
SC191 As TabSpr
LEFT OUTER JOIN SC191 As TabSprGr1 ON TabSpr.PARENTID = TabSprGr1.ID
LEFT OUTER JOIN SC191 As TabSprGr2 ON TabSprGr1.PARENTID = TabSprGr2.ID
LEFT OUTER JOIN SC191 As TabSprGr3 ON TabSprGr2.PARENTID = TabSprGr3.ID
WHERE
TabSpr.ISFOLDER = 2

В этом коде условие TabSpr.ISFOLDER = 2 необходимо для отбора только элементов (не групп).

Вместо проверки на NULL можно воспользоваться функцией COALESCE, которая ищет слева направо в переданных параметрах значения не равные NULL, но тогда усложняется процесс получения различных значений групп на каждом уровне, поэтому рекомендуется это делать уже на клиентском приложении (после выполнения запроса).
2.5.9. Пример 9

Следующий этап - выборка элементов из подчиненного справочника. Для примера возьмем справочник "Контрагенты" (SC191) и справочник "ДенежныеСчета" (SC146), подчиненный справочнику Контрагенты.
SELECT
TabSpr.DESCR As Наименование,
TabRS.SP143 As НомерСчета
FROM
SC191 As TabSpr
LEFT OUTER JOIN SC146 As TabRS ON TabSpr.ID = TabRS.PARENTEXT
WHERE
TabSpr.ISFOLDER = 2

В этом коде условие TabSpr.ISFOLDER = 2 необходимо для отбора только элементов (не групп). Ведь в 1С подчиненные элементы справочника существуют только у элементов. В результат выборки попадут и помеченные на удаление подчиненные элементы. Если надо получить только непомеченные подчиненные элементы (расчетные счета), тогда текст запроса должен быть таким:
SELECT
TabSpr.DESCR As Наименование,
TabRS.SP143 As НомерСчета
FROM
SC191 As TabSpr
LEFT OUTER JOIN SC146 As TabRS ON TabSpr.ID = TabRS.PARENTEXT
AND TabRS.ISMARK = 0
WHERE
TabSpr.ISFOLDER = 2

В этом коде условие TabRS.ISMARK = 0 необходимо включать как раз в условие связывания, так как если включить условие в раздел WHERE - то те записи справочника Контрагенты, у которых есть запись в подчиненном справочнике и этот элемент помечен на удаление, вообще не попадут в результат запроса.
3. Подсистема документов и регистров
3.1. Предисловие

Данная часть предназначена для тех, кто пытается разобраться в структуре хранения данных в системе 1С версии 7.7. А также покажет, как можно получать данные напрямую из таблиц 1С, минуя программу 1С. Для понимания того, о чем идет речь в статье, необходимо понимать принципы работы 1С версии 7.7 и иметь начальные навыки работы с SQL Server Enterprise Manager и SQL Server Query Analyzer.
3.2. Введение

Следует учесть, что в статье рассматриваются только примеры для SQL формата базы данных. Для DBF формата есть некоторые особенности, и не все запросы буду идентичны как для DBF, так и для SQL формата баз. В любом случае для выполнения запросов к DBF необходимы или ODBC или OLEDB драйвера. Можно использовать любой драйвер, работающий с DBASE2 форматом DBF. Опыт показывает, что наиболее сопоставимы по тексту запросов драйвера Visual FoxPro (ведь MS SQL Server и MS Visual FoxPro принадлежат одной фирме, и есть вероятность, что тексты запросов будут унифицироваться в будущем). Я советую использовать драйвер OLE DB Visual FoxPro 9.0, так как в нем меньше ограничений и он поддерживает больше функций и методов по сравнению с ODBC Visual FoxPro 6.0. Скачать последний драйвер (OLE DB Visual FoxPro 9.0) можно по этой ссылке: http://www.microsoft.com/downloads/details.aspx?FamilyId=E1A87D8F-2D58-491F-A0FA-95A3289C5FD4&displaylang=en.

Для того, чтобы получить ID имен таблиц, достаточно просмотреть файл КаталогоИБ()+"1cv7.dds" ("1cv7.dd" для DBF формата базы).

Для получения ID объектов 1С можно воспользоваться компонентой 1С++ (http://www.1cpp.ru/) позволяющей получать ID объектов 1С прямо из 1С (например, преобразование значения ТекущийДокумент() в его ID (строка(9), или в длинную строку ИД (строка(13)), включая вид документа, или в самый длинный ID длиной 23 символа (строка(23)), включая ID типа, вида и самого объекта).

Также получение ID объектов рассмотрено в этой статье: http://www.sinor.ru/~my1c/knowhow/get_id.html

Как формируется ID код, можно почитать выше.
3.3. Общее описание хранения данных 1С в подсистеме документов

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

3.3.1. Краткое описание таблиц

DH - таблицы документов (реквизитов шапки). Создаются при первом добавлении реквизита шапки в документ.

DT – таблицы документов (реквизитов табличной части). Создаются при первом добавлении реквизита табличной части в документ.

_1SJOURN – таблица документов (общих и системных реквизитов). Именно это и есть таблица полного журнала документов. Все остальные журналы формируются по полному журналу с дополнительным отбором по типам документов.

_1SCRDOC – Таблица подчиненных документов. В этой таблице хранятся ссылки документов-родителей на подчиненные документы.

В системе 1С 7.7 документы всегда хранятся максимум в 2 таблицах (т.е. табличная часть может быть только одна, и все данные табличной части хранятся в этой таблице) (за исключением общих реквизитов, которые хранятся в одной общей таблице).

Данные документов хранятся в 2 таблицах: DHххх и DTххх, где ххх – десятичный идентификатор вида документа (в файле 1cv7.MD). Непосредственные номера таблиц документов можно посмотреть в файле 1cv7.dds (для dbf версии - 1cv7.dd).

В таблице DHххх – хранятся данные реквизитов шапки (за исключением общих реквизитов). Т.е. на каждый реквизит отведено минимум одно поле таблицы (может быть и 2 в случае неопределенного реквизита). Также в этой таблице хранятся данные по некоторым реквизитам табличной части (рассмотрим позже). В таблице DTххх – хранятся данные реквизитов табличной части (каждый реквизит минимум одно поле таблицы плюс системное поле LINENO_ - номер строки документа). Отдельно следует рассмотреть реквизиты табличной части типа Число (в понимании 1С), по которым установлен флаг «Итог по колонке» в свойствах реквизита. Итог по этим реквизитам хранится в таблице реквизитов шапки (DHххх), причем название поля совпадает для таблицы шапки (DH) и для таблицы табличной части (DT).

В таблице реквизитов шапки хранятся все реквизиты за исключением общих реквизитов и атрибутов документов (также и системных, таких как время, флаг проведения и удаления). Они все хранятся в одной общей таблице журналов _1SJOURN (1SJOURN для dbf). Описание полей таблицы _1SJOURN можно посмотреть выше.

Таблица журналов _1SJOURN, таблица реквизитов шапки DHххх и таблица реквизитов табличной части DTххх «связаны» по полю IDDOC. Связь эта «мнимая» (т.е. значения полей как бы равны для соответствующих строк), но это не стандартные связи SQL.

Поэтому необходимо учесть, что 1С не создает связей для ограничения целостности данных (связи между полями) средствами MS SQL. Также особое внимание надо уделить тому, что все поля создаются как NOT NULL, т.е. в них нельзя записывать значения типа NULL (видимо поэтому и нет связей). И если значение пустое (в понимании SQL должно быть NULL), то 1С заполняет это поле таким образом:
Для неопределенного типа: ' ' (3 пробела), при этом связанное поле TSP заполняется значением 'U '.
Для типа «число»: 0 (нулем).
Для типа «строка»: ' ' (строка с пробелами на всю размерность поля, в примере - 10 пробелов на значение Строка(10)).
Для типа «дата»: 01.01.1753. Именно эта дата является началом отсчета дат для типов smalDataTime в SQL.
Для типа «Справочник», «Документ», «Счет», «Календарь», «ВидРасчета» неопределенного вида: ' 0 0 ' (строка(13)).
Для типа «Справочник», «Документ», «Счет», », «ВидРасчета» определенного вида, «Перечисление»: ' 0 ' (строка(9)).
Для типа «Планы счетов», «Виды Субконто» неопределенного вида: ' 0 ' (строка(9)).
3.3.2. Примеры запросов
3.3.2.1. Пример 1

Получение всех документов одного вида со всеми реквизитами шапки (кроме общих):
SELECT * FROM DH14
3.3.2.2. Пример 2

Получение всех документов одного вида со всеми реквизитами шапки (включая все общие и системные реквизиты):
SELECT
TabJ.*, Tab1.*
FROM
DH14 As Tab1
INNER JOIN
_1SJOURN As TabJ ON (Tab1.IDDOC = TabJ.IDDOC)
3.3.2.3. Пример 3

Получение всех документов одного вида со всеми реквизитами шапки (включая все общие и системные реквизиты), а также всех реквизитов табличной части:
SELECT
TabJ.*, Tab1.*, Tab2.*
FROM
DH14 As Tab1
INNER JOIN
_1SJOURN As TabJ ON (Tab1.IDDOC = TabJ.IDDOC)
INNER JOIN
DT14 As Tab2 ON (Tab1.IDDOC = Tab2.IDDOC)
3.3.2.4. Пример 4

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

В этом примере необходимо учесть, что дата документа храниться в SQL и DBF формате базы по-разному. Но, в любом случае, дата документа храниться в таблице журналов _1SJOURN, рассмотрим пример для SQL базы:
SELECT
TabJ.*, Tab1.*, Tab2.*
FROM
DH14 As Tab1
INNER JOIN
_1SJOURN As TabJ ON (Tab1.IDDOC = TabJ.IDDOC)
INNER JOIN
DT14 As Tab2 ON (Tab1.IDDOC = Tab2.IDDOC)
WHERE
LEFT(TabJ.DATE_TIME_IDDOC,8) = '20050219'
3.3.2.5. Пример 5

Получение списка документов родителей по подчиненному документу (получение только общих реквизитов с отборами и системных реквизитов).
SELECT
TabJ.*
FROM
_1SCRDOC As TabRod
INNER JOIN
_1SJOURN As TabJ ON (SUBSTRING(TabRod.PARENTVAL,7,9) = TabJ.IDDOC)
WHERE
(TabRod.MDID = 0) -- только документы, без граф отбора
AND (TabRod.CHILDID = ' 6C3RK ')

где ' 6C3RK ' – ID документа родителя
3.3.2.6. Пример 6

Получение списка подчиненных документов (получение только общих реквизитов с отборами и системных реквизитов).
SELECT
TabJ.*
FROM
_1SCRDOC As TabRod
INNER JOIN
_1SJOURN As TabJ ON (TabRod.CHILDID = TabJ.IDDOC)
WHERE
(TabRod.MDID = 0) -- только документы, без граф отбора
AND (TabRod.PARENTVAL = @ИдДокРодителя)
ORDER BY
TabRod.CHILD_DATE_TIME_IDDOC

где @ИдДокРодителя – ID документа родителя (включая тип и вид документа, т.е. 23 символа), получить такой ID можно с помощью метода ЗначениеВСамуюДлиннуюСтрокуБД(ТекДок) класса MetaDataWork компоненты 1С++.
3.3.2.7. Пример 7

Получение списка только проведенных документов. Для отбора только проведенных документов можно воспользоваться значением поля CLOSED таблицы _1SJOURN. Так как первый бит его отвечает за то, проведен документ или нет, соответственно, сделав унарное умножение с 1, получим результат в виде 1 - проведен, 0 - непроведен.
SELECT
TabJ.*, Tab1.*
FROM
DH14 As Tab1
INNER JOIN
_1SJOURN As TabJ ON (Tab1.IDDOC = TabJ.IDDOC)
WHERE
TabJ.CLOSED&1 = 1
3.3.2.8. Пример 8

Получение списка документов, принадлежащих разным компонентам (у которых включен флажок принадлежности к типам учета). Для отбора документов по типам учета применяется унарное умножение поля APPCODE с требуемым значением. Список значений для отбора по типам учета:
(1) 000001 - опер учет.
(2) 000010 - расчет.
(4) 000100 - бух учет.
(8) 001000 - движения периодических реквизитов.
(16) 010000 - существует документ операция у документа (Документ может быть непроведен, но тогда должно быть выбрано Создавать операцию - Всегда в окне свойства документа).
(32) 100000 - Выполнен метод Операция.Записать(); в документе, принадлежащем бух. учету.

Соответственно, если надо выбрать документы, принадлежащие оперативному и бухгалтерскому учету, надо наложить бинарную маску (бинарное умножение) на 000001 и 000100, или же вместе 000101, или же в десятичной системе 5.
SELECT
TabJ.*, Tab1.*
FROM
DH14 As Tab1
INNER JOIN
_1SJOURN As TabJ ON (Tab1.IDDOC = TabJ.IDDOC)
WHERE
TabJ.APPCODE&5 = 5
3.3.2.9. Пример 9

Получение списка документов по графе отбора.

Все графы отбора хранятся в 1С в таблице _1SCRDOC. Также в этой таблице хранятся и ссылки на подчиненные документы. Разница между подчиненными документами и графами отбора в том, что поле MDID для подчиненных документов равно 0, а для граф отбора - десятичному идентификатору графы отбора. Допустим, надо получить список документов с отбором по графе "Контрагент". Получим десятичный ИД этой графы отбора с помощью метода ИДОбъекта класса MetaDataWorks компоненты 1С++. К примеру так: ИДГрафы = глMDW.ИДОбъекта(Метаданные.ГрафаОтбора("Контрагент")), где глMDW = СоздатьОбъект("MetaDataWork"). Отбирать будем по столбцу PARENTVAL, в котором хранится полный идентификатор (23 символа) элемента справочника. Получить полный ИД необходимого элемента можно тем же путем, что и идентификатор для графы отбора, но немного другим методом. Например, отбираем по клиенту, значение которого хранится в переменной ВыбКлиент, тогда полный его идентификатор получаем как: IDКлиента = глMDW.ЗначениеВСамуюДлиннуюСтрокуБД(ВыбКлиент).
SELECT
TabJ.IDDOC AS Документ,
TabJ.IDDOCDEF AS Документ_вид,
TabJ.DOCNO AS НомерДок
FROM
dbo._1SCRDOC AS TabGraf (NOLOCK)
INNER JOIN _1SJOURN AS TabJ (NOLOCK) ON (TabJ.IDDOC = TabGraf.CHILDID)
WHERE
MDID = 14730 -- ИД графы отбора
AND PARENTVAL = 'B1 4U 2 ' --23 ID элемента справочника
ORDER BY
TabJ.DATE_TIME_IDDOC

где соответственно ИДГрафы = 14730, а IDКлиента = 'B1 4U 2 '.
3.4. Регистры
3.4.1. Общее описание подсиcтемы регистров

Физически регистры остатков состоят из двух таблиц: таблица остатков RGххх и таблица движений RAххх. В таблице движений хранятся все движения документов по регистрам. Список документов, которые сделали движения по регистру, можно получить, выбрав записи из таблицы журналов _1SJOURN с условием равенства поля RFxxx 1 (или не равно 0), например (в случае, если идентификатор регистра 16, т.е. таблица движений = RG16, а таблица остатков = RA16), для получения всех документов, сделавших движения по регистру, можно сделать так:
SELECT
*
FROM
_1SJOURN
WHERE
(RF16 = 0x1)
AND (CLOSED&1=1)
AND (APPCODE&1=1)

где CLOSED&1=1 – ограничение на выбор записей только проведенных документов. Фактически при корректных записях в базе, это условие лишнее, но при каких-то «глюках» таким условием можно «отловить» непроведенные документы с существующими движениями (к сожалению, такое может встречаться).

А вот условие APPCODE&1=1 означает отобрать документы, принадлежащие оперативному учету. Подробнее о полях таблицы _1SJOURN см. выше.

Рассмотрим более подробно, как хранятся данные в таблицах RG и RA.

В таблицу RA записываются все движения документов, с учетом флага прихода или расхода. Т.е. в таблице RA хранятся сведения о всех движениях документов, которые сделали движения по этому регистру. Соответственно методы "ДвижениеПриходВыполнить()/ДвижениеПриход()" записывают одну строку в таблицу RA с флагом приход (DEBKRED = 0), а методы "ДвижениеРасходВыполнить()/ДвижениеРасход()" также добавляют одну запись в таблицу RA, но уже с флагом расхода (DEBKRED = 1). Соответственно, если в регистре отражается приход и расход по складу, и самое первое движение по регистру есть первый приход на склад (т.е. это первое поступление на склад, до этого на складе ничего не было), то для получения остатка на складе нам надо сложить все приходы и вычесть все расходы. Хорошо, когда таких приходов немного, и с начала заполнения таблиц прошло пару месяцев. Теперь представим, что система работает пару лет. В таком случае для получения остатка на складе нам надо выполнить операцию сложения всех приходов и вычитания всех расходов с начала работы. А если еще учесть, что остатки нам надо считать каждый раз при формировании нового движения расхода (внесения новой записи в таблицу) (для контроля остатка или для расчета себестоимости), то налицо лишние операции сканирования всех записей таблицы RA. Для того чтобы не делать таких пересчетов, 1С разработали регистры остатков таким образом, что они состоят из двух таблиц. 1 - RA (хранятся все движения с флагом прихода или расхода), 2 - RG для хранения промежуточных итогов (как бы заблаговременно подсчитанные итоги по таблице RA за какой-то период).
3.4.2. Рассмотрим примеры

Рассмотрим получение остатков и оборотов по регистру остатков.

Период хранения остатков - месяц. Исходная таблица остатков (RG):
PERIOD SP20 SP22 SP21
2005-02-01 00:00:00.000 ' AA ' ' 1A ' 35.00
2005-03-01 00:00:00.000 ' AA ' ' 1A ' 20.00



Исходная таблица движений (RA):
IDDOC LINENO_ ACTNO DEBKRED IDOCDEF DATE_TIME_IDDOC SP20 SP22 SP21
1 0 1 0 12 '200502157579C0 1 ' ' AA ' ' 1A ' 10.00
2 0 1 0 12 '20050215759EHS 2 ' ' AA ' ' 1A ' 10.00
6 0 1 0 23 '200502157QOSK0 6 ' ' AA ' ' 1A ' 15.00
7 0 1 1 23 '200503013KLMO0 7 ' ' AA ' ' 1A ' 15.00



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

При описании таблицы остатков (RG) необходимо отметить важную особенность.

Особенность заключается в том, что записи в таблице сортируются по периодам остатков (колонка «PERIOD» и это поле является кластерным индексом (в состав индекса также всегда входит и 1 измерение регистра), по умолчанию периодичность регистров остатков - месяц, именно этот случай и рассматривается). В поле «PERIOD» хранится ДАТА НАЧАЛА периода (МЕСЯЦА). Исключением является период, совпадающий с точкой актуальности, в этом периоде все записи относятся не к концу месяца, а к времени или документу ТА (рассчитаны по этот документ, т.е. с учетом его движений, если документ последовательности проведен). Т.е. другими словами, в этой таблице всегда записи или на конец месяца, или на ТА.
3.4.2.1. Структура и описание полей таблиц регистров
3.4.2.1.1. Таблица RGxxx

Краткое описание: таблица предназначена для хранения итогов по периодам в разрезе по измерениям и по всем ресурсам регистра.
Название поля Описание
PERIOD Период остатков. Всегда равен началу периода (месяц для нашего примера). Тип - DateTime (для dbf - Date).
SPххx Измерения, ресурсы или атрибуты регистра (типы: Numeric, DateTime, Char(n), n=1:999).
TSPyyy Дополнение к измерению или реквизиту, заполняется только для неопределенных типов значений (используется совместно с полем «SPyyy»). Тип - Char(3).

3.4.2.1.2. Таблица RAxxx

Краткое описание: таблица предназначена для хранения движений по регистрам (включая все измерения, ресурсы и реквизиты регистра).
Название поля Описание
IDDOC ID документа, тип «строка», по этому полю осуществляется связь с таблицей документов («1SJOURN» или «_1SJOURN» для SQL). Тип - Char(9).
LINENO_ Номер строки документа (то, что устанавливается методом «ПривязатьСтроку()»). Тип - SmallInt.
ACTNO Порядковый номер движения (это не номер строки, а именно движения). Тип - Integer.
DEBKRED Флаг прихода или расхода. 0 - приход, 1 - расход. Тип – bit.
IDOCDEF Поле присутствует лишь тогда, когда в свойствах регистра установлен флажок «Быстрая обработка движений». ID вида документа (из метаданных, причем номер, а не 36-ричное представление). Тип - Integer.
DATE_TIME_IDDOC Поле присутствует лишь тогда, когда в свойствах регистра установлен флажок «Быстрая обработка движений». Значения этого поля идентичны значениям поля «DATE_TIME_IDDOC» таблицы «_1SJOURN» (журналов). Тип - Char(23).
SPххx Измерения, ресурсы или атрибуты регистра (типы: Numeric, DateTime, Char(n), n=1:999).
TSPyyy Дополнение к измерению или реквизиту, заполняется только для неопределенных типов значений (используется совместно с полем «SPyyy»). Тип - Char(3).



Особое внимание надо уделить полям «TSP». Это поле создается лишь тогда, когда реквизит (измерение, ресурс) имеет неопределенный тип (длина ID кода 23 символа). Опытным путем было установлено, что это поле по умолчанию заполняется пустой строкой (3 пробела).
Для невыбранного типа (заполняется по умолчанию): TSP235 = ' ' (3 пробела), при этом связанное неопределенное поле SP235 заполняется значением 'U ' (символ U и 22 пробела), что скорее всего означает «Undefined», т.е. неопределенный.
Для типов «Число»: связанное поле SP235 заполняется строковым значением числа, но с символом «N» в начале строки (например 'N 2' для целого числа 2). Возможные значения TSP235:
'F30' - число с плавающей запятой (float);
'320' - число, состоящее только из десятичных (0.99, т.е. Число(3,2)).
'A00' - целое число (int);
Для типов «Строка»: связанное поле SP235 заполняется строкой, но с символом 'S' слева (например 'SАбвгдежзклимно '). Возможные значения TSP235 в этом случае: количество знаков, определенных для строкового значения, например, '14' - количество знаков в строке, в 36-ричном формате (_IDToStr). Следует отметить, хотя 1С и назначает длину строки больше 22 символов, но фактически в этом поле можно хранить только 22 символа (1 разряд приходиться на обозначение типа поля 'S').
Для типов «Дата»: связанное поле SP235 заполняется строкой, но с символом 'D' слева (например 'D20050303 '). TSP235 - ' ' (3 пробела), т.е. поле пустое.
Для типов 1С (Справочник, Документ, Перечисление, Счет, и т.д.): связанное поле SP235 заполняется строкой включающий полный идентификатор объекта (его тип, вид, внутренний ИД код). Следует отметить, что для типов «Перечисление» нельзя не назначать вид, так как неназначение вида приведет к несовпадению типов и незаполнению неопределенного поля. Возможные значения TSP235:
'0 ' - был назначен только тип объекта, вид не назначен (например «НазначитьТип(«Справочник»)»)
'1 ' - был назначен как тип объекта, так и его вид (например «НазначитьТип(«Справочник.Клиенты»)»)
3.4.2.2. Пример 1

Получение итогов на ТА или на конец периода (месяца в нашем случае).

Поскольку итоги в таблице RG хранятся на ТА или на конец месяца (для нашего примера), то для получения итогов не обходимо лишь указать условие по периоду, на который нам надо получить итоги.
SELECT
TabRegOst.SP20 As Товар,
Sum(TabRegOst.SP21) As КвоКонОст
FROM
RG13 As TabRegOst
WHERE
TabRegOst.PERIOD = @PERIODR
GROUP BY
TabRegOst.SP20
ORDER BY
TabRegOst.SP20

где @PERIODR – переменная типа DateTime, условие по которой служит для получения результата запроса на тот период, который нам необходим.

Например, если надо получить остаток на ТА, при этом ТА = «19.02.2006», то переменной @PERIODR необходимо присвоить значение начала месяца «01.02.2006».
SET @PERIODR = CONVERT(DateTime, '20060201',112)

Если необходимо рассчитать остаток на конец января, то переменной необходимо присвоить значение «01.01.2006»:
SET @PERIODR = CONVERT(DateTime, '20060101',112)

Нетрудно догадаться, что если необходимо получить остаток на конец января, то надо в качестве условия поставить начало февраля (ведь остаток на конец января есть остаток на начало февраля. Именно так и делает 1С при расчете остатка на конец периода).

Спросите, почему присвоение переменной @PERIODR происходит с помощью функции CONVERT, да и еще дата в таком странном формате? Это все дело привычки (дело в том, что в таблице журналов дата документа в поле DATE_TIME_IDDOC как раз храниться в виде строки ГГГГММДД).
3.4.2.3. Пример 2

Получение сумм прихода и расхода за один месяц (оборотов).

Поскольку обороты хранятся в таблице RA, запрос выполняется только по этой таблице с условием на дату документа, сделавшего движение.
SELECT
TabReg.SP20 As Товар,
Sum(TabReg.SP21*((DEBKRED+1)%2)) As КвоПриход,
Sum(TabReg.SP21*DEBKRED) As КвоРасход,
FROM
RA13 As TabReg
INNER JOIN _1SJOURN As TabJ
ON (TabReg.IDDOC = TabJ.IDDOC)
WHERE
TabJ.DATE_TIME_IDDOC >= @PERIODN
AND TabJ.DATE_TIME_IDDOC <= @PERIODK
GROUP BY
TabReg.SP20
ORDER BY
TabReg.SP20

где @PERIODN, @PERIODK – переменные типа Char(8), условия по которых служат для получения результата запроса на тот период, который нам необходим.

В нашем случае @PERIODN = «20060201», @PERIODK = «20060228».

Код для установки переменных:
SET @PERIODR = '20060201'
SET @PERIODR = '20060228'
3.4.2.4. Пример 3

Получение итогов и остатков на произвольную дату.

Поскольку итоги в нашем примере хранятся только на конец месяца или ТА, то необходимо выполнить 2 запроса, первый – для получения итога на начало периода (из примера 1), второй – для получения оборотов (из примера 2).
SELECT
TMP.Товар As Товар,
SUM(TMP.НачОст) As НачОст,
SUM(TMP.Приход) As Приход,
SUM(TMP.Расход) As Расход,
SUM(TMP.НачОст + TMP.Приход - TMP.Расход) As КонОст
FROM
(
SELECT
TabRegOst.SP20 As Товар,
TabRegOst.SP21 As НачОст,
0 As Приход,
0 As Расход
FROM
RG13 As TabRegOst
WHERE
TabRegOst.PERIOD = @PERIODR
UNION ALL
SELECT
TabReg.SP20 As Товар,
0 As НачОст,
(TabReg.SP21*((DEBKRED+1)%2)) As Приход,
(TabReg.SP21*DEBKRED) As Расход
FROM
RA13 As TabReg
INNER JOIN _1SJOURN As TabJ
ON (TabReg.IDDOC = TabJ.IDDOC)
WHERE
TabJ.DATE_TIME_IDDOC >= @PERIODN
AND TabJ.DATE_TIME_IDDOC <= @PERIODK
) As TMP
GROUP BY
TMP.Товар
ORDER BY
TMP.Товар

где @PERIODR – переменная типа DateTime, условие по которой служит для получения результата запроса на период, меньший на 1 от рассчитываемого. В нашем случае для получения остатка на 20.02.2006 этой переменной необходимо присвоить значения начала января, т.е. «01.01.2006» (так как в случае условия на 01.02.2006 получим остаток или на конец февраля, или на точку актуальности):
SET @PERIODR = CONVERT(DateTime, '20060101',112)

@PERIODN, @PERIODK – переменные типа Char(8), условия по которым служат для получения результата в запросе за определенный период. Для нашего примера @PERIODN = «20060201», @PERIODK = «20060220»:
SET @PERIODR = '20060201'
SET @PERIODR = '20060220'

Следует отметить, что условие:
AND TabJ.DATE_TIME_IDDOC <= @PERIODK

предполагает получение остатка на начало дня, это связано с тем, что в поле DATE_TIME_IDDOC хранится не только дата в текстовом виде, но и время, и идентификатор документа. Так, чтобы получить строку представления начала дня, необходимо выполнить функцию СформироватьПозициюДокумента(<Дата>,<Час>,<Мин>,<Сек>,<ФлагКонцаСекунды>), которая и сформирует строку даты и времени в текстовом представлении. Например, для даты 14.03.2006 функция вернет значение '#20060314 0 0 ' (в нашем случае символ # надо убрать). Как видим, первый 0 - это 36 ричное представление времени, а вот второй 0 - это ID документа (невыбранного документа). Но, поскольку сравнению в условию поддается каждый символ, то любая пустая строка будет меньше любой заполненной строки (в нашем случае пробелами и 0). Поэтому, чтобы получить остаток на конец дня, надо или отсечь лишние символы в поле DATE_TIME_IDDOC, или же сравнивать с завтрашней датой. Я предпочитаю второй вариант, так как в таком случае нагрузка на сервер меньше (не требуется выполнять обрезание значений функцией Left), но приведу в качестве примера оба решения:
AND LEFT(TabJ.DATE_TIME_IDDOC,8) <= @PERIODK

или условие предыдущее, но значение переменной устанавливается на дату @PERIODK = «20060221»:
SET @PERIODR = '20060221'
3.4.2.5. Пример 4

Получение итогов и остатков на документ.

Задача аналогична задаче из предыдущего примера, за исключением того, что условие периода не по дату, а по позицию документа.
SELECT
TMP.Товар As Товар,
SUM(TMP.НачОст) As НачОст,
SUM(TMP.Приход) As Приход,
SUM(TMP.Расход) As Расход,
SUM(TMP.НачОст + TMP.Приход - TMP.Расход) As КонОст
FROM
(
SELECT
TabRegOst.SP20 As Товар,
TabRegOst.SP21 As НачОст,
0 As Приход,
0 As Расход
FROM
RG13 As TabRegOst
WHERE
TabRegOst.PERIOD = @PERIODR
UNION ALL
SELECT
TabReg.SP20 As Товар,
0 As НачОст,
(TabReg.SP21*((DEBKRED+1)%2)) As Приход,
(TabReg.SP21*DEBKRED) As Расход
FROM
RA13 As TabReg
INNER JOIN _1SJOURN As TabJ
ON (TabReg.IDDOC = TabJ.IDDOC)
WHERE
TabJ.DATE_TIME_IDDOC >= @PERIODN
AND TabJ.DATE_TIME_IDDOC < @POZDOK
) As TMP
GROUP BY
TMP.Товар
ORDER BY
TMP.Товар

где @PERIODR – переменная типа DateTime, условие по которой служит для получения результата запроса на период, меньший на 1 от рассчитываемого. В нашем случае для получения остатка на 20.02.2006 этой переменной необходимо присвоить значения начала Января, т.е. «01.01.2006» (так как в случае условия на 01.02.2006 получим остаток или на конец февраля, или на точку актуальности):
SET @PERIODR = CONVERT(DateTime, '20060101',112)

где @PERIODN – переменная типа Char(8), условие по которой служит для получения результата в запросе на определенный период. Для нашего примера @PERIODN = «20060201».
SET @PERIODR = '20060201'

где @POZDOK – переменная типа char(23), позиция документа (получаемая методом ПолучитьПозицию()).

Следует отметить, что поле DATE_TIME_IDDOC может быть в таблице движений регистра (в случае установленного флага «Быстрая обработка движения» при конфигурировании настроек регистра), в этом случае связь с таблицей журналов (_1SJOURN) необязательна, и условие:
AND TabJ.DATE_TIME_IDDOC < @POZDOK

Может выглядеть вот так:
AND TabReg.DATE_TIME_IDDOC < @POZDOK

И строка:
INNER JOIN _1SJOURN As TabJ ON (TabReg.IDDOC = TabJ.IDDOC)

не нужна.

Т.е. Запрос будет выглядеть так:
SELECT
TMP.Товар As Товар,
SUM(TMP.НачОст) As НачОст,
SUM(TMP.Приход) As Приход,
SUM(TMP.Расход) As Расход,
SUM(TMP.НачОст + TMP.Приход - TMP.Расход) As КонОст
FROM
(
SELECT
TabRegOst.SP20 As Товар,
TabRegOst.SP21 As НачОст,
0 As Приход,
0 As Расход
FROM
RG13 As TabRegOst
WHERE
TabRegOst.PERIOD = @PERIODR
UNION ALL
SELECT
TabReg.SP20 As Товар,
0 As НачОст,
(TabReg.SP21*((DEBKRED+1)%2)) As Приход,
(TabReg.SP21*DEBKRED) As Расход
FROM
RA13 As TabReg
WHERE
TabReg.DATE_TIME_IDDOC >= @PERIODN
AND TabReg.DATE_TIME_IDDOC < @POZDOK
) As TMP
GROUP BY
TMP.Товар
ORDER BY
TMP.Товар
3.4.2.6. Пример 5

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

Приведенный в этом примере код составлен по таблицам конфигурации ПУБ для Украины. Запрос получает остатки и обороты в разрезе продукции за период с "15.01.2006" по "15.02.2006", при условии что период хранения остатков в регистрах остатков - Месяц.
-- Объявление переменных
Declare @PERIODR DateTime
Declare @PERIODN Char(8)
Declare @PERIODK Char(8)
Declare @PERIODN2 Char(8)
Declare @PERIODK2 Char(8)
-- присвоение переменным значений
SET @PERIODR = Convert(DateTime,'20051201',112)
SET @PERIODN = '20051201'
SET @PERIODK = '20060115'
SET @PERIODK2 = '20060215'

-- сам запрос
SELECT
TMP.Продукция AS Продукция,
SUM(TMP.НачОст) As НачОст,
SUM(TMP.Приход) As Приход,
SUM(TMP.Расход) As Расход,
SUM(TMP.НачОст + TMP.Приход - TMP.Расход) As КонОст
FROM (
SELECT
TabRegOst.SP1718 As Продукция,
TabRegOst.SP1722 As НачОст,
0 As Приход,
0 As Расход
FROM
RG1714 As TabRegOst
WHERE
TabRegOst.PERIOD = @PERIODR

UNION ALL
SELECT
TabReg.SP1718 As Товар,
(TabReg.SP1722*((DEBKRED+1)%2))- (TabReg.SP1722*DEBKRED) As НачОст,
0 As Приход,
0 As Расход
FROM
RA1714 As TabReg
INNER JOIN _1SJOURN As TabJ
ON (TabReg.IDDOC = TabJ.IDDOC)
WHERE
TabJ.DATE_TIME_IDDOC >= @PERIODN
AND TabJ.DATE_TIME_IDDOC < @PERIODK

UNION ALL
SELECT
TabReg2.SP1718 As Товар,
0 As НачОст,
(TabReg2.SP1722*((DEBKRED+1)%2)) As Приход,
(TabReg2.SP1722*DEBKRED) As Расход
FROM
RA1714 As TabReg2
INNER JOIN _1SJOURN As TabJ
ON (TabReg2.IDDOC = TabJ.IDDOC)
WHERE
TabJ.DATE_TIME_IDDOC >= @PERIODK
AND TabJ.DATE_TIME_IDDOC < @PERIODK2
) AS TMP
GROUP BY
TMP.Продукция
3.4.2.7. Пример 6

Пересчет итогов по одному из регистров остатков.

Данный код приведен как пример работы с регистрами.
-- Создадим процедуры по конвертации 10-36, 30-10
-- Проверим есть ли такие процедуры
--IF EXISTS(SELECT name FROM sysobjects WHERE name = 'Convert10To36') DROP Procedure 'Convert10To36'

CREATE PROCEDURE [Convert10To36] @Deci INT, @Res36 CHAR(9) OUTPUT AS
SET NOCOUNT ON
DECLARE @j INT
DECLARE @Arr36 CHAR(36)
SELECT @Arr36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
SELECT @Res36 = ''
SELECT @j = LOG(@Deci)/LOG(36) +1
while @j>0
begin
SELECT @Res36 = LTRIM(RTRIM(@Res36)) + SUBSTRING(@Arr36, @Deci/POWER(36,@j-1) +1 ,1)
SELECT @Deci = @Deci%POWER(36,@j-1)
SELECT @j =@j-1
end
GO
--IF EXISTS(SELECT name FROM sysobjects WHERE name = 'Convert36To10') DROP Procedure 'Convert36To10'
CREATE PROCEDURE [Convert36To10] @Res36 CHAR(9), @Deci INT OUTPUT AS
SET NOCOUNT ON
DECLARE @j INT
DECLARE @Arr36 CHAR(36)
SELECT @Arr36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
SELECT @Deci = 0
SELECT @j = 1
while @j <= LEN(LTRIM(RTRIM(@Res36)))
begin
if @j <> 1
SELECT @Deci = @Deci*36
SELECT @Deci = @Deci + CHARINDEX(SUBSTRING(LTRIM(RTRIM(@Res36)), @j,1),@Arr36) -1
SELECT @j = @j+1
End
GO

DECLARE @DATE_POSL datetime
DECLARE @PERIOD_POSL datetime
DECLARE @TIME_POSL int
DECLARE @IDDOC_POSL char(9)
DECLARE @POZ_POSL char(23)
DECLARE @POZ_POSLMAX char(23)
DECLARE @DOC_POSL char(23)
DECLARE @Res36 char(9)
--SELECT @DATE_POSL = Convert(Varchar(23),(SELECT MAX(CURDATE) FROM _1SSYSTEM),112)
-- Получаем дату ТА, время ТА в десятичном формате от начала суток*10000, ID
-- документа последовательности
SELECT @DATE_POSL = MAX(CURDATE),
@TIME_POSL = MAX(CURTIME),
@IDDOC_POSL = MAX(EVENTIDTA)
FROM _1SSYSTEM

IF @TIME_POSL <> 0
-- Конвертируем время в 36 ричный вид
EXEC Convert10To36 @TIME_POSL, @Res36=@POZ_POSL OUTPUT
ELSE
SELECT @POZ_POSL= ''
-- Если ТА установлена не на документ - тогда время и ID документа будут равны 0
IF @IDDOC_POSL = ' 0 ' SELECT @IDDOC_POSL= ''

-- Получаем позицию ТА
SELECT @POZ_POSL = Convert(Varchar(23),@DATE_POSL,112)+LTRIM(RTRIM(@POZ_POSL))+@IDDOC_POSL

-- Получаем документ ТА, если такой имеется (если ТА находится на документе)
SELECT @DOC_POSL = (SELECT DATE_TIME_IDDOC FROM _1SJOURN WHERE DATE_TIME_IDDOC = @POZ_POSL)
-- Получаем начало месяца даты ТА
SELECT @PERIOD_POSL = DATEADD(dd,1-DATEPART(dd,@DATE_POSL),@DATE_POSL)
--SELECT @DATE_POSL,@TIME_POSL,@POZ_POSL,@DOC_POSL,@PERIOD_POSL

-- Заполняем временную таблицу периодами пересчета
SELECT
DISTINCT RG8918.PERIOD As PERIOD,
Convert(Varchar(8),RG8918.PERIOD,112) AS PERIODS,
(CASE WHEN RG8918.PERIOD <> @PERIOD_POSL THEN Convert(Varchar(8),DATEADD(mm,1,RG8918.PERIOD),112)
ELSE @POZ_POSL END) As PERIODPO INTO ##TabPeriod FROM RG8918

GO
--SELECT * FROM ##TabPeriod ORDER BY PERIODS
-- Получаем курсор для обхода каждой строки временной таблицы ##TabPeriod
DECLARE MyCur cursor for
SELECT PERIOD, PERIODS, PERIODPO FROM ##TabPeriod ORDER BY PERIOD

OPEN MyCur
-- переменные для периодов
DECLARE @PERIOD datetime
DECLARE @PERIODPRED datetime
DECLARE @PERIODS varchar(23)
DECLARE @PERIODPO varchar(23)
-- все делаем в транзакции
-- обходим в цикле по каждой строке таблицы (по каждому периоду)
FETCH NEXT FROM MyCur INTO @PERIOD, @PERIODS, @PERIODPO

WHILE @@FETCH_STATUS = 0
BEGIN
-- Получим дату предыдущего периода (месяца)
SELECT @PERIODPRED = DATEADD(mm,-1,@PERIOD)

PRINT convert(Varchar(8),@PERIODPRED,112)+' # '+@PERIODS+' # '+@PERIODPO
--SET HACT_ABORT ON
BEGIN TRANSACTION
-- очищаем текущий период
DELETE FROM RG8918
WHERE PERIOD = @PERIOD
-- заполняем текущий период по пересчитанным данным

INSERT RG8918
SELECT @PERIOD,
T1.SP8908,
T1.SP8909,
T1.SP8910,
T1.SP8911,
T1.SP8912,
T1.SP8913,
T1.SP8914,
T1.SP8915,
'',
T1.SP11200,
SUM(T1.SP8916),
0,
0,
0,
0

FROM
(
SELECT TabRegOst.SP8908 As SP8908,
TabRegOst.SP8909 As SP8909,
TabRegOst.SP8910 As SP8910,
TabRegOst.SP8911 As SP8911,
TabRegOst.SP8912 As SP8912,
TabRegOst.SP8913 As SP8913,
TabRegOst.SP8914 As SP8914,
TabRegOst.SP8915 As SP8915,
TabRegOst.SP11200 As SP11200,
SUM(TabRegOst.SP8916) As SP8916
FROM RG8918 As TabRegOst (HOLDLOCK)
WHERE TabRegOst.PERIOD = @PERIODPRED
GROUP BY
TabRegOst.SP8908,
TabRegOst.SP8909,
TabRegOst.SP8910,
TabRegOst.SP8911,
TabRegOst.SP8912,
TabRegOst.SP8913,
TabRegOst.SP8914,
TabRegOst.SP8915,
TabRegOst.SP11200
UNION ALL
SELECT TabRegOb.SP8908 As SP8908,
TabRegOb.SP8909 As SP8909,
TabRegOb.SP8910 As SP8910,
TabRegOb.SP8911 As SP8911,
TabRegOb.SP8912 As SP8912,
TabRegOb.SP8913 As SP8913,
TabRegOb.SP8914 As SP8914,
TabRegOb.SP8915 As SP8915,
TabRegOb.SP11200 As SP11200,
SUM(TabRegOb.SP8916*(1-TabRegOb.DEBKRED*2)) As SP8916
FROM RA8918 AS TabRegOb (HOLDLOCK)
INNER JOIN _1SJOURN As TabJourn ON (TabRegOb.IDDOC = TabJourn.IDDOC)
WHERE TabJourn.DATE_TIME_IDDOC BETWEEN @PERIODS AND @PERIODPO

GROUP BY
TabRegOb.SP8908,
TabRegOb.SP8909,
TabRegOb.SP8910,
TabRegOb.SP8911,
TabRegOb.SP8912,
TabRegOb.SP8913,
TabRegOb.SP8914,
TabRegOb.SP8915,
TabRegOb.SP11200
) AS T1
GROUP BY
T1.SP8908,
T1.SP8909,
T1.SP8910,
T1.SP8911,
T1.SP8912,
T1.SP8913,
T1.SP8914,
T1.SP8915,
T1.SP11200
HAVING SUM(T1.SP8916) <> 0

COMMIT TRANSACTION

FETCH NEXT FROM MyCur INTO @PERIOD, @PERIODS, @PERIODPO
END
CLOSE MyCur
DEALLOCATE MyCur

SELECT * FROM ##TabPeriod ORDER BY PERIODS
DROP TABLE ##TabPeriod

Перейти на главную страничку сайта (список статей, файлы для скачивания)

© 2005 http://www.script-coding.info Все материалы сайта охраняются законом об авторском праве. Частичная или полная перепечатка материалов сайта без разрешения владельца сайта строго запрещена!