Переключение на Главную Страницу Страницы: [1] 2  ОтправитьПечать
Очень популярная тема (более 25 ответов) FAQ по прямым запросам к данным 1С (число прочтений - 99012 )
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
FAQ по прямым запросам к данным 1С
19. Мая 2006 :: 11:33
Печать  
Ветка перенесена с итланда.
Что-то подверглось небольшому редактированию. Флуд убран.
Что-то потерялось (надеюсь, небезвозвратно), ибо ответ зачастую давался в виде ссылки на ветку. Печаль
« Последняя редакция: 08. Ноября 2006 :: 11:42 - fez »  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #1 - 19. Мая 2006 :: 11:42
Печать  
1)С чего начинать и самый простой пример

Я делаю так:
В глобальном модуле задаем переменную
Перем RecordSet Экспорт;

Инициализируем её (для SQL версии)

[1Cv7]
Процедура ПриНачалеРаботыСистемы()
DataBase = СоздатьОбъект("ODBCDatabase");
DataBase.Attach1C();

RecordSet = СоздатьОбъект("ODBCRecordSet");
RecordSet.SetDatabase(DataBase);
...
КонецПроцедуры
[/1Cv7]

Пишем Функцию

[1Cv7]
Функция глПолучитьВыборку(ODBCRecordset = "",Текст, Отладка=0, Очищать = 1, ТЗ2 = "") Экспорт
   Если ПустоеЗначение(ODBCRecordset) = 1 Тогда
      ODBCRecordset = Recordset;
   КонецЕсли;

   ODBCRecordset.Отладка(Отладка);
   Если ODBCRecordset.Открыть(Текст) = 0 Тогда
      Ошибка = ODBCRecordset.ПолучитьОписаниеОшибки();
      Если ПустоеЗначение(Ошибка) = 0 Тогда
          Сообщить(Ошибка);
      КонецЕсли;
   КонецЕсли;

   Если ПустоеЗначение(ТЗ2) = 1 Тогда
     ТЗ2 = СоздатьОбъект("ТаблицаЗначений");
   КонецЕсли;

  ODBCRecordset.ПолучитьРезультатыВ_ТЗ(ТЗ2, Очищать);
  ODBCRecordset.Закрыть();
 
  Возврат ТЗ2;
КонецФункции
[/1Cv7]


Далее простой пример

[1Cv7]
Текст = "
|SELECT * FROM $Справочник.Номенклатура";

ТЗ = глПолучитьВыборку(, Текст);
ТЗ.ВыбратьСтроку();
[/1Cv7]
  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #2 - 19. Мая 2006 :: 11:43
Печать  
2)Типизация к типам 1С

Текст = "
|SELECT
| Спр.ID as [Элемент $Справочник.Номенклатура]
|FROM
| $Справочник.Номенклатура as Спр
|";

Правило: Типизируем к тому виду который хранится с базе, например
$Справочник.Номенклатура
$Справочник
$Документ.РасходнаяНакладная
$Документ
$Неопределенный
дату, число, строку типизировать не надо

Еще пример

Текст = "
|SELECT
| $Док.Контрагент [Контрагент $Справочник.Контрагенты]
|FROM
| $Документ.ПКО as Док
|";

Соответственно as можно не писать.
  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #3 - 19. Мая 2006 :: 11:44
Печать  
Получение объекта документ неопределенного вида

Для это используем хранимую функцию

Код
Выбрать все
Текст = "create FUNCTION  sp_tohex(@val int, @len1 int) RETURNS varchar(9)
|AS
|begin
|declare @v int;
|declare @tval varchar(9);
|set @v = @val;
|set @tval = '';
|
|while (@v > 0)
|begin
|   set @tval = SUBSTRING('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',1+@v%36,1)+@tval;
|   set @v = @v/36;
|end;
|
|if @tval='' set @tval='0';
|
|while (len(@tval) < @len1)
|begin
|set @tval = ' '+@tval;
|end;
|RETURN(@tval);
|end
|";
RecordSet.Открыть(Текст);
 



Которая будет преобразовывать вид документа из числа в строку

Пример


Код
Выбрать все
Текст = "
|SELECT
|     dbo.sp_tohex(Жур.IDDocDef, 4) + Жур.IDDoc [Док $Документ]
|FROM
|    _1SJourn Жур
|";
 



Получение Документа из Регистра
1) Если в регистре стоит галочка "Быстрая обработка движений" или
есть отбор движений по какому-нибудь измерению или реквизиту, то
в таблице движений регистра есть поле "IDDocDef"

Код
Выбрать все
Текст = "
|SELECT
|     dbo.sp_tohex(Рег.IDDocDef, 4) + Рег.IDDoc [Док $Документ]
|FROM
|    $Регистр.Партии Рег
|";
 



2) В остальных случаях

Код
Выбрать все
Текст = "
|SELECT
|     dbo.sp_tohex(Жур.IDDocDef, 4) + Рег.IDDoc [Док $Документ]
|FROM
|    $Регистр.Партии Рег
|LEFT JOIN _1SJourn Жур ON Жур.IDDoc = Рег.IDDoc
|";
 


  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #4 - 19. Мая 2006 :: 11:47
Печать  
Получение наименования справочника неопр. вида
Конечно в общем случае нельзя. Например есть колонка МПЗ в которой может быть
справочник Номенклатура или Материалы


SQL      
Код
Выбрать все
SELECT
...
COALESCE(СпрН.Descr, СпрМ.Descr) Наименование

LEFT JOIN $Справочник.Номенклатура СпрН ON $ВидСправочника36.Номенклатура + СпрН.ID = $Рег.МПЗ
LEFT JOIN $Справочник.Материалы СпрН ON $ВидСправочника36.Материалы + СпрМ.ID = $Рег.МПЗ
...
 

     
  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #5 - 19. Мая 2006 :: 11:47
Печать  
Выборка документов разного вида

SQL      
Код
Выбрать все
SELECT
$ВидДокумента36.ПКО + ДокП.IDDoc [Док $Документ]
FROM $Документ.ПКО ДокП

UNION ALL

SELECT
$ВидДокумента36.РКО + ДокР.IDDoc [Док $Документ]
FROM $Документ.РКО ДокР
 

  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #6 - 19. Мая 2006 :: 11:49
Печать  
Получение остатков на ТА


1Cv7      
Код
Выбрать все
   ТекстЗапроса = "
   |Select
   |    Товар [Товар $Справочник.Товары],
   |    Склад [Склад $Справочник.Склады],
   |    ОстатокТовараОстаток ОстатокТовара
   |FROM
   |    $РегистрОстатки.ОстаткиТовара(
   |	,
   |	,
   |	Товар in (Select val from #ВыбТовар)
   |				   And
   |	Склад in (Select val from #ВыбСклад),
   |	(Товар, Склад),
   |	ОстатокТовара
   |	) остатки
   |";
   Запрос = СоздатьОбъект("ODBCRecordSet");
   Запрос.УложитьСписокОбъектов(ВыбСклад,"#ВыбСклад","Склады");
   Запрос.УложитьСписокОбъектов(ВыбТовар,"#ВыбТовар","Товары");
   ТЗ = Запрос.ВыполнитьИнструкцию(ТекстЗапроса);
   ТЗ.ВыбратьСтроку();
 

     

  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #7 - 19. Мая 2006 :: 11:50
Печать  
Отбор по пустому значению:

1Cv7      
Код
Выбрать все
Мета=СоздатьОбъект("MetaDataWork");
Элемент = ПолучитьПустоеЗначение("Справочник.Фирмы");
СтрЭлемент=Мета.ЗначениеВСтрокуБД(Элемент); //Именно так в действительности выглядит пустое значение в базе данных

Запрос.УстановитьТекстовыйПараметр("ПустаяФирма",СтрЭлемент);
 



другой вариант предложенный trad'ом такой

1Cv7      

Код
Выбрать все
ник.Фирмы"));
 

     

как ни странно тоже работает вроде...

Начиная с версии 1.8.1.6 появились значения $ПустойИД, $ПустойИД13 -
сответственно пустое значение определенного и неопределенного вида
Пример:
Регистр партии: Фирма - Тип: Справочник.Фирмы, МПЗ - Тип: Справочник

Код
Выбрать все
SELECT
Рег.КоличествоОстаток
FROM $РегистрОстатки.Партии(:ВыбДата~, , Фирма = $ПустойИД AND МПЗ = $ПустойИД13,,)
 

  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #8 - 19. Мая 2006 :: 11:51
Печать  
Группировка результата

1Cv7      
Код
Выбрать все
ТекстЗапроса="|SELECT
   | $Регистр.ОстаткиТоваров.Товар as [Товар $Справочник.Номенклатура],
   | $Регистр.ОстаткиТоваров.Склад as [Склад $Справочник.МестаХранения],
   | SUM($Регистр.ОстаткиТоваров.ОстатокТовара) as Остаток,
   | GROUPING ($Регистр.ОстаткиТоваров.Товар) as Группа1,
   | GROUPING ($Регистр.ОстаткиТоваров.Склад) as Группа2
   |FROM $Регистр.ОстаткиТоваров
   |GROUP BY $Регистр.ОстаткиТоваров.Товар,$Регистр.ОстаткиТоваров.Склад WITH ROLLUP
   |ORDER BY $Регистр.ОстаткиТоваров.Товар,$Регистр.ОстаткиТоваров.Склад,Группа1 DESC,Группа2 DESC
   |";
 


     
На что стоит обратить внимание:
WITH ROLLUP это ключевое слово дает понять что надо добавлять пустые строки с итогами, что очень напоминает нам выгрузку в тз запроса 1С... но строки с итогами добавляются внизу, что нам неудобно.... поэтому добавим две колонки GROUPING... колонка эта ставит единицу если строчка добавлена ROLLUP и 0 если это просто строчка таблицы.... ну и в конце просто отсортируем по товарам, складам, и этим двум колонкам, и таким образом получим привычную нашему взгляду тз запроса 1С.
  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #9 - 19. Мая 2006 :: 11:52
Печать  
Q. Расскажите, каким образом 1С определяет вид данных, находящихся в поле таблицы?

A. У реквизита есть ТИП - это определяет ВСЕ!

Примеры: тип_реквизита - тип_sql - вариант типизации:
Документ.РасходнаяНакладная - char(9) - $Документ.РасходнаяНакладная
Документ - char(13) - $Документ
Неопределенный - char(23) - $Неопределенный

Когда имеем не реквизит, а только идентификатор документа в формате char(9) (скажем из _1sjourn), для того чтобы выполнить типизацию, нам необходимо дополнительное поле с видом документа (не смотря на то, что идентификаторы документов уникальны во всей базе). Если типизирующее имя $Документ и тип поля char(9), то ODBCRecordset ищет дополнительное типизирующее поле, он его определяет как поле с именем типизируемого поля с суффиксом "_вид":
select
ra.iddoc as [ДокументДвижения $Документ], --char(9)
j.iddocdef as ДокументДвижения_вид, --int
  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #10 - 19. Мая 2006 :: 11:54
Печать  
Q. Где базе данных 1С хранятся отборы журналов???
A. В таблице _1SCRDOC для SQL и в файле 1scrdoc.dbf для DBF. Там же находятся подчинённые документы и дополнительные журналы.

Q. Приведите пример использования для формирования реестра документов с разными фильтрами (можно с этими графами - короче как эффективнее, отборы разные - и по шапкам и по табличным частям)
A1. Общий реквизит с отбором.

SQL
Код
Выбрать все
SELECT IDDOC [Документ $Документ], IDDOCDEF Документ_вид
FROM _1SJOURN WHERE ($ОбщийРеквизит.Фирма = :ВыбФирма) AND (_1SJOURN.DATE_TIME_IDDOC Between :НачДата AND :КонДата ~)
 

     

A2. Графа отбора.

SQL      
Код
Выбрать все
SELECT IDDOC [Документ $Документ], IDDOCDEF Документ_вид
FROM _1SJOURN INNER JOIN _1SCRDOC ON _1SJOURN.IDDOC = _1SCRDOC.CHILDID
WHERE (_1SCRDOC.MDID = $ГрафаОтбора.Контрагент) AND (_1SCRDOC.PARENTVAL = :ВыбКонтрагент *) AND (_1SCRDOC.CHILD_DATE_TIME_IDDOC Between :НачДата AND :КонДата ~)
 



A3. Подчинённые документы.

SQL      
Код
Выбрать все
SELECT IDDOC [Документ $Документ], IDDOCDEF Документ_вид
FROM _1SJOURN INNER JOIN _1SCRDOC ON _1SJOURN.IDDOC = _1SCRDOC.CHILDID
WHERE (_1SCRDOC.MDID = 0) AND (_1SCRDOC.PARENTVAL = :ДокРодитель *) AND (_1SCRDOC.CHILD_DATE_TIME_IDDOC Between :НачДата AND :КонДата ~)
 



Q. Как использовать графу отбора по прямым запросам в регистры (обороты)?
A. Смотри A2 для предыдущего вопроса.
  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #11 - 19. Мая 2006 :: 11:55
Печать  
В MSSQL 2000 есть баг связанный с времменными таблицами. Проявляетя в виде замедления проведения документов (1 месяц - 1сек, 2 месяц - 2 сек на документ и т.д.). Ошибка лечится переподключением к БД (вышли и зашли из 1С).
В обход этой проблемы в 1С++ был введен метод ODBCDatabase.ReconnectNaive().

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

Код
Выбрать все
База=СоздатьОбъект("ODBCDatabase");

Запрос=СоздатьОбъект("ODBCRecordset");
Запрос.УстановитьТекстовыйПараметр("НачДата",НачДата);
Запрос.УстановитьТекстовыйПараметр("КонДата",КонДата);

ТЗ=Запрос.ВыполнитьИнструкцию("
|select
| iddoc [Документ $Документ],
| iddocdef Документ_вид
|from _1sjourn j (nolock)
|where j.date_time_iddoc between :НачДата and :КонДата~
| and closed = 1
|order by j.date_time_iddoc
|");

ДоковВСеансе=5;
Сч=ДоковВСеансе;
ДокОбъект=СоздатьОбъект("Документ");

ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку()=1 Цикл

ДокОбъект.НайтиДокумент(ТЗ.Документ);
Сообщить(ТЗ.Документ);
ДокОбъект.Провести();

Сч=Сч-1;

Если Сч=0 Тогда
 Сообщить("============================ Реконнект ================================");
 База.ReconnectNative();
 Сч=ДоковВСеансе;
КонецЕсли;

КонецЦикла;
 



Во время выполнение метода должны быть закрыты все курсоры, т.е. все формы справочников, документов, журналов, закрыты все открытые переменные: Док = СоздатьОбъект("Документ").
  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #12 - 19. Мая 2006 :: 11:56
Печать  
Вычисление остатков

Немного теории

Каждый регистр состоит из 2х таблиц: таблицы итогов (RG) и таблицы движений (RA). В таблице итогов хранятся остатки на конец каждого месяца и на ТА. Поэтому чтобы получить остатки на середину месяца нужно делать запрос по 2 таблицам с объединением и группировкой
НО этого делать абсолютно не нужно, т.к. виртуальная таблица $РегистрОстатки или $РегистрОстаткиИобороты это и есть нужный правльный и оптимизированный запрос

P.S. Если вдруг возникнет желание посмотреть, что же эта за виртуальная таблица, то можно воспользоваться методом ODBCRecordSet.Отладка(1)
P.P.S. Хоть в таблице итогов и лежат остатки на конец месяца, то период там стоит равный началу месяца.
Цитата:
Просто счет они ведут не прямым расчетом, а обратным (т.е. не ПРИБАВЛЯЮТ движения от начала до тек. момента, а ВЫЧИТАЮТ из конечных остатков движения от тек. момента до конца). При таком методе расчета последние остатки будут получаться быстрее (а остатки на конец периода - и подавно). А поскольку чаще получают именно остатки более "близкие" к концу периода ("близкие" имеется в виду по кол-ву движений, а не непосредственно по времени), такая схема более эффективна. При этом маркировка особого значения не имеет - и так и так ее придется вычислять. А вычислить дату начала периода удобнее, чем дату конца - посмотрите на ХП _1sp_GetBeginOfPeriod и _1sp_GetNextPeriod, и поймете.
  
Наверх
www  
IP записан
 
fez
Forum Administrator
1c++ power user
Отсутствует


I wanted to cry, but the
tears wouldn't come

Сообщений: 2712
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: FAQ по прямым запросам.
Ответ #13 - 19. Мая 2006 :: 11:57
Печать  
Вычисление остатков

Теперь практика.

Пример запроса на 1С и 1С++

Код
Выбрать все
|Товар = Регистр.ОстаткиТоваров.Товар;
|Склад = Регистр.ОстаткиТоваров.Склад;
|ОстатокТовара = Регистр.ОстаткиТоваров.ОстатокТовара;
|Функция ОстатокТовараКонОст = КонОст(ОстатокТовара);
|Группировка Товар;
|Условие(Товар в ВыбТовар);
|Условие(Склад в ВыбСклад);
 

     

и

Код
Выбрать все
ТекстЗапроса = "
  |Select
  |    Товар [Товар $Справочник.Товары],
  |    Склад [Склад $Справочник.Склады],
  |    ОстатокТовараОстаток ОстатокТовара
  |FROM
  |    $РегистрОстатки.ОстаткиТовара(
  |	,
  |	,
  |	Товар in (Select val from #ВыбТовар)
  |				   And
  |	Склад in (Select val from #ВыбСклад),
  |	(Товар, Склад),
  |	ОстатокТовара
  |	) остатки
  |";
  Запрос = СоздатьОбъект("ODBCRecordSet");
  Запрос.УложитьСписокОбъектов(ВыбСклад,"#ВыбСклад","Склады");
  Запрос.УложитьСписокОбъектов(ВыбТовар,"#ВыбТовар","Товары");
  ТЗ = Запрос.ВыполнитьИнструкцию(ТекстЗапроса);
  ТЗ.ВыбратьСтроку();
 



Конечно в этих запросах есть немного отличий, а именно в обработке пустых фильтров. В прямом запросе результат будет пустой
  
Наверх
www  
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: FAQ по прямым запросам.
Ответ #14 - 07. Ноября 2006 :: 09:25
Печать  
Пара ссылок, представляющих интерес в плане особенностей применения:

Расчет остатков на документ и по документ (пост №5 trad)
http://www.1cpp.ru/forum/YaBB.pl?num=1162733649/5#5

Текущие особенности и порядок деинициализации объектов ODBCRecordSet и ODBCDataBase
http://www.1cpp.ru/forum/YaBB.pl?num=1162472281/8#8 (пост №8 Quan)
« Последняя редакция: 13. Ноября 2006 :: 18:56 - kms »  

De quelle planète es-tu?
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: [1] 2 
ОтправитьПечать