SQL 具有未确定列的交叉表
SQL Crosstab with undetermined columns
我看到很多类似的问题,但几乎所有问题都将结果分组为列名(基于结果的列名),我的是一个更简单的列表。我不在乎它是否使用动态 SQL 或不(我认为它必须)。
请不要告诉我我需要重组 tables,我在旧系统上工作,没有那个选项。
基本上,我只需要一个列表,其中包含连续匹配 table "A" 中给定记录的所有有效 table "B" 条目。
我还没有任何代码示例,因为我没有找到正确设置它的方法。
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" 收集某种最多记录的计数,并基于此定义输出列。如果已经回答了这个问题,请随时 link 并关闭它,我在搜索时没有看到这种特定情况。
我猜你已经知道如何使用交叉表,所以你只需要准备数据就可以使用它。
第 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 步:为交叉表创建源
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
但现在懒得更改了。
这是另一种动态方法。我们使用 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);
Returns
如果有帮助,生成的 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
只是为了可视化,查询 "feeding" Pivot 生成:
我看到很多类似的问题,但几乎所有问题都将结果分组为列名(基于结果的列名),我的是一个更简单的列表。我不在乎它是否使用动态 SQL 或不(我认为它必须)。
请不要告诉我我需要重组 tables,我在旧系统上工作,没有那个选项。
基本上,我只需要一个列表,其中包含连续匹配 table "A" 中给定记录的所有有效 table "B" 条目。
我还没有任何代码示例,因为我没有找到正确设置它的方法。
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" 收集某种最多记录的计数,并基于此定义输出列。如果已经回答了这个问题,请随时 link 并关闭它,我在搜索时没有看到这种特定情况。
我猜你已经知道如何使用交叉表,所以你只需要准备数据就可以使用它。
第 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 步:为交叉表创建源
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
但现在懒得更改了。
这是另一种动态方法。我们使用 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);
Returns
如果有帮助,生成的 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
只是为了可视化,查询 "feeding" Pivot 生成: