Оптимизация конфигураций на платформе 1C SQL
На главную
Обратная связь
Карта сайта
Оптимизация конфигураций на платформе 1C SQL
ГлавнаяМатериалыСтатьиРазработка
  • Деятельность

  • Материалы

  •      › Скачать
         › Статьи
         › FAQ
         › Новости
  • Контакты

  • Форум



  • e-mail:
    icq: 169296011
    © 2003-2010 Шемякин Павел





    Разработка

    Опубликована: 01.07.2002, отредактирована: 26.03.2003 Автор: toypaul

    Периодические реквизиты: как есть, как может быть

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

    Итак, как я уже писал в одной из своих статей периодические реквизиты хранятся в таблице _1sconst. Рассмотрим ее структуру:

    ROW_ID

    I

    0

    Автоматически изменяемый счетчик строк

    OBJID*

    C

    9

    Идентификатор объекта справочника. Ссылка на элемент справочника

    ID

    I

    0

    Идентификатор поля справочника. Идентификатор поля как объекта метаданных

    DATE

    D

    0

    Дата периодического реквизита

    VALUE

    V

    255

    Значение. Имеют смысл только первые 23 символа. В случае если реквизит определенного типа, но неопределенного вида то 13 символов. Если реквизит имеет определенный тип и вид, то 9 символов.

    DOCID

    C

    9

    Идентификатор документа, который установил данное значение. Заполняется только при использовании метода УстановитьРеквизитСправочника в модуле документа. При этом реквизит DATE равняется дате документа

    TIME

    I

    0

    Время документа, установившего значение

    ACTNO

    I

    0

    Номер движения. Похоже, что все движения в модуле имеют сквозную нумерацию (в том числе по регистрам и наверно по проводкам)

    LINENO_

    S

    0

    Номер строки документа, к которой производилась привязка методом документа ПривязатьСтроку

    TVALUE

    C

    3

    Равен ‘1 ’ если реквизит имеет неопределенный тип

    * — данное поле имеет значение ‘ 0 ’, если данная строка относится не к периодическому реквизиту, а к константе.

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

    1)      Шаблон для извлечения одного периодического реквизита на определенную дату

    SELECT value
    FROM _1sconst h
    WHERE
    h.objid = <%obj_id%> AND h.id = <%field_id%> AND
    h.date =
    (SELECT (MAX(hh.date))
    FROM _1SCONST hh
    WHERE (h.objid = hh.objid) AND (hh.id = h.id) AND hh.date <= <%date%>
    

    Здесь <%obj_id%> — идентификатор элемента справочника, <%field_id%> — идентификатор периодического реквизита (идентификатор метаданных), <%date%> — дата получения периодического реквизита. Если периодический реквизит имеет числовое значение, то можно сразу преобразовать его в число методами cast или convert.

    2)      Шаблон для извлечения нескольких периодических реквизитов

    Здесь уже нет простого решения, так как получить один периодический реквизит можно только отдельной выборкой. Я покажу вам два варианта. Возможно они не самые лучшие. В свое время на «Территории 1С» я пробовал с помощью ее обитателей найти более красивое (быстрое и простое) решение. Один из вариантов был очень неплох, но к сожалению он мною утерян. Возможно у вас будет свое решение — присылайте и я добавлю его в эту статью. Итак собственно сами решения.

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

    SELECT
    CASE WHEN r1.id IS NULL THEN r2.id ELSE r1.id END,
    r1.value,r2.value FROM
    (
    SELECT value
    FROM _1sconst h,<%sc%> s
    WHERE
    h.objid = s.id AND h.id = <%field_id1%> AND
    h.date =
    (SELECT (MAX(hh.date))
    FROM _1SCONST hh
    WHERE (h.objid = hh.objid) AND (hh.id = h.id) AND hh.date <= <%date%>
    ) r1 FULL JOIN
    (
    SELECT value
    FROM _1sconst h,<%sc%> s
    WHERE
    h.objid = s.id AND h.id = <%field_id2%> AND
    h.date =
    (SELECT (MAX(hh.date))
    FROM _1SCONST hh
    WHERE (h.objid = hh.objid) AND (hh.id = h.id) AND hh.date <= <%date%>
    ) r2 ON r1.id = r2.id

    Здесь <%sc%> — выбранный справочник.

    Второе решение — с помощью подзапросов.

    SELECT s.ID,
    (SELECT value
    FROM _1sconst h
    WHERE
    h.objid = s.id AND h.id = <%field_id1%> AND
    h.date =
    (SELECT (MAX(hh.date))
    FROM _1SCONST hh
    WHERE (h.objid = hh.objid) AND (hh.id = h.id) AND hh.date <= <%date%>
    )),
    (SELECT value
    FROM _1sconst h
    WHERE
    h.objid = s.id AND h.id = <%field_id2%> AND
    h.date =
    (SELECT (MAX(hh.date))
    FROM _1SCONST hh
    WHERE (h.objid = hh.objid) AND (hh.id = h.id) AND hh.date <= <%date%>
    ))
    FROM
    <%sc%> s

    3)      Получение периодического реквизита в зависимости от даты из другой таблицы

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

    SELECT CASE WHEN Tot.id IS NULL THEN Tot2.id ELSE Tot.id END, Tot.seb,Tot2.seb FROM
    (SELECT s.ID,Seb = (SUM(n.sp2994*(CONVERT(numeric(14,2),h1.value))))
    FROM
    dt2988 n,
    _1SJOURN j,
    sc656 s LEFT JOIN _1sconst h1 ON s.id = h1.objid AND h1.id = 658
    WHERE
    s.ID = n.sp2993 AND
    j.IDDOC = n.IDDOC AND
    LEFT(DATE_TIME_IDDOC,8) >= '20000810' AND LEFT(DATE_TIME_IDDOC,8) <= '20000820' AND
    h1.date =
    (SELECT (MAX(hh.date))
    FROM _1SCONST hh
    WHERE (h1.objid = hh.objid) AND (h1.id = hh.id) AND (CONVERT(char(8),hh.date,112) <= LEFT(j.DATE_TIME_IDDOC,8))
    )
    GROUP BY s.id
    ) Tot FULL JOIN
    (SELECT s.ID,Seb = (SUM(n.sp2994*(CONVERT(numeric(14,2),h1.value))))
    FROM
    dt2988 n,
    _1SJOURN j,
    sc656 s LEFT JOIN _1sconst h1 ON s.id = h1.objid AND h1.id = 659
    WHERE
    s.ID = n.sp2993 AND
    j.IDDOC = n.IDDOC AND
    LEFT(DATE_TIME_IDDOC,8) >= '20000810' AND LEFT(DATE_TIME_IDDOC,8) <= '20000820' AND
    h1.date =
    (SELECT (MAX(hh.date))
    FROM _1SCONST hh
    WHERE (h1.objid = hh.objid) AND (h1.id = hh.id) AND (CONVERT(char(8),hh.date,112) <= LEFT(j.DATE_TIME_IDDOC,8))
    )
    GROUP BY s.id
    ) Tot2 ON Tot.id = Tot2.id
    ORDER BY TOt.id

    В примере используется способ с помощью соединения двух запросов.

    На этом закончим первую часть статьи и приступим ко второй. Собственно именно эта часть, как мне кажется будет более важной.

    На мой взгляд организация периодических реквизитов в системе 1С не идеальная (а нужно к этому стремиться). Почему я так считаю? Во-первых, все периодические реквизиты хранятся в одной таблице. Это очень плохо если учесть систему блокировок 1С, когда при записи блокируется вся таблица. В этом случае, если периодические реквизиты у вас используются достаточно активно, то есть большая вероятность, что эта таблица станет «узким местом» в вашей системе из-за постоянных ожиданий на запись (иногда и на чтение) данных. Во-вторых по этой же причине размер таблицы увеличивается быстрее чем могло бы быть. Из-за этого снижается скорость доступа к ней. В-третьих по этой же причине данные в таблице хранятся в ненормализованном виде, причем используются достаточно неэффективно (используется только максимум 10%, а обычно всего 5% размера поля отведенного под значение периодического реквизита).

    Поэтому лично я в разрабатываемых системах стараюсь во-первых вообще не использовать периодических реквизитов. Обычно историю можно получить по документам. А для чего еще могут служить периодические реквизиты как не для хранения истории? На текущий момент практически всегда бывает необходимо только одно значение реквизита. Во-вторых там, где действительно нужна история, точнее там, где нужно устанавливать некоторые реквизиты на будущие даты для дальнейшего использования, я использую подчиненные справочники. Можно использовать два варианта: хранение реквизитов в одной таблице, хранение реквизитов в разных таблицах. Я обычно использую первый вариант. Во-первых получается удобнее иметь доступ сразу ко всем реквизитам на определенную дату, во-вторых не нужно заводить несколько таблиц. Минусы: увеличивается размер таблицы, необходимо предусмотреть при смене одного из реквизитов, дублирование остальных. Первый минус не так страшен, так как обычно лишние данные из таблицы удаляются, второй тоже не страшен, так как обычно это возлагается на пользователя (история выводится в модальном окне подчиненного справочника, где сам пользователь уже манипулирует ей на свое усмотрение).

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

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

    SELECT [h.Себестоимость]
    FROM [Справочник.Сбт_ИсторияПрод] h
    WHERE [h.Владелец] = [@ВыбТовар] AND
    [h.ДатаРекв] =
    (
    SELECT (MAX([h.ДатаРекв]))
    FROM [Справочник.Сбт_ИсторияПрод] hh
    WHERE [h.Владелец] = [hh.Владелец] AND [h.ДатаРекв] <= [@ВыбДата]
    )

     наверх