在 SQL Server 中将行值转换为列名

Posted

技术标签:

【中文标题】在 SQL Server 中将行值转换为列名【英文标题】:Convert row values to column names in SQL Server 【发布时间】:2020-11-23 10:43:46 【问题描述】:

我无法找到我的问题的答案,几天来我一直在为这个问题头疼。

基本上我有 3 张桌子:

Receipt, Receiptline, Receiptlinedetail

为什么这 3 个的简短摘要:

收据 - 所有收据的列表(例如:收据 100) 收据行 - 收据的每一行(例如:收据 100 已用 Visa 支付) Receiptlinedetail - 收据行的详细信息(例如:NetAmount 为 50 欧元)

现在我想要对每天完成的所有付款进行横向概览。

输出应该是这样的:

Day       | Visa | Mastercard | Cash 
2020-11-30  20.00    10.00       5.00

我的数据:

receipt

ReceiptId   ReceiptNumber   ReceiptType   ReceiptDateTime      ModifiedKind
----------------------------------------------------------------------------
44919            1             100        2020-08-15 13:49:45.633   100
44920            1             200        2020-08-15 13:50:15.060   100
44921            2             100        2020-08-15 13:54:14.007   100
44922            2             300        2020-08-15 13:55:31.650   100
44923            3             100        2020-08-15 13:55:38.263   100
44924            3             300        2020-08-15 13:56:47.677   100
44925            4             100        2020-08-15 13:56:58.940   100
44926            4             200        2020-08-15 13:57:13.707   100

receiptline

ReceiptLineId   ReceiptId   LineType    ItemId  Description     Quantity    ModifiedKind
----------------------------------------------------------------------------------------
89471             44919      300          0      GlobalInfo     1.0000      100
89472             44920      100         225     Sprite         4.0000      100
89473             44920      200          3      Cash           1.0000      100
89474             44920      300          0      GlobalInfo     1.0000      100
89475             44921      300          0      GlobalInfo     1.0000      100
89476             44922      300          0      GlobalInfo     1.0000      100
89477             44923      300          0      GlobalInfo     1.0000      100
89478             44924      300          0      GlobalInfo     1.0000      100
89479             44925      300          0      GlobalInfo     1.0000      100
89480             44926      200          6      VISA           1.0000      100

receiptlinedetail:

ReceiptLineDetailId ReceiptLineId   LineType    ItemId  Description Quantity    NetAmount   ModifiedKind
---------------------------------------------------------------------------------------------------------
89493               89471             300         0      GlobalInfo 1.0000      0.00       100
89494               89472             100         225    Sprite     4.0000      8.00       100
89495               89473             200         3      Cash       1.0000      -8.00      100
89496               89474             300         0      GlobalInfo 1.0000      0.00       100
89497               89475             300         0      GlobalInfo 1.0000      0.00       100
89498               89476             300         0      GlobalInfo 1.0000      0.00       100
89499               89477             300         0      GlobalInfo 1.0000      0.00       100
89500               89478             300         0      GlobalInfo 1.0000      0.00       100
89501               89479             300         225    Sprite     1.0000      2.00       100
89502               89482             300         0      GlobalInfo 1.0000      0.00       100
89503               89480             200         6      VISA       1.0000      -2.00       100

我还有一张付款表,其中列出了所有不同的付款方式。

PaymentId   Description     PaymentType ModifiedKind
----------------------------------------------------
1           Diners Club      200        200
2           Mastercard       200        200
3           Cash             100        200
4           Mobile payment   100        200
5           Shopmaster       500        200
6           VISA             200        200

简短的总结:

收据中的所有付款都是“LineType 200”。支付方式的数量可能因数据库而异,但总是列在支付表中。

【问题讨论】:

我看不到任何将付款链接到收据的方法。你的问题需要更清楚。为什么付款表没有简单的日期或收据 ID? 我能理解您的问题,收据和付款之间的链接在 itemid 中,具体取决于 linetype 。因此,例如 payment id 6 = visa 和 itemid = 6 指的是那个。这是 POS(销售点)/收银机的数据库。因此,收据/账单可以用不同的付款方式支付,例如 50% 现金和 50% 通过 VISA。用户可以定义不同的支付方式。我希望这足够清楚 【参考方案1】:

认为链接到Payments 是通过itemId 列。剩下的只是连接和条件聚合:

select convert(date, r.ReceiptDateTime),
       sum(case when p.Description = 'Visa' then rld.net_amount else 0 end) as visa,
       sum(case when p.Description = 'MasterCard' then rld.net_amount else 0 end) as mastercard,
       sum(case when p.Description = 'Cash' then rld.net_amount else 0 end) as cash
from receipt r join
     receiptline rl
     on rl.ReceiptId = r.ReceiptId join
     receiptlinedetail rld
     on rld.ReceiptLineId = rl.ReceiptLineId join
     payments p
     on p.paymentid = rl.itemid and rl.LineType = 200
group by convert(date, r.ReceiptDateTime)

【讨论】:

我希望它这么简单,不幸的是,硬编码付款说明还不够好。因为它们不是“标准的”。可以有一个客户不使用“VISA”付款方式,或者我们可以有比现在更多的付款方式,在表格中 @Steven 。 . .这确实回答了您提出的问题。【参考方案2】:

我想到的唯一方法是使用动态 SQL 进行 PIVOTING。

试试这个:

DECLARE @columns VARCHAR(MAX) = '';
declare @sql     NVARCHAR(MAX) = '';


SELECT 
    @columns+=QUOTENAME(Description) + ','
FROM 
    payment
ORDER BY 
    Description;

SET @columns = LEFT(@columns, LEN(@columns) - 1);

SET @sql = '
select *
from(

select convert(date, r.ReceiptDateTime) as Day,
       p.Description,
       rld.netamount
    
from receipt r 
    join receiptline rl
        on rl.ReceiptId = r.ReceiptId 
    join receiptlinedetail rld
        on rld.ReceiptLineId = rl.ReceiptLineId 
    join payment p
        on p.paymentid = rl.itemid and rl.LineType = 200
) d pivot (
        sum(netamount)
        for Description in (' + @columns + ')
) as t;'

EXECUTE sp_executesql @sql;

【讨论】:

以上是关于在 SQL Server 中将行值转换为列名的主要内容,如果未能解决你的问题,请参考以下文章

如何在动态查询中将行值连接到列名

将行值转换为列名

SQL Server:在使用where子句的同时将2行值转换为1列并加入它

在 SQL Server 中将数据从一列拆分为多行

如何在 sql server 2008r2 中将行名更改为列名

动态生成列名时如何获取kendo选择的行值