Каков более эффективный способ T-SQL для запроса таблицы с иерархическим типом данных

У меня есть таблица под названием Budgetline. Он отслеживает строки бюджета для проектов (Гранты).

Схема:

BudgetId       int
Amount         decimal
LoginUser      varchar
InsertDate     datetime
GrantPhaseID   int
BudgetChartID  int
Rootdir        hierarchyid
OverHead       decimal

Столбец Amount — это бюджетная сумма для строки бюджета. Бюджетная строка может иметь подбюджетную строку. Подбюджетная строка может иметь другую подбюджетную строку. Иногда может быть до 5 уровней.

Есть еще одна таблица TransactionsDetail; он отслеживает расходы бюджета

Схема:

TransactionDetailID  int
TransactionID        int
Amount               numeric
ExRateAmount         numeric
TransactionDate      date
BudgetId             int
InsertDate           datetime
OverHead             decimal
Paid                 bit
PaidDate             datetime
LoginUser            varchar

Проекты (гранты) имеют этапы. Есть еще одна таблица GrantPhase, чтобы отслеживать это.

Есть еще одна таблица под названием BudgetChart. Он содержит список Budgetlines. Разные проекты (гранты) могут иметь одинаковые бюджетные линии.

Ниже приведена полная табличная функция для получения подстрок бюджета (потомков) строки бюджета (родителя).

ALTER FUNCTION [dbo].[getSUBS]
    (@BudgetID INT
     --,@GrantPhaseID INT
    )
RETURNS @Mytable TABLE (CID INT,
                        [COUNT] INT,
                        DESCRIPTION VARCHAR(256),
                        AMOUNT NUMERIC(18,2),
                        SPENT NUMERIC(18,2),
                        BALANCE NUMERIC(18,2),
                        OVERHEAD NUMERIC(18,2)
                        --  BUDGETLIMIT numeric(18,2)
                       )
AS
BEGIN

-- get budgetline root level
declare @BudgetIDrootRevel int
SELECT @BudgetIDrootRevel = RootDir.GetLevel() FROM budgetlines WHERE budgetid = @BudgetID

-- GET GRANTPHASEID
DECLARE @GrantPhaseID int=(
select GrantPhaseID from BudgetLines where BudgetId=@BudgetID
)

DECLARE @RootDir HIERARCHYID
SELECT @RootDir = RootDir FROM budgetlines WHERE budgetid = @BudgetID
insert into @Mytable(
    CID 
,   [COUNT]
,   DESCRIPTION 
,   AMOUNT 
,   SPENT
,   BALANCE
,   OVERHEAD 
--, BUDGETLIMIT
)

SELECT
    BudgetId 
,   ROW_NUMBER() OVER (ORDER BY BudgetID DESC)
,   [Description]
,   dbo.[getBudgetAmount](BudgetLines.BudgetId)  AMOUNT --Sums all transactions made in the TransactionDetails table
,   [dbo].[getBudgetSpent](BudgetId) as SPENT
,   ISNULL((dbo.[getBudgetAmount](BudgetLines.BudgetId)-[dbo].[getBudgetSpent](BudgetId)),0) as BALANCE
,   BudgetLines.OVERHEAD
--, BUDGETLIMIT

FROM BudgetLines INNER JOIN BudgetChart 
ON BudgetChart.BudgetChartID = BudgetLines.BudgetChartID

WHERE RootDir.IsDescendantOf(@RootDir)=1
and GrantPhaseID = @GrantPhaseID
and Rootdir.GetLevel()=(@BudgetIDrootRevel+1)
--AND isBudgetline=1

return ;

end

Оно работает.

  • В таблице Budgetline всего 252 записи.
  • В таблице TransactionDetails всего 172 записи.

Моя задача:

Возврат подстроек бюджета занимает около 10 секунд, если эта конкретная бюджетная строка имеет 3 или более подуровней (потомков).

Мой вопрос:

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

Ниже показано, как выглядит конструкция. Пользователь может увидеть строки суббюджета, дважды щелкнув строку или нажав кнопку [Вниз].

Как выглядит пользовательский интерфейс

Излишне говорить: это мой первый пост о всемогущем stackoverflow. Извините, если я нарушил какие-либо правила сообщества. Я все еще учу их.


person Kaunda    schedule 11.11.2018    source источник
comment
Обычный способ построения и запроса иерархических данных в sqlserver — с помощью рекурсивного cte — взгляните на: -line-by-line" title="как рекурсивный cte запускается построчно"> stackoverflow.com/questions/3187850/ - также гуглите рекурсивный cte и нажимайте на лучшие ответы SO для получения дополнительной информации. stackoverflow.com/questions/14518090/ stackoverflow.com/questions/45888858/   -  person Caius Jard    schedule 11.11.2018
comment
На самом деле я читал CTE, когда мне нужно было сохранить иерархические данные. 1) Мне было трудно понять. 2) Я прочитал на сайте Microsoft, что тип данных HierarchyID оптимизирован для представления древовидной структуры данных. В нем говорится: Встроенный тип данных Hierarchid упрощает хранение и запрос иерархических данных. иерархия оптимизирована для представления деревьев, которые являются наиболее распространенным типом иерархических данных. Источник: (docs.microsoft.com/en-us/sql/relational-databases/)   -  person Kaunda    schedule 11.11.2018
comment
Прочитав более внимательно, я немного озадачен, почему вы, кажется, сами управляете обходом дерева - кажется, вы идете только на один уровень глубже, чем в настоящее время в getsubs, но, безусловно, IsAncestorOf и isDescendantOf sqlserver пройдут дерево для вас и получить все необходимые данные?   -  person Caius Jard    schedule 12.11.2018
comment
Это то, что я использовал. Функция IsDescendantOf. Вы можете видеть это в разделе кода WHERE: WHERE RootDir.IsDescendantOf(@RootDir)=1. Я не управляю обходом дерева сам   -  person Kaunda    schedule 12.11.2018
comment
Да, но вы также ограничиваете уровень : Rootdir.GetLevel()=(@BudgetIDrootRevel+1) ? а затем несколько раз вызывать эту функцию, чтобы получить каждый следующий уровень? Если вам нужны все уровни, почему бы просто не позволить SQLS получить их все одним нажатием?   -  person Caius Jard    schedule 12.11.2018
comment
Это так. Я даже не могу вспомнить, почему я по глупости добавил ..and Rootdir.GetLevel()‹=100 к условию where. Я снимаю это. Спасибо. Но медлительность та же.   -  person Kaunda    schedule 12.11.2018
comment
А с какой скоростью вы сталкиваетесь при простом запуске SELECT * FROM BudgetLines INNER JOIN BudgetChart ON BudgetChart.BudgetChartID = BudgetLines.BudgetChartID WHERE RootDir.IsDescendantOf(@RootDir)=1 and GrantPhaseID = @GrantPhaseID ? Я думаю, вам нужно настроить это в первую очередь   -  person Caius Jard    schedule 12.11.2018
comment
Это возвращает рекорды с молниеносной скоростью. Это дало мне подсказку проверить скалярные функции (getBudgetAmount и getBudgetSpent). Вот где головная боль это. Он отображается очень хорошо и быстро, если я комментирую столбцы «Бюджет», «Потрачено» и «Отклонение». Спасибо за подсказку   -  person Kaunda    schedule 13.11.2018
comment
Часто использование скалярного udf в запросе является плохой идеей, поскольку это может означать, что запросы выполняются на тысячи или миллионы запросов больше, чем необходимо. Иногда оптимизатор запросов может экстраполировать запрос внутри udf и переписать основной запрос так, чтобы он включал запрос внутри udf, но это маловероятно. У меня возникло бы искушение превратить эти udf в хранимые процедуры (чтобы их нельзя было случайно использовать в запросе) и переписать ваш запрос, использующий udf, так, чтобы он просто получал требуемые результаты в виде одного запроса — лучшая оптимизация и операции на основе наборов обеспечивают повышение скорости   -  person Caius Jard    schedule 13.11.2018
comment
Спасибо. я работаю над этим   -  person Kaunda    schedule 14.11.2018


Ответы (1)


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

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

*эффективен с точки зрения: не тратить время БД на сбор нежелательных данных/не передавать данные по сети, если они не будут использоваться

person Caius Jard    schedule 11.11.2018
comment
1) Это правда: я никогда не думал о реализации этого на уровне пользовательского интерфейса. Я буду искать в этом. 2) Мой запрос возвращает только данные Wanted. Или он делает что-то еще за сценой? - person Kaunda; 11.11.2018