将小计插入动态生成的 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 动态生成的表

根据dataTable动态生成grid++report 的方法

在反应中从json数据动态生成列表

如何在 Codeigniter 中插入动态数据?

即时生成水晶报表

SqlDataReader生成动态Lambda表达式