将小计插入动态生成的 DataTable
Posted
技术标签:
【中文标题】将小计插入动态生成的 DataTable【英文标题】:Inserting subtotals to my dynamically generated DataTable 【发布时间】:2021-12-23 04:28:53 【问题描述】:我在使用 Dapper 创建的 .Net 核心应用程序中有一个 SQL Server 临时表。然后我对此进行旋转,动态生成一个 DataTable,然后我用它为用户导出到 Excel。
我要添加的是小计行,但无法解决。这是我的代码的简单演示表示以及我希望实现的目标:
IF OBJECT_ID('tempdb..#t2') IS NOT NULL
DROP TABLE #t2
create table #t2 (
Client varchar(50),
TradeId varchar(50),
BusDate datetime,
ExpiryDate datetime,
Amount int
)
insert into #t2 values ('clientA', 'A0124', '20211110', '20211113', -400)
insert into #t2 values ('clientA', 'A0124', '20211111', '20211113', -400)
insert into #t2 values ('clientA', 'A0124', '20211112', '20211113', -400)
insert into #t2 values ('clientA', 'A0124', '20211113', '20211113', -400)
insert into #t2 values ('clientA', 'A0125', '20211110', '20211113', 250)
insert into #t2 values ('clientA', 'A0125', '20211111', '20211113', 250)
insert into #t2 values ('clientA', 'A0125', '20211112', '20211113', 250)
insert into #t2 values ('clientA', 'A0125', '20211113', '20211113', 250)
insert into #t2 values ('clientB', 'B0125', '20211110', '20211112', 100)
insert into #t2 values ('clientB', 'B0125', '20211111', '20211112', 100)
insert into #t2 values ('clientB', 'B0125', '20211112', '20211112', 100)
insert into #t2 values ('clientC', 'C0125', '20211110', '20211111', -500)
insert into #t2 values ('clientC', 'C0125', '20211111', '20211111', -500)
结果:
下一阶段展示了我向用户展示数据的关键:
-- pivot to create dynamic datatable for further exporting to Excel
DECLARE @cols2 AS NVARCHAR(MAX),
@query2 AS NVARCHAR(MAX)
select @cols2 = STUFF((SELECT ',' + QUOTENAME(CobDate)
from #t2
group by CobDate
order by CobDate asc
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query2 = N'SELECT CPartyName, SourceSystemTradeID, ' + @cols2 + N' from
(
select CpartyName, SourceSystemTradeID, CobDate, TradeMaturityDate, NotionalFX
from #t2
) x
pivot
(
max(NotionalFX)
for CobDate in (' + @cols2 + N')
) p '
exec sp_executesql @query2;
结果:
所以我试图在每个客户的行下插入一个小计行,例如在这个演示中,这一行将在两个 clientA 行下方:
我已尝试将此逻辑添加到 sql 中的数据透视查询中,还尝试使用以下不正确的 C# 方法修改 DataTable,但没有解决:
private void InsertSubTotals(ref DataTable dt)
DataRow totalsRow = dt.NewRow();
foreach (DataColumn col in dt.Columns)
if (col.ColumnName != "Client" && col.ColumnName != "TradeId")
var colTotal = 0;
foreach (DataRow row in col.Table.Rows)
var t = int.Parse(row[col].ToString());
colTotal += t;
totalsRow[col.ColumnName] = colTotal;
dt.Rows.Add(totalsRow);
非常感谢任何帮助。
【问题讨论】:
你的InsertSubTotals
方法有什么问题?
我冒昧地建议,在 Excel 中旋转它可能比在 SQL Server 中更容易,因为 SQL Server 喜欢修复它的列
【参考方案1】:
我不确定我是否按照表格的外观。但是,对于任何列,如果它是数字列,则可能不需要遍历行。 DataTable
有一个 Compute
方法,它将为您提供 SUM
列。因此,您可以将代码更改为...
private void InsertSubTotals(DataTable dt)
DataRow totalsRow = dt.NewRow();
totalsRow["ColumnName1"] = Convert.ToInt32(table.Compute("SUM(ColumnName1)", ""));
totalsRow["ColumnName2"] = Convert.ToInt32(table.Compute("SUM(ColumnName2)", ""));
//....
dt.Rows.Add(totalsRow);
当然,如果有很多列要求和,那么您可能需要使用循环。
还应注意,Compute
函数中的第二个参数是“过滤器”。为空时,它将列中的所有单元格相加。但是,您也可以“过滤”总和以仅应用于某些单元格。例如,只有“clientA”客户端的总和可能看起来像……
totalsRow["ColumnName1"] = Convert.ToInt32(table.Compute("SUM(ColumnName1)", "Client = ‘clientA’"));
【讨论】:
【参考方案2】:使用显式条件聚合比使用PIVOT
语法要容易得多。
然后您可以使用GROUPING SETS
指定您想要的确切汇总。
DECLARE @cols2 AS NVARCHAR(MAX) = (
SELECT DISTINCT
', ' + QUOTENAME(CobDate) + ' = MAX(CASE WHEN CobDate = ' + QUOTENAME(CobDate, '''') + ' THEN NotionalFX END)'
from #t2
group by CobDate
order by CobDate asc
FOR XML PATH(''), TYPE
).value('text()[1]', 'NVARCHAR(MAX)');
DECLARE @query2 AS NVARCHAR(MAX) = N'
SELECT
CPartyName,
SourceSystemTradeID
' + @cols2 + N'
from #t2
GROUP BY GROUPING SETS (
(CpartyName, SourceSystemTradeID),
(CpartyName)
);
';
PRINT @query2; --for testing
exec sp_executesql @query2;
不过我想说,在 Excel 中做这种事情要容易得多。 SQL Server 喜欢它的列具有固定的名称。
【讨论】:
以上是关于将小计插入动态生成的 DataTable的主要内容,如果未能解决你的问题,请参考以下文章
将 jQuery DataTable 插件应用于从 ASP.NET 动态生成的表