具有未确定列的SQL交叉表

时间:2017-06-15 16:17:44

标签: sql sql-server tsql pivot crosstab

我看到很多类似的问题,但是几乎所有问题都将结果分组为列名(基于结果的列名),我的是一个更简单的列表。我不在乎它是否使用动态SQL(我认为必须这样做)。

请不要告诉我需要对表格进行重组,我是在遗留系统中工作而没有选择。

基本上,我只需要一张有效表格的列表" B"与表#34; A"中的给定记录匹配的条目。

我还没有任何代码示例,因为我没有找到正确设置的方法。

Table: Customer c
CustID   Name
1        Bill Smith
2        Jim Jones
3        Mary Adams
4        Wendy Williams

Table: Debt d
CustID   Creditor             Balance
1        ABC Loans            245
1        Citibank             815
2        Soprano Financial   74000
3        Citibank             24
3        Soprano Financial   93000
3        Wells Fargo          275
3        Midwestern S&L       2500
4        ABC Loans            1500
4        Fred's Payday Loan   1000

Desired Output:
Name            Cred1                Bal1   Cred2               Bal2   Cred3        Bal3   Cred4            Bal4
Bill Smith      ABC Loans            245    Citibank            815    (NULL)       (NULL) (NULL)           (NULL)
Jim Jones       Soprano Financial    74000  (NULL)              (NULL) (NULL)       (NULL) (NULL)           (NULL)
Mary Adams      Citibank             24     Soprano Finanacial  93000  Wells Fargo   275   Midwestern S&L   2500
Wendy Williams  ABC Loans            1500   Fred's Payday Loan  1000   (NULL)       (NULL) (NULL)           (NULL)

基本上,我可能必须收集某些特定" CustomerID"的大量记录的计数,并根据它来定义输出列。如果已经回答了这个问题,请随时链接并关闭它,我在搜索时没有看到这种特定情况。

2 个答案:

答案 0 :(得分:3)

这是另一种动态方法。我们使用Row_Number()创建最小数量的列。

示例

Declare @SQL varchar(max) = Stuff((Select Distinct ','+QuoteName(concat('Cred',ColNr)) 
                                                  +','+QuoteName(concat('Bal',ColNr)) 
                                    From (Select ColNr=Row_Number() over (Partition By CustID Order By Creditor) From Debt ) A  
                                    Order By 1 
                                    For XML Path('')),1,1,'') 

Select  @SQL = '
Select *
From (
        Select C.Name
              ,B.*
         From (
                Select *,ColNr=Row_Number() over (Partition By CustID Order By Creditor)
                 From  Debt
              ) A
         Cross Apply (values (concat(''Cred'',ColNr),[Creditor])
                            ,(concat(''Bal'' ,ColNr) ,cast(Balance as varchar(25)))
                     ) B (Item,Value)
         Join Customer C on A.CustID=C.CustID

    ) A
Pivot (max([Value]) For [Item] in (' + @SQL + ') ) p'
--Print @SQL
Exec(@SQL);

<强>返回

enter image description here

如果是Helps,则生成的SQL看起来像这样:

Select *
From (
        Select C.Name
              ,B.*
         From (
                Select *,ColNr=Row_Number() over (Partition By CustID Order By Creditor)
                 From  Debt
              ) A
         Cross Apply (values (concat('Cred',ColNr),[Creditor])
                            ,(concat('Bal' ,ColNr) ,cast(Balance as varchar(25)))
                     ) B (Item,Value)
         Join Customer C on A.CustID=C.CustID

    ) A
Pivot (max([Value]) For [Item] in ([Cred1],[Bal1],[Cred2],[Bal2],[Cred3],[Bal3],[Cred4],[Bal4]) ) p

仅针对可视化,“提供”数据透视的查询生成:

enter image description here

答案 1 :(得分:2)

我猜您已经知道如何使用交叉表,因此您只需准备好数据即可使用它。

第1步:加入两个表:

 SELECT c.Name, d.Creditor, d.Balance
 FROM Customer c
 JOIN Debt d
   ON c.CustID = d.CustID

第2步:在与您将用于跨选项卡的客户相关的每个元素中包含行号

SELECT c.Name, d.Creditor, d.Balance, 
       ROW_NUMBER() over (PARTITION BY Name ORDER BY creditor) as rndebt_tab
FROM Customer c
JOIN Debt d
  ON c.CustID = d.CustID

现在你有:

CustID   Creditor             Balance    rn
1        ABC Loans            245        1      
1        Citibank             815        2
2        Soprano Financial   74000       1
3        Citibank             24         1
3        Soprano Financial   93000       2
3        Wells Fargo          275        3
3        Midwestern S&L       2500       4
4        ABC Loans            1500       1
4        Fred's Payday Loan   1000       2

第3步:为交叉表创建SOURCE

WITH cte as (
     <query from step2>
)
SELECT Name, 
       'CREDITOR_' + RIGHT('000' + CAST(rn AS VARCHAR(3)),3) as cross_tab,
       Creditor as Value
FROM cte  
UNION all
SELECT Name, 
       'DEBT_' + RIGHT('000' + CAST(rn AS VARCHAR(3)),3) as cross_tab,
       CAST(Balance as VARCHAR(max)) as Value      
FROM cte  

现在你有:

CustID   cross_tab          Value             
1        CREDITOR_001       ABC Loans         
1        CREDITOR_002       Citibank          
2        CREDITOR_001       Soprano Financial 
3        CREDITOR_001       Citibank          
3        CREDITOR_002       Soprano Financial 
3        CREDITOR_003       Wells Fargo       
3        CREDITOR_004       Midwestern S&L    
4        CREDITOR_001       ABC Loans         
4        CREDITOR_002       Fred's Payday Loan
1        DEBT_001           245  
1        DEBT_002           815  
2        DEBT_001       `   74000
3        DEBT_001           24   
3        DEBT_002           93000
3        DEBT_003           275  
3        DEBT_004           2500 
4        DEBT_001           1500 
4        DEBT_002           1000 

编辑:我在示例中使用CustID而不是Name,但现在懒得改变。