powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Проектирование БД [игнор отключен] [закрыт для гостей] / Проектирование базы для последующей работы с ней без использования динамических запросов
25 сообщений из 34, страница 1 из 2
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39001849
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем доброго времени суток! :)

Столкнулся с неразрешимой на сегодняшний момент для меня проблемой. Посоветуйте, пожалуйста, как можно ее решить.

Общие сведения (предыстория):
Необходимо разработать приложения для бухгалтерского учета, в котором должна быть возможность использования бухгалтерских проводок. Все это реализуется на базе MS SQL server 2008.
Приложение при помощи ОДНОГО запроса считывает данные, которые касаются проводок. При помощи другого запроса записывают данные в базу.
Проблема в том, что не получается создать нединамический запрос к базе для считывания данных. Т.е пока есть решение только с использованием динамического запроса, от которого, понятное дело, хочется уйти.

Создал следующую модель базы данных (схема в аттаче):

Схема и краткое описание структуры таблиц:
Account - счета, таблица для хранения счетов бухгалтерского учета.
LedgerAccount - субконто счетов. Здесь есть связь с таблицей Object для того, что бы понимать какой субконто соотносится с какими справочниками, например Субконто Договора" можно сопоставить значениям из справочника(объекта системы) "Договора".
AccountLedgerAccount - Привязка субконто к счетам.
Transaction - бухгалтерские проводки
LedgerAccountValue - значения субконто счетов в определенной проводке.
Object - перечень всех объектов в системе, под объектами понимаем все справочники, журналы, отчеты. Обьект собирается во вьюхе, условно считаем, что это одна таблица.

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

Код: sql
1.
2.
Select tr.* 
from Transaction tr


-- цепляем счета по Дебету и по Кредиту
Код: sql
1.
2.
join Account acD on acD.AccountId = tr.DebAccountId
join Account acC on acC.AccountId = tr.CredAccountId


-- цепляем субконто счетов кредита и дебета
Код: sql
1.
2.
join LedgerAccountValue laD on laD.AccountId = acD.AccountId
join LedgerAccountValue laC on laC.AccountId = acC.AccountId


/* далее нужно выцепить ЗНАЧЕНИЕ субконто. Предполагается, что этим значением является одна из записей из справочников или журналов системы (ссылка на договор, контрагента и т.д.) и хранится это значение в поле ReferenceId проблема в том, что далее в запросе нужно подцепить нужную таблицу, а какую нужно цеплять таблицу(вьюху) зависит от того, какое
именно субконто стоит в данном счете.
Например, если у нас одно из субкон контрагенты тогда нужно цеплять таблицу Contragent:
*/
Код: sql
1.
join Contragent co1 on co1.ContragentId = laD.ReferenceId



--если же субконто договора, тогда нужно цеплять таблицу Dogovor:
Код: sql
1.
join Dogovor dog1 on dog1.DogovorId = laD.ReferenceId



Проблема в том, что в ReferenceId таблицы LedgerAccountValue может быть значение из многих таблиц (объектов). Понимаю, что это неправильно
(подскажите, пожалуйста, как можно это сделать правильно?). Для вывода значений субконто пока вижу только путь с использованием динамического запроса:

Для этого вытаскиваем имя таблицы(вьюхи)
-- цепляем таблицу субконто
Код: sql
1.
join LedgerAccount la1 on la1.LedgerAccountId = laD.LedgerAccountId



и тогда в la1.ObjectId - будет ID объекта системы, по которому я уже смогу выйти на название таблицы
Код: sql
1.
join object ob on ob.ObjectId = la1.ObjectId



ob.ViewName - имя таблицы (вьюхи) , котрую нужно цеплять

и дальше написать финальный
динамический запрос для выемки значение субконто из таблицы.
Хотелось бы реализовать все это таким образом, что бы при добавлении новых
субконто в счета, а так же добавлении новых счетов с субконтами, не было необходимости переписывать хранимые процедуры и менять exe-шник,
и не использовать динамические запросы. (Не слишком ли это многого я хочу ?)

Чувствую, что чем дальше иду, тем больше делаю ошибок в проектировании базы.
Подскажите, пожалуйста, какие есть еще альтернативные варианты для решения моей задачи.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39001865
SERG1257
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
unimilk проблема в том, что далее в запросе нужно подцепить нужную таблицусквозная нумерация id у разных объектов или guid. И джойнь их всех, только в одной будут непустые значения
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002024
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
SERG1257, приветствую! :)

Спасибо за ответ. Боюсь, сейчас вариант с сквозными ID достаточно проблематично реализовать, т.к. сейчас в системе уже существует большое количество объектов(таблиц) с большим количеством внешних связей, и изменять их Id на сквозной это очень большие трудозатраты. Нет ли еще какого-либо пути? может, можно как-то изменить структуру базы данных, относящуюся к проводкам?
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002034
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
SERG1257,

+ в случае добавления нового субконто каждый раз нужно будет править запрос, который начитывает данные, а это тоже крайне нежелательно.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002064
LSV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
это значение в поле ReferenceId проблема в том, что далее в запросе нужно подцепить нужную таблицу, а какую нужно цеплять таблицу(вьюху) зависит от того, какое именно субконто стоит в данном счете. Это не проблема.
делаете ф-цию: параметры ReferenceId, DocTypeID... и в ней извлекаете сколь угодно сложным образом нужные значения.
При появлении новых таблиц достаточно поправить код этой ф-ции. Код приложения останется неизменным.

зы: ф-ции ацки рулят. Код сокращается в разы. Читабельность - во много раз.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002103
Mikle83
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
unimilkНапример, если у нас одно из субкон контрагенты тогда нужно цеплять таблицу Contragent:
*/
Код: sql
1.
join Contragent co1 on co1.ContragentId = laD.ReferenceId


--если же субконто договора, тогда нужно цеплять таблицу Dogovor:
[src]
join Dogovor dog1 on dog1.DogovorId = laD.ReferenceId

Как вы определяете условие "ЕСЛИ". Где у вас хранится признак что именно для конкретной записи "субконто"?
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002138
Кот Матроскин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сквозные ID в принципе необязательны (хотя с ними система будет аккуратнее) - достаточно вместо
Код: sql
1.
join Contragent co1 on co1.ContragentId = laD.ReferenceId


писать
Код: sql
1.
join Contragent co1 on co1.ContragentId = laD.ReferenceId and object.name = 'Контрагенты'


И т.п.


unimilkSERG1257,

+ в случае добавления нового субконто каждый раз нужно будет править запрос, который начитывает данные, а это тоже крайне нежелательно.

Нет - только при добавлении нового типа субконто. Например, решаем "теперь у нас в системе могут быть субконто Контрагенты" - тогда да, надо будет переписывать запрос, чтобы добавить приведенную мной строчку.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002228
так зашёл
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
unimilk,

Если нельзя сделать сквозную нумерацию, то

Если типов субконто не очень много(не более 10ти), то можно обойтись набором left join'ов
Помним, что у оптимизатора сносит крышу на более чем 10ти таблицах. Так что наши джойны можно смело помечать хинтом loop
Так же, если вы уверены, что расположили таблицы в запросе в правильном порядке, следует использовать хинты force order и можно ещё maxdop 1
Как-то так:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
Select tr.*,
  ob.ViewNam as Type,
  coalesce(co1.some_value, dog1.some_value, dog2.some_value, dog3.some_value) some_value
from Transaction tr
  join Account acD on acD.AccountId = tr.DebAccountId
  join Account acC on acC.AccountId = tr.CredAccountId

  join LedgerAccountValue laD on laD.AccountId = acD.AccountId
  join LedgerAccountValue laC on laC.AccountId = acC.AccountId

  join LedgerAccount la1 on la1.LedgerAccountId = laD.LedgerAccountId
  join object ob on ob.ObjectId = la1.ObjectId

  left loop join Contragent co1 on co1.ContragentId = laD.ReferenceId and ob.ViewNam = 'Contragent'
  left loop join Dogovor1 dog1 on dog1.DogovorId = laD.ReferenceId and ob.ViewNam = 'Dogovor1'
  left loop join Dogovor2 dog2 on dog2.DogovorId = laD.ReferenceId and ob.ViewNam = 'Dogovor2'
  left loop join Dogovor3 dog3 on dog3.DogovorId = laD.ReferenceId and ob.ViewNam = 'Dogovor3'
option(force order, maxdop 1)



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

Возможно есть смысл создать индексированную вьюху(type ASC), которая объединяет все таблицы.(uinon all)

А вот если таблиц очень много. Тогда действительно стоит выбрать динамический запрос.
Не надо его сильно пугаться.
Это даст выигрыш в производительности, ну и со временем менять ничего не придётся.
и помним, что это небезопасно, например если ob.ViewNam выдаст несуществующую таблицу и.т.п

А если всё таки есть возможность перенести данные.
То можно создать табличку для субконто, куда внести все существующие поля из всех таблиц с составным индексом (тип, refId) - после переноса данных переписать код и грохнуть таблички. Но надо быть готовым к тому, что статистика по таблице будет никакая и оптимизатор может начать творить чушь.
Вертикальное хранение то ещё зло, поэтому рекомендовать этот вариант не буду.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002652
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
unimilkSERG1257, приветствую! :)

Спасибо за ответ. Боюсь, сейчас вариант с сквозными ID достаточно проблематично реализовать, т.к. сейчас в системе уже существует большое количество объектов(таблиц) с большим количеством внешних связей, и изменять их Id на сквозной это очень большие трудозатраты. Нет ли еще какого-либо пути? может, можно как-то изменить структуру базы данных, относящуюся к проводкам?

Ты совершил кардинальную ошибку при проектировании твоей БД (нарушение доменной целостности), теперь нужны кардинальные усилия для её преодоления, всё логично.

Можно и без сквозного ID.
У тебя есть вид этого субконто.
Делай LEFT JOIN-ы на все возможные таблицы с этим субконто,
и потом CASE-ом выбирай по виду субконто нужный объект из, скажем, 10.
Будет много JOIN-ов, и будет большой длинный CASE, но это всё не страшно для запроса.
Запрос будет статическим.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002653
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
unimilkSERG1257,

+ в случае добавления нового субконто каждый раз нужно будет править запрос, который начитывает данные, а это тоже крайне нежелательно.

Ничего, поправишь...
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002654
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LSVэто значение в поле ReferenceId проблема в том, что далее в запросе нужно подцепить нужную таблицу, а какую нужно цеплять таблицу(вьюху) зависит от того, какое именно субконто стоит в данном счете. Это не проблема.
делаете ф-цию: параметры ReferenceId, DocTypeID... и в ней извлекаете сколь угодно сложным образом нужные значения.
При появлении новых таблиц достаточно поправить код этой ф-ции. Код приложения останется неизменным.

зы: ф-ции ацки рулят. Код сокращается в разы. Читабельность - во много раз.

Функции в запросах ненужны и вредны.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39002663
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот всё правильно написал, а все же...

так зашёлПомним, что у оптимизатора сносит крышу на более чем 10ти таблицах. Так что наши джойны можно смело помечать хинтом loop


С какой стати? Т.е. 10 таблиц -- всё будет ок, а вот если вдруг 11-ую добавим -- сразу слёт крыши ?
У нас тут даже СУБД в обсуждении не зафиксирована, а ты такие вещи говоришь...
Тем более, что все эти 10-20 JOIN-ов будут LEFT OUTER, и оптимизатору не нужно будет думать, какая таблица пойдёт вперёд -- однозначно не эти, потому что они не добавляют селективность запросу. Плюс должны же быть индексы, так что фактически наоборот
запрос обречён на хорошее функционирование при благоприятности остальных условий (что самих транзакций будет не миллион, а 10-100)
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003087
LSV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MasterZivФункции в запросах ненужны и вредны.Мой начальник когда-то тоже так считал. Поэтому у него в коде отчетов каждая управленческая цифра (а их много) извлекалась "заново" т.е. запросом. Сотни точек копипаста.
Отчеты были огромными sql-мегапростынями. Переделка логики документа (иногда надо) могла потребовать переделки десятка таких простыней. Это если про все вспомнишь, а это крайне непросто.
До хрипоты спорили. В итоге ему простыни надоели и он согласился юзать ф-ции.

В результате код некот. отчетов сократился в 10 раз, а правильность кода возросла, т.к ф-ция вылизывается и считает идеально точно везде.
Отчет лаконичен (зачастую один простой SELECT) и очень просто понимается и переделывается, если нужно.

Ф-ции тормозят ? Да. Но далеко не всегда. Главное ими аккуратно пользоваться.
Там где торможение было критичным - переделывали на обычные запросы. Но это 5% случаев.

Используя ф-ции никто не запрещает пользоваться обычными запросами.

зы: по сабжу ф-ции проблему решат.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003159
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
LSV,
LSV, Как вы думаете, а возможно ли какое-то решение на уровне СУБД, что бы при добавлении новых таблиц завязанных с субконто, не приходилось бы править код хранимых процедур и функций?
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003166
Кот Матроскин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
unimilk а возможно ли какое-то решение на уровне СУБД, что бы при добавлении новых таблиц завязанных с субконто, не приходилось бы править код хранимых процедур и функций ?

Можно править код View.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003168
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Mikle83Как вы определяете условие "ЕСЛИ". Где у вас хранится признак что именно для конкретной записи "субконто"?

Mikle83, нужную таблицу, связанную с субконто, могу получить через такой запрос:

Код: sql
1.
2.
3.
select ob.ViewName
LedgerAccount  La
join Object ob on ob.ObjectId = la.ObjectId



в ob.ViewName - имя искомой таблицы которую нужно цеплять. Проблема в том , что заранее неизвестно какое количество субконто может быть у счета, их количество может изменяться от 1 до .. N. Соответственно, неизвестно сколько и какие таблицы нужно join -ить в запросе получения данных.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003175
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Кот МатроскинНет - только при добавлении нового типа субконто. Например, решаем "теперь у нас в системе могут быть субконто Контрагенты" - тогда да, надо будет переписывать запрос, чтобы добавить приведенную мной строчку.


В том-то все и дело, что хочется уйти от постоянной правки запросов, при добавлении новых субконто (типов субконто). Возможно ли это ? Использовать функции (но в них получается тоже необходимо будет использовать динамические запросы ?) Либо как-то изменить структуру данных ?
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003187
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
так зашёлЕсли типов субконто не очень много(не более 10ти), то можно обойтись набором left join'ов

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

авторА вот если таблиц очень много. Тогда действительно стоит выбрать динамический запрос.
Не надо его сильно пугаться.

Таблиц уже достаточно много в системе и в них уже есть данные. Опасение внушает не сам факт использования динамических запросов, а то, что была допущена ошибка при проектировании базы (теперь я уже в этом уверен), и что эта ошибка в дальнейшем наложит свои ограничения на работу с данными.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003189
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
MasterZivТы совершил кардинальную ошибку при проектировании твоей БД (нарушение доменной целостности), теперь нужны кардинальные усилия для её преодоления, всё логично.


Да, теперь я это понимаю. Как ее можно исправить?
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003198
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Кот МатроскинМожно править код View

извиняюсь за неточную формулировку, да, я имел виду что бы не приходилось править sql -ный код ни во view, ни в функциях, ни в хранимых процедурах, ни в триггерах
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003259
Кот Матроскин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
unimilkв ob.ViewName - имя искомой таблицы которую нужно цеплять. Проблема в том , что заранее неизвестно какое количество субконто может быть у счета, их количество может изменяться от 1 до .. N. Соответственно, неизвестно сколько и какие таблицы нужно join -ить в запросе получения данных.

Количество субконто у счета неважно, прибавление еще одного субконто из заранее определенного списка на счет не влечет правки кода ("Вот теперь на счете 322223 у нас будет еще и субконто контрагенты"). Правку влечет добавление нового типа субконто в систему ("теперь у нас в качестве субконто могут быть еще и контрагенты").За ~7 лет сопровождения учетных систем, в которых было понятие "субконто", я видел такой кейс 2 раза - когда предопределенного списка типов субконто не хватило и его пришлось расширять.

unimilkчто хочется уйти от постоянной правки запросов, при добавлении новых субконто (типов субконто). Возможно ли это ? Использовать функции (но в них получается тоже необходимо будет использовать динамические запросы ?) Либо как-то изменить структуру данных ?


В принципе - возможно (например, при создании/изменении любого обьекта в системе заранее писать его "копию" в таблицу "значение субконто". ). Но это варианты с большими накладными расходами, и имхо правки кода или динамический sql выглядят предпочтительнее.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003280
LSV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
unimilkизвиняюсь за неточную формулировку, да, я имел виду что бы не приходилось править sql -ный код ни во view, ни в функциях, ни в хранимых процедурах, ни в триггерахМожно. Поручить это делать другому специалисту. :)
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003451
dma_caviar
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Почему нельзя в значения субконто писать не только ID справочников/документов и т.п., но и их "наименование"? Чтобы не извлекать это джоинами.
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003495
Mikle83
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
unimilkMikle83Как вы определяете условие "ЕСЛИ". Где у вас хранится признак что именно для конкретной записи "субконто"?

Mikle83, нужную таблицу, связанную с субконто, могу получить через такой запрос:
Код: sql
1.
2.
3.
select ob.ViewName
LedgerAccount  La
join Object ob on ob.ObjectId = la.ObjectId



в ob.ViewName - имя искомой таблицы которую нужно цеплять. Проблема в том , что заранее неизвестно какое количество субконто может быть у счета, их количество может изменяться от 1 до .. N. Соответственно, неизвестно сколько и какие таблицы нужно join -ить в запросе получения данных.

Следующий шаг - что конкретно по каждому субконто вытаскивается? Список полей, выбираемых в итоговый датасет, по всем субконто - единый? Или можно его привести к единому виду?
...
Рейтинг: 0 / 0
Проектирование базы для последующей работы с ней без использования динамических запросов
    #39003526
unimilk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Mikle83Следующий шаг - что конкретно по каждому субконто вытаскивается? Список полей, выбираемых в итоговый датасет, по всем субконто - единый? Или можно его привести к единому виду?

По субконто вытаскивается его значение так:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
Select 
from LedgerAccountValue  lav
-- если это субконто контрагентов 
join Contragent con on con.ContragentId = lav.ReferenceId
--или  если это субконто Договоров
join Dogovor dog on dog.DogovorId = lav.ReverevceId
---или если это товар
join Good g on g.GoodId = lav.ReverevceId
-- и т.д.



соответственно, список полей в итоговом датасете может быть разный , в зависимости от того с каким субконто имеем дело (con.ContragentName или dog.Dogovor, или g.good и т.д.)
...
Рейтинг: 0 / 0
25 сообщений из 34, страница 1 из 2
Форумы / Проектирование БД [игнор отключен] [закрыт для гостей] / Проектирование базы для последующей работы с ней без использования динамических запросов
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]