Сумма на основе поведения поля с использованием цикла или динамического запроса - PullRequest
1 голос
/ 18 июня 2019

У меня есть такая таблица:

ID       FullName   ID_Total                           Balance
100000   kkkk       100020|100003|100080|100050        NULL
100001   llll       100080|100050|100005               NULL
100002   qqqq       100004|100031                      NULL
100003   wwww                                          1025.02 
100004   rrrr                                          298.63   
100005   tttt                                          548.25
100006   yyyy                                          659.20
100010   uuuu       100003...100005|100050...100080    NULL
100020   iiii                                          3687.05 
100030   oooo       100004...100006                    NULL 
100031   pppp                                          945.36  
100040   aaaa       100006|100031...100080|100003      NULL
100050   ssss                                          1064.98 
100080   ffff                                          569.65   

Я использую этот запрос, который работает, только если ID_Total остается прежним:

;WITH CTE AS( SELECT ID, FullName, 
--SEPARATE ID_Total into Columns 
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),1,6) TL1,
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),7,6) TL2, 
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),13,6) TL3,
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),19,6) TL4,
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),25,6) TL5,
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),31,6) TL6,
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),37,6) TL7,
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),43,6) TL8,
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),49,6) TL9,
SUBSTRING(REPLACE(REPLACE(ID_Total,'.',''),'|',''),55,6) TL10,
ID_Total, Balance 

FROM TABLE1
)

SELECT ID, FullName, ID_Total,

(CASE WHEN Balance IS NULL THEN         

    CASE
    --Code to sum balances corresponding to kkkk FullName
        WHEN X.TL1 != '' AND X.TL2 != '' AND X.TL3 != '' AND X.TL4 != ''
            THEN    
               (
                SELECT SUM(Balance)
                FROM CTE 
                WHERE ID = X.TL1 OR ID= X.TL2 OR ID=X.TL3 OR ID= X.TL4 --OR IN THIS CASE IS TO SUM Values with | 
                )

    --Code to sum balances corresponding to llll FullName

        WHEN X.TL1 != '' AND X.TL2 != '' AND X.TL3 != '' 
            THEN    
                (
                    SELECT SUM(Balance)
                    FROM CTE
                    WHERE ID = X.TL1 OR ID = X.TL2 OR ID= X.TL3
                    )

    --Code to sum balances corresponding to qqqq FullName

        WHEN X.TL1 != '' AND X.TL2 != ''
            THEN
                (
                    SELECT SUM(Balance)
                    FROM CTE
                    WHERE ID = X.TL1 OR ID = X.TL2
                )

    --Code to sum balances corresponding to uuuu FullName

        WHEN X.TL1 != '' AND X.TL2 != '' AND X.TL3 != '' AND X.TL4 != '' AND  LEN(ID_Total) = 31
            THEN    
                (
                    SELECT SUM(Balance)
                    FROM CTE
                    WHERE ID >= X.TL1 AND ID <= X.TL2 OR ID >= X.TL3 AND ID <= X.TL4
                )

    --Code to sum balances corresponding to oooo FullName

        WHEN X.TL1 != '' AND X.TL2 != '' AND LEN(ID_Total) = 15
            THEN
                (
                    SELECT SUM(Balance)
                    FROM CTE
                    WHERE ID >= X.TL1 AND ID <= X.TL2 
                )

    --Code to sum balances corresponding to aaaa FullName

        WHEN X.TL1 != '' AND X.TL2 != '' AND X.TL3 != '' AND X.TL4 != '' AND LEN(ID_Total) = 29
            THEN
            (
                SELECT SUM(Balance)
                FROM CTE
                WHERE ID = X.TL1 OR ID >= X.TL2 AND ID <= X.TL3 OR ID = X.TL4
            )

            END

            ELSE Balance

        END) AS Balances

    FROM CTE X

    WHERE ID IN (
100000,100001,100002,100003,100004,100005,100006,
100010,100020,100030,100031,100040,100050,100080
)

ORDER BY ID

Требуется вывод:

ID       FullName   ID_Total                           Balance
100000   kkkk       100020|100003|100080|100050        6346.7
100001   llll       100080|100050|100005               2182.88
100002   qqqq       100004|100031                      1243.99
100003   wwww                                          1025.02 
100004   rrrr                                          298.63   
100005   tttt                                          548.25
100006   yyyy                                          659.20
100010   uuuu       100003...100005|100050...100080    3506.26
100020   iiii                                          3687.05 
100030   oooo       100004...100006                    1506.08 
100031   pppp                                          945.36  
100040   aaaa       100006|100031...100080|100003      4264.21
100050   ssss                                          1064.98 
100080   ffff                                          569.65  

Я пытаюсь суммировать баланс в соответствии с порядком, указанным в столбце ID_Total, где | означает сумму сследующее значение и ... - это диапазон , например, 100001 ... 100004 . Это означает, что баланс будет рассчитываться на основе суммы всех идентификаторов, начиная с 100001 100002 100003.на 100004.

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

ПРИМЕЧАНИЕ: Независимо от того, какая формула всегда будет такой же, как когдаидентификатор отделяется |это означает сумму со следующим идентификатором, а когда ... это означает диапазон.

2Примечание: Некоторые примеры того, как может измениться шаблон ID_Total:

100001|200002|355520 to 100001|200002...450002
200008...200015|200020|300030...400000 to 200008...200015|200020|300030...400000|500008...500012
100001...200025 to 100001...200025|300092...300098

и т.д ...

Любая помощь будет признательна!

1 Ответ

1 голос
/ 18 июня 2019

С двумя вспомогательными функциями.

Пример

Select A.ID
      ,A.FullName
      ,A.ID_Total
      ,Balance= sum(B.balance)
 From  (
         Select ID
               ,FullName
               ,ID_Total
               ,Pos1 = C.Pos1
               ,Pos2 = IsNull(C.Pos2,C.Pos1)
         From @YourTable
         Cross Apply [dbo].[tvf-Str-Parse](IsNull(NullIf(ID_Total,''),ID),'|') B
         Cross Apply [dbo].[tvf-Str-Parse-Row](B.RetVal,'...') C
 ) A  
 Join @YourTable B on B.ID between A.Pos1 and A.Pos2
 Group By A.ID
         ,A.FullName
         ,A.ID_Total

Возвращает

enter image description here

Функции, если интересно

CREATE FUNCTION [dbo].[tvf-Str-Parse] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (  
    Select RetSeq = row_number() over (order by 1/0)
          ,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
    From  (Select x = Cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i)
);

и

CREATE FUNCTION [dbo].[tvf-Str-Parse-Row] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (
    Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
          ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
          ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
          ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
          ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
          ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
          ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
          ,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
          ,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
    From  ( values (cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml))) as A(xDim)
)

РЕДАКТИРОВАТЬ - ТОЛЬКО ДЛЯ УДОВОЛЬСТВИЯ

Если вам не нужны функции, вот еще один вариант

dbFiddle

...