C#如何让DataSet1仅展示单个客户的报表数据
解决ReportViewer展示多客户账户明细的问题
我看了你的代码,问题核心是SQL查询使用了CROSS JOIN且未正确关联交易表与账户表的账号字段,另外还存在严重的SQL注入风险,我来一步步帮你修正:
1. 修复关联查询逻辑
在GenerateBankStatement方法中,你用了CROSS JOIN,这会让transactions表中所有符合日期条件的记录,都和account_info中选中的账号记录做笛卡尔积——哪怕这些交易根本不属于该客户。正确的做法是用INNER JOIN关联两张表的accountNo字段,确保只返回该客户的交易记录。
2. 替换字符串拼接为参数化查询
直接拼接用户输入的账号和日期到SQL语句中,会导致SQL注入漏洞,同时也可能因为日期格式问题引发错误。改用参数化查询可以同时解决这两个问题。
修改后的完整代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.Reporting.WinForms; namespace TmpZ { public partial class BalanceSheet : Form { string constring = ConfigurationManager.ConnectionStrings["ConnData"].ConnectionString; public BalanceSheet() { InitializeComponent(); } private void BalanceSheet_Load(object sender, EventArgs e) { } private void reportViewer1_Load(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(accountNo1.Text)) { MessageBox.Show("Please Enter Account Number"); return; } // 验证日期格式是否合法 if (!DateTime.TryParse(dateFrom.Text, out DateTime dateFromVal) || !DateTime.TryParse(dateTo.Text, out DateTime dateToVal)) { MessageBox.Show("Please Enter Valid Date Format"); return; } DataTable dtb = GenerateBankStatement(dateFromVal, dateToVal, accountNo1.Text); DataTable dtb2 = GetAddressInfo(accountNo1.Text); reportViewer1.LocalReport.DataSources.Clear(); ReportDataSource rpd = new ReportDataSource("DataSet1", dtb); ReportDataSource rpd2 = new ReportDataSource("DataSet2", dtb2); reportViewer1.LocalReport.DataSources.Add(rpd); reportViewer1.LocalReport.DataSources.Add(rpd2); reportViewer1.RefreshReport(); } private DataTable GetAddressInfo(string accountNo) { DataTable dt = new DataTable(); using (SqlConnection cn = new SqlConnection(constring)) { try { string sql = "select fullname as [fullname], accountNo as [accountNo], ccy as [ccy] from account_info where accountNo = @AccountNo"; SqlDataAdapter da = new SqlDataAdapter(sql, cn); da.SelectCommand.Parameters.AddWithValue("@AccountNo", accountNo); da.Fill(dt); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } return dt; } private DataTable GenerateBankStatement(DateTime dateFrom, DateTime dateTo, string accountNo) { DataTable dt = new DataTable(); using (SqlConnection cn = new SqlConnection(constring)) { try { // 使用INNER JOIN关联交易表与账户表的账号字段 string sql = @"SELECT ai.fullname as [fullname], ai.accountNo as [accountNo], ai.ccy as [ccy], ai.address as [address], t.id as [id], t.transaction_desc as [transaction_desc], t.credit as [credit], t.debit as [debit], t.balance as [balance], t.transaction_date as [transaction_date] FROM transactions t INNER JOIN account_info ai ON t.accountNo = ai.accountNo WHERE ai.accountNo = @AccountNo AND t.transaction_date BETWEEN @DateFrom AND @DateTo"; SqlDataAdapter da = new SqlDataAdapter(sql, cn); // 添加参数,避免SQL注入和日期格式问题 da.SelectCommand.Parameters.AddWithValue("@AccountNo", accountNo); da.SelectCommand.Parameters.AddWithValue("@DateFrom", dateFrom); da.SelectCommand.Parameters.AddWithValue("@DateTo", dateTo); da.Fill(dt); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } return dt; } } }
关键修改点说明
- 替换CROSS JOIN为INNER JOIN:通过
ON t.accountNo = ai.accountNo确保只有属于该客户的交易才会被关联查询出来,彻底解决多客户明细混显的问题。 - 参数化查询:用
@AccountNo、@DateFrom、@DateTo代替字符串拼接,彻底避免SQL注入风险,同时无需手动转换日期格式,适配数据库的日期处理逻辑。 - 增加输入验证:在按钮点击事件中先验证账号是否为空、日期格式是否正确,提前拦截无效输入,提升用户体验和代码健壮性。
- 优化方法参数:将需要的参数直接传入方法,避免方法直接依赖UI控件,让代码逻辑更清晰、更易维护。
这样修改后,输入客户账号时就只会展示该客户在指定日期范围内的账户报表数据了。
内容的提问来源于stack exchange,提问作者TimTim
相关产品推荐
相关产品推荐

