T-SQL - 如何将变量拆分为4列以进行输出?

时间:2017-10-18 12:39:44

标签: sql sql-server tsql sql-server-2014

这是一个T-SQL问题,我使用Microsoft SQL Server 2014.我正在加入三个表,这非常简单。棘手的部分是:其中一个变量SubtotalKey采用“ABD_1999_MAE_1”形式。我想将此变量拆分为四个组件,由下划线分隔,并在查询中的特定点包含输出中的四列。我有一个可行的解决方案,它使用标量函数。在它完成我刚才描述的意义上是可行的......但是存在使其无法使用的性能问题。我已经将标量函数转换为表值函数,并在其上使用“外部应用”作为解决方案。不幸的是,这会导致连接中每个结果行的输出中有4行。不知道从哪里开始 - 我试过枢轴,但枢轴需要数字列来转动,我想。所有人都非常感谢。

下面代码中的表值函数ufn_SplitString将上面的字符串拆分为一个包含1列和4行的表。 4行分别包含ABD,1999,MAE,1的值。

出于这个问题的目的,SubtotalKey中有4个元素,但实际上,这个数字是可变的。如果我事先不知道所需的额外列数是多少,那么解决方案是否可行? 到目前为止,这是我的代码:

SELECT 
    t1.t_proj AS time_period,
    ufn.item,
    t3.AnnClaimVal AS annuity_outgo_smbel,
    t3.DeathClaimVal AS death_outgo_smbel,
    t2.SolvSurvXCF AS annuity_outgo_reins,
    t2.SolvDeathXCF AS death_outgo_reins,
    t2.ReinSwapXCF AS mortswap_fixedleg_payment,
    t3.ExpenseValXCF AS ren_exp,
    t1.InvExpSCF AS inv_exp,
    t1.InvExpReinSCF AS inv_exp_reins

    FROM [sch_ImmAnn].[viw_mdlEV_Formulae] t1
    INNER JOIN [sch_ImmAnn].[viw_mdl_Formulae] t2 
    ON t1.t_proj = t2.t_proj AND t1._SubtotalKey = t2._SubtotalKey AND t1._Scenario = t2._Scenario AND t1._ExecRun_UID = t2._ExecRun_UID 
    INNER JOIN [sch_ImmAnn].[viw_mdlValue_Formulae] t3 
    ON t1.t_proj = t3.t_proj AND t1._SubtotalKey = t3._SubtotalKey AND t1._Scenario = t3._Scenario AND t1._ExecRun_UID = t3._ExecRun_UID 
    OUTER APPLY sch_Common.ufn_SplitString(t1._SubtotalKey,'_') ufn
    WHERE t1._ExecRun_UID = @ExecUID AND t1._Scenario = @Scenario
    AND t1.t_proj >= 0 AND  t1.t_proj <= 650
    ORDER BY SubtotalKey, time_period

以下是t1的一些示例数据:

t_proj SubtotalKey   Scenario    ExecRun_UID InvExpSCF InvExpReinSCF
1      ABD_1999_MAE_1       1       36FA21C8 5334.44   37.88
2      EMM_E12_MAE_3        1       36FA21C8 1894.88   1298.3
3      XYZ_2008_MAE_1       1       36FA21C8 12.99     10009.33

以下是t2的一些示例数据:

t_proj SubtotalKey   Scenario    ExecRun_UID SolvSurvXCF   SolvDeathXCF ReinSwap    

1      ABD_1999_MAE_1       1       36FA21C8 543.88        12.33          1.2
2      EMM_E12_MAE_3        1       36FA21C8 2985.11       59.31          4.6
3      XYZ_2008_MAE_1       1       36FA21C8 309999.12     111.33         9.7

以下是t3的一些示例数据:

t_proj SubtotalKey   Scenario    ExecRun_UID ExpenseValXCF AnnClaimVal DeathClaimVal 
1      ABD_1999_MAE_1       1       36FA21C8 100           901         678
2      EMM_E12_MAE_3        1       36FA21C8 200           492         121
3      XYZ_2008_MAE_1       1       36FA21C8 554           510         144

这是所需的输出:

t_proj  Col1   Col2   Col3   Col4   Scenario   ExecRun_UID   InvExpSCF   InvExpReinSCF   SolvSurvXCF   SolvDeathXCF   ReinSwap   ExpenseValXCF  AnnClaimVal DeathClaimVal
1       ABD    1999   MAE    1      1          36FA21C8      5334.44     37.88           543.88         12.33          1.2       100             901         678
2       EMM    E12    MAE    1      1          36FA21C8      1894.88     1298.3          2985.11        59.31          4.6       200             492         121
3       XYZ    2008   MAE    1      1          36FA21C8      12.99       10009.33        309999.12      111.33         9.7       554             510         144

功能代码:

ALTER FUNCTION [sch_Common].[ufn_SplitString]
(     
      @Input NVARCHAR(MAX),
      @Character CHAR(1)
)

RETURNS @Output TABLE (
      Item NVARCHAR(1000)
)

AS

BEGIN

      DECLARE @StartIndex INT, @EndIndex INT

      SET @StartIndex = 1
      IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
      BEGIN
            SET @Input = @Input + @Character
      END

      WHILE CHARINDEX(@Character, @Input) > 0
      BEGIN
            SET @EndIndex = CHARINDEX(@Character, @Input)
            INSERT INTO @Output(Item)
            SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)
            SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
      END
      RETURN
END

1 个答案:

答案 0 :(得分:1)

假设你看下面的内容(Varibale:ABD_1999_MAE_1):

Column1 Column2 Column3 Column4
ABD     1999    MAE     1

如果,以上是正确的,那么您可以使用XML方法和CROSS APPLY

SELECT DISTINCT
       A.t_proj,
       split.a.value('/X[1]', 'NVARCHAR(MAX)') Col1,
       split.a.value('/X[2]', 'NVARCHAR(MAX)') Col2,
       split.a.value('/X[3]', 'NVARCHAR(MAX)') Col3,
       split.a.value('/X[4]', 'NVARCHAR(MAX)') Col4,
       A.Scenario,
       A.ExecRun_UID,
       A.InvExpSCF,
       A.InvExpReinSCF,
       A.SolvSurvXCF,
       A.SolvDeathXCF,
       A.ReinSwap,
       A.ExpenseValXCF,
       A.AnnClaimVal,
       A.DeathClaimVal
FROM
(
    SELECT T1.t_proj,
           CAST('<X>'+REPLACE(T1.SubtotalKey, '_', '</X><X>')+'</X>' AS XML) AS String,
           T1.Scenario,
           T1.ExecRun_UID,
           T1.InvExpSCF,
           T1.InvExpReinSCF,
           T2.SolvSurvXCF,
           T2.SolvDeathXCF,
           T2.ReinSwap,
           T3.ExpenseValXCF,
           T3.AnnClaimVal,
           T3.DeathClaimVal
    FROM T1
         INNER JOIN  T2 ON T2.t_proj = T1.t_proj
                              AND T2.SubtotalKey = T1.SubtotalKey
                              AND T2.Scenario = T1.Scenario
         INNER JOIN  T3 ON T3.t_proj = T1.t_proj
                              AND T3.SubtotalKey = T3.SubtotalKey
                              AND T3.Scenario = T1.Scenario
) AS A
CROSS APPLY String.nodes('/X') split(a);

期望的结果:

t_proj  Col1   Col2   Col3   Col4   Scenario   ExecRun_UID   InvExpSCF   InvExpReinSCF   SolvSurvXCF   SolvDeathXCF   ReinSwap   ExpenseValXCF  AnnClaimVal DeathClaimVal
1       ABD    1999   MAE    1      1          36FA21C8      5334.44     37.88           543.88         12.33          1.2       100             901         678
2       EMM    E12    MAE    3      1          36FA21C8      1894.88     1298.3          2985.11        59.31          4.6       200             492         121
3       XYZ    2008   MAE    1      1          36FA21C8      12.99       10009.33        309999.12      111.33         9.7       554             510         144