制作动态数据透视表但不使用提琴

时间:2019-05-29 19:22:27

标签: sql sql-server

我已经做到了:

;WITH a AS
(
    SELECT
        a.account
        ,index_num_date = 'date ' + CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY ratechangedate ))
        ,index_num_rate = 'rate ' + CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY ratechangedate ))
        ,ratechangedate
        ,new_noterate
    FROM MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
    INNER JOIN
    (
        SELECT *
        FROM mars..vw_loans
        WHERE loanstatus <> 'bk payment plan'
    ) b ON a.account = b.account
    WHERE archivedate = '5/20/2019'
)
,q1 AS
(
    SELECT DISTINCT
        account
        ,ratechangedate
        ,index_num_date
    FROM a
)
,q2 AS
(
    SELECT DISTINCT
        account
        ,new_noterate
        ,index_num_rate
    FROM a
)
,datepivot AS
(
    SELECT DISTINCT
        account
        ,[date 1]
        ,[date 2]
        ,[date 3]
        ,[date 4]
        ,[date 5]
        ,[date 6]
        ,[date 7]
        ,[date 8]
        ,[date 9]
        ,[date 10]
        ,[date 11]
        ,[date 12]
        ,[date 13]
    FROM q1
        PIVOT
        (
            MIN(ratechangedate)
            FOR index_num_date IN ( [date 1]
                ,[date 2]
                ,[date 3]
                ,[date 4]
                ,[date 5]
                ,[date 6]
                ,[date 7]
                ,[date 8]
                ,[date 9]
                ,[date 10]
                ,[date 11]
                ,[date 12]
                ,[date 13]
            )
        ) pvt1
)
,ratepivot AS
(
    SELECT DISTINCT
        account
        ,[rate 1]
        ,[rate 2]
        ,[rate 3]
        ,[rate 4]
        ,[rate 5]
        ,[rate 6]
        ,[rate 7]
        ,[rate 8]
        ,[rate 9]
        ,[rate 10]
        ,[rate 11]
        ,[rate 12]
        ,[rate 13]
    FROM q2
        PIVOT
        (
            MIN(new_noterate)
            FOR index_num_rate IN ( [rate 1]
                ,[rate 2]
                ,[rate 3]
                ,[rate 4]
                ,[rate 5]
                ,[rate 6]
                ,[rate 7]
                ,[rate 8]
                ,[rate 9]
                ,[rate 10]
                ,[rate 11]
                ,[rate 12]
                ,[rate 13]
            )
        ) pvt2
)
SELECT
    a.Account
    ,[date 1]
    ,[rate 1]
FROM datepivot a
LEFT JOIN ratepivot b ON a.Account = b.Account

哪个给我这个

enter image description here

但这不是动态的,我的Microsoft版本2016不允许我使用各种帖子中推荐的Fiddle。因此,建议使用Coalesce()函数,但不知道如何使后者动态化。任何帮助(不涉及某些帖子)都会真正帮助您。

更新

在评论之后,我尝试了此操作:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.Account) 
            FROM MARS_DW.[dbo].[vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive] c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT Account, ' + @cols + ' from 
            (
                select Account
                    , ratechangedate
                    , new_noterate
                from  MARS_DW.[dbo].[vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive]
           ) x
            pivot 
            (
                 min(ratechangedate)
                for category in (' + @cols + ')
            ) p '

但出现此错误:

Msg 1056, Level 15, State 1, Line 37
The number of elements in the select list exceeds the maximum allowed number of 4096 elements.
Msg 102, Level 15, State 1, Line 43
Incorrect syntax near 'x'.

更新

我试图以此来限制数量

DECLARE @cols AS NVARCHAR(MAX);
DECLARE @query  AS NVARCHAR(MAX);

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.RateChangeDate) 
            FROM MARS_DW.[dbo].[vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive] c
            WHERE c.ArchiveDate = '5/21/2019' AND c.AppliedDate > '1/2/2018'
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT Account, ' + @cols + ' from 
            (
                select Account
                    , ratechangedate
                    , new_noterate
                from  MARS_DW.[dbo].[vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive]
           ) x
            pivot 
            (
                 min(ratechangedate)
                for category in (' + @cols + ')
            ) p 
            pivot
            (
                min(new_noterate)
                for category in (' + @cols + ')
            )

            '


execute(@query)

但是我收到此错误:

Msg 102, Level 15, State 1, Line 52
Incorrect syntax near ')'.

此处要求的是数据表中的前10名

enter image description here

2 个答案:

答案 0 :(得分:2)

创建交叉表查询会容易得多。如果您可以识别模式,则动态代码可以更容易编码,并且Internet(和此站点)上有多个示例。如果您不知道如何创建动态代码,建议您不要使用它,直到您完全了解该做什么和不该做什么为止。

public void Method1()
{
    TimeSpan result = TimeMethod(Method2);
    string thisEvent = event1 ? "event1" : "event2";
    Console.WriteLine($"Time elapsed from {thisEvent}: {result.ToString("hh\\:mm\\:ss")}");
}

更新:

我告诉您阅读有关如何使用动态SQL的文章。我还告诉过您确定模式,但您没有。现在,有一个答案会比我建议的要慢得多,并且我只是不想传播劣等代码,所以这里有一个选择。

WITH a
AS (
    SELECT a.account,
        dense_rank() OVER ( PARTITION BY a.account ORDER BY ratechangedate) AS index_num,
        ratechangedate,
        new_noterate
    FROM MARS_DW.[dbo].[vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive] a
    INNER JOIN (
        SELECT *
        FROM mars..vw_loans
        WHERE loanstatus <> 'bk payment plan'
        ) b ON a.account = b.account
    WHERE archivedate = '5/20/2019'
    )
SELECT a.Account,
    MIN( CASE WHEN index_num = 1 THEN ratechangedate END) AS [date 1],
    MIN( CASE WHEN index_num = 1 THEN new_noterate END)   AS [rate 1],
    MIN( CASE WHEN index_num = 2 THEN ratechangedate END) AS [date 2],
    MIN( CASE WHEN index_num = 2 THEN new_noterate END)   AS [rate 2],
    MIN( CASE WHEN index_num = 3 THEN ratechangedate END) AS [date 3],
    MIN( CASE WHEN index_num = 3 THEN new_noterate END)   AS [rate 3],
    MIN( CASE WHEN index_num = 4 THEN ratechangedate END) AS [date 4],
    MIN( CASE WHEN index_num = 4 THEN new_noterate END)   AS [rate 4],
    MIN( CASE WHEN index_num = 5 THEN ratechangedate END) AS [date 5],
    MIN( CASE WHEN index_num = 5 THEN new_noterate END)   AS [rate 5],
    MIN( CASE WHEN index_num = 6 THEN ratechangedate END) AS [date 6],
    MIN( CASE WHEN index_num = 6 THEN new_noterate END)   AS [rate 6],
    MIN( CASE WHEN index_num = 7 THEN ratechangedate END) AS [date 7],
    MIN( CASE WHEN index_num = 7 THEN new_noterate END)   AS [rate 7],
    MIN( CASE WHEN index_num = 8 THEN ratechangedate END) AS [date 8],
    MIN( CASE WHEN index_num = 8 THEN new_noterate END)   AS [rate 8]
FROM a
GROUP BY a.Account;

答案 1 :(得分:2)

在玩了一张临时表之后,我想我终于找到了。不过,首先要注意几件事。

  • 由于您要旋转两列,因此必须分别旋转两列,然后将子结果重新组合在一起。
  • 由于我们要在两个枢轴上都使用@cols,所以我们必须创建@cols变量的版本,该变量为最终选择的动态创建的枢轴列名称取别名。

因此,让我们进入代码。

首先,我们将创建包含将动态创建的列名的字符串。

DECLARE @colsAll AS NVARCHAR(MAX);
DECLARE @cols AS NVARCHAR(MAX);

SET @cols = 
STUFF((
        SELECT DISTINCT
            ',' + QUOTENAME(CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate)))
        FROM MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
        INNER JOIN
        (
            SELECT account
            FROM mars..vw_loans
            WHERE loanstatus <> 'bk payment plan'
        ) b ON a.account = b.account
        WHERE
            a.ArchiveDate = '5/21/2019'
            AND a.AppliedDate > '1/2/2018'
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)'), 1, 1, ''
)

SET @colsAll = 
STUFF((
        SELECT DISTINCT
            ',' + 'd.' + QUOTENAME(CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY account ORDER BY ratechangedate ))) + ' AS [Date'
            + CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY account ORDER BY ratechangedate )) + '], ' + 'r.'
            + QUOTENAME(CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY account ORDER BY ratechangedate ))) + ' AS [Rate'
            + CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY account ORDER BY ratechangedate )) + ']'
        FROM MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
        INNER JOIN
        (
            SELECT account
            FROM mars..vw_loans
            WHERE loanstatus <> 'bk payment plan'
        ) b ON a.account = b.account
        WHERE
            a.ArchiveDate = '5/21/2019'
            AND a.AppliedDate > '1/2/2018'
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)'), 1, 1, ''
)

设置@colsAll时,我使用的是将在最终选择中定义的表别名。

现在要生成枢轴:

DECLARE @query AS NVARCHAR(MAX);

SET @query = ';WITH dates as (SELECT Account, ' + @cols
             + ' from 
            (
                select a.Account
                    , a.ratechangedate
                    , DateIndex = CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))
                from  MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
                INNER JOIN (SELECT account
                                FROM mars..vw_loans
                                WHERE loanstatus <> ''bk payment plan''
                            ) b ON a.account = b.account
                WHERE a.ArchiveDate = ''5/21/2019'' AND a.AppliedDate > ''1/2/2018''
           ) x

            pivot
            (
                min(ratechangedate)
                for DateIndex in (' + @cols + ')
            ) d)


            ,rates as (SELECT Account, ' + @cols
             + ' from 
            (
                select a.Account
                    , a.new_noterate
                    , RateIndex = CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))
                from  #vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
                INNER JOIN (SELECT account
                                FROM mars..vw_loans
                                WHERE loanstatus <> ''bk payment plan''
                            ) b ON a.account = b.account
                WHERE a.ArchiveDate = ''5/21/2019'' AND a.AppliedDate > ''1/2/2018''
           ) x

            pivot
            (
                min(new_noterate)
                for RateIndex in (' + @cols + ')
            ) r)


            SELECT d.Account, ' + @colsAll + '
            FROM dates d
            JOIN rates r ON d.Account = r.Account'

EXECUTE ( @query )

如果要PRINT查询,您将看到将要运行的查询。我一直喜欢PRINT查询,然后将结果复制并粘贴到新的查询窗口中并运行它。如果弹出任何错误,则调试最终查询要比调试动态sql容易。作为参考,上面的代码吐出的查询看起来像这样:

;WITH dates AS
(
    SELECT
        Account
        ,[1]
        ,[2]
        ,[3]
    FROM
    (
        SELECT
            a.Account
            ,a.ratechangedate
            ,DateIndex = CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account
                                                                  ORDER BY a.ratechangedate
                                                           )
                         )
        FROM MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
        INNER JOIN
        (
            SELECT account
            FROM mars..vw_loans
            WHERE loanstatus <> 'bk payment plan'
        ) b ON a.account = b.account
        WHERE
            a.ArchiveDate = '5/21/2019'
            AND a.AppliedDate > '1/2/2018'
    ) x
    PIVOT
    (
        MIN(ratechangedate)
        FOR DateIndex IN ( [1]
            ,[2]
            ,[3]
        )
    ) d
)
,rates AS
(
    SELECT
        Account
        ,[1]
        ,[2]
        ,[3]
    FROM
    (
        SELECT
            a.Account
            ,a.new_noterate
            ,RateIndex = CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account
                                                                  ORDER BY a.ratechangedate
                                                           )
                         )
        FROM #vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
        INNER JOIN
        (
            SELECT account
            FROM mars..vw_loans
            WHERE loanstatus <> 'bk payment plan'
        ) b ON a.account = b.account
        WHERE
            a.ArchiveDate = '5/21/2019'
            AND a.AppliedDate > '1/2/2018'
    ) x
    PIVOT
    (
        MIN(new_noterate)
        FOR RateIndex IN ( [1]
            ,[2]
            ,[3]
        )
    ) r
)
SELECT
    d.Account
    ,d.[1] AS Date1
    ,r.[1] AS Rate1
    ,d.[2] AS Date2
    ,r.[2] AS Rate2
    ,d.[3] AS Date3
    ,r.[3] AS Rate3
FROM dates d
JOIN rates r ON d.Account = r.Account

编辑:

这是查询的更新版本,该数据透视表的列名用前导零填充以创建三位数的序数。

SET @cols = 
STUFF((
        SELECT DISTINCT
            ',' + QUOTENAME(RIGHT(('00'+CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))),3))
        FROM MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
        INNER JOIN
        (
            SELECT account
            FROM mars..vw_loans
            WHERE loanstatus <> 'bk payment plan'
        ) b ON a.account = b.account
        WHERE
            a.ArchiveDate = '5/21/2019'
            AND a.AppliedDate > '1/2/2018'
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)'), 1, 1, ''
)

SET @colsAll = 
STUFF((
        SELECT DISTINCT
            ',' + 'd.' + QUOTENAME(RIGHT(('00'+CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))),3)) + ' AS [Date'
            + RIGHT(('00'+CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))),3) + '], ' + 'r.'
            + QUOTENAME(RIGHT(('00'+CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))),3)) + ' AS [Rate'
            + RIGHT(('00'+CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))),3) + ']'
        FROM MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
        INNER JOIN
        (
            SELECT account
            FROM mars..vw_loans
            WHERE loanstatus <> 'bk payment plan'
        ) b ON a.account = b.account
        WHERE
            a.ArchiveDate = '5/21/2019'
            AND a.AppliedDate > '1/2/2018'
        FOR XML PATH(''), TYPE
    ).value('.', 'NVARCHAR(MAX)'), 1, 1, ''
)


SET @query = ';WITH dates as (SELECT Account, ' + @cols
             + ' from 
            (
                select a.Account
                    , a.ratechangedate
                    , DateIndex = RIGHT((''00''+CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))),3)
                from  MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
                INNER JOIN (SELECT account
                                FROM mars..vw_loans
                                WHERE loanstatus <> ''bk payment plan''
                            ) b ON a.account = b.account
                WHERE a.ArchiveDate = ''5/21/2019'' AND a.AppliedDate > ''1/2/2018''
           ) x

            pivot
            (
                min(ratechangedate)
                for DateIndex in (' + @cols + ')
            ) d)


            ,rates as (SELECT Account, ' + @cols
             + ' from 
            (
                select a.Account
                    , a.new_noterate
                    , RateIndex = RIGHT((''00''+CONVERT(VARCHAR(30), DENSE_RANK() OVER ( PARTITION BY a.account ORDER BY a.ratechangedate ))),3)
                from  MARS_DW.dbo.vw_GTMScheduledRateAndPaymentChangesWithAccountNumber_Archive a
                INNER JOIN (SELECT account
                                FROM mars..vw_loans
                                WHERE loanstatus <> ''bk payment plan''
                            ) b ON a.account = b.account
                WHERE a.ArchiveDate = ''5/21/2019'' AND a.AppliedDate > ''1/2/2018''
           ) x

            pivot
            (
                min(new_noterate)
                for RateIndex in (' + @cols + ')
            ) r)


            SELECT d.Account, ' + @colsAll + '
            FROM dates d
            JOIN rates r ON d.Account = r.Account'