使用循环或动态查询基于字段行为的求和

时间:2019-06-18 15:26:58

标签: sql sql-server while-loop sql-server-2014

我有一个这样的表:

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的总和来计算。

我在上面提供的查询工作正常,但是当ID_total的模式更改顺序时出现了问题,因此每次ID_Total更改时,我都必须修改查询以使其与新的ID_Total模式对齐,因此我正在尝试以获得一种动态地对余额进行求和或根据ID_Total使用一段时间的方式。

注意: 不管什么公式,其ID总是用|分隔。它表示下一个ID的总和,当...表示范围。

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 个答案:

答案 0 :(得分:1)

具有两个助手功能。

示例

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