You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#连接Access实现库存计算并绑定DataGridView问题

解决WinForms库存计算与DataGridView绑定无数据问题

我来帮你一步步排查问题,搞定库存统计和DataGridView绑定的需求:

一、先揪出现有代码的核心问题

你的代码和SQL查询存在两个关键问题,导致无数据显示:

1. SQL查询的逻辑缺陷

  • INNER JOIN的局限性:你用的INNER JOIN只会返回采购表和销售表中**同时存在相同Bcode**的记录。如果某个产品只有采购记录没有销售,或者只有销售没有采购,就会被直接过滤掉,这些产品的库存自然统计不到。
  • 分组与聚合逻辑错误:Access的SQL规则要求,SELECT子句里的非聚合列必须出现在GROUP BY中。你第一次写的查询里Purchase.InvoiceDate、Purchase.Pname既不在GROUP BY里,也没用到聚合函数(比如SUM、LAST),理论上应该报错;第二次的查询虽然加了聚合,但GROUP BY列冗余,且依然用INNER JOIN漏掉了单边数据。

2. 数据绑定代码的错误

你在while(dr.Read())循环里遍历DataGridView的现有行,但初始时DataGridView是空的,dataGridView1.Rows.Count为0,循环根本不会执行,自然看不到任何数据。


二、修正方案:正确的SQL查询+绑定逻辑

第一步:编写正确的库存统计SQL

我们需要用LEFT JOIN + UNION模拟Access不支持的FULL JOIN,确保所有有采购/销售记录的产品都能被统计,同时正确计算总采购量减总销售量:

-- 统计有采购记录的产品(含无销售的)
SELECT 
    p.Bcode,
    p.Pname,
    SUM(p.Pqty) AS TotalPurchase,
    Nz(SUM(s.Pqty), 0) AS TotalSales,
    SUM(p.Pqty) - Nz(SUM(s.Pqty), 0) AS RemainingQty,
    LAST(p.InvoiceDate) AS LatestPurchaseDate,
    LAST(s.InvoiceDate) AS LatestSalesDate
FROM Purchase p
LEFT JOIN Sales s ON p.Bcode = s.Bcode
GROUP BY p.Bcode, p.Pname

UNION

-- 补充统计只有销售记录的产品
SELECT 
    s.Bcode,
    s.Pname,
    0 AS TotalPurchase,
    SUM(s.Pqty) AS TotalSales,
    0 - SUM(s.Pqty) AS RemainingQty,
    NULL AS LatestPurchaseDate,
    LAST(s.InvoiceDate) AS LatestSalesDate
FROM Sales s
LEFT JOIN Purchase p ON s.Bcode = p.Bcode
WHERE p.Bcode IS NULL
GROUP BY s.Bcode, s.Pname
  • Nz函数用来把销售数量的NULL值转为0,避免计算时出现错误;
  • UNION部分补充了只有销售没有采购的产品,确保库存统计无遗漏。

第二步:正确的DataGridView绑定代码

推荐用DataTable绑定(更简洁易维护),代码如下:

// 假设你的数据库连接cn已正确初始化
string inventorySql = @"
    SELECT 
        p.Bcode,
        p.Pname,
        SUM(p.Pqty) AS TotalPurchase,
        Nz(SUM(s.Pqty), 0) AS TotalSales,
        SUM(p.Pqty) - Nz(SUM(s.Pqty), 0) AS RemainingQty,
        LAST(p.InvoiceDate) AS LatestPurchaseDate,
        LAST(s.InvoiceDate) AS LatestSalesDate
    FROM Purchase p
    LEFT JOIN Sales s ON p.Bcode = s.Bcode
    GROUP BY p.Bcode, p.Pname

    UNION

    SELECT 
        s.Bcode,
        s.Pname,
        0 AS TotalPurchase,
        SUM(s.Pqty) AS TotalSales,
        0 - SUM(s.Pqty) AS RemainingQty,
        NULL AS LatestPurchaseDate,
        LAST(s.InvoiceDate) AS LatestSalesDate
    FROM Sales s
    LEFT JOIN Purchase p ON s.Bcode = p.Bcode
    WHERE p.Bcode IS NULL
    GROUP BY s.Bcode, s.Pname
";

// 使用using确保资源自动释放,避免连接泄漏
using (OleDbCommand cmd = new OleDbCommand(inventorySql, cn))
{
    using (OleDbDataAdapter adapter = new OleDbDataAdapter(cmd))
    {
        DataTable inventoryTable = new DataTable();
        adapter.Fill(inventoryTable);
        
        // 绑定到DataGridView
        dataGridView1.DataSource = inventoryTable;
        
        // 自定义列名,让界面更友好
        dataGridView1.Columns["Bcode"].HeaderText = "产品编号";
        dataGridView1.Columns["Pname"].HeaderText = "产品名称";
        dataGridView1.Columns["TotalPurchase"].HeaderText = "总采购量";
        dataGridView1.Columns["TotalSales"].HeaderText = "总销售量";
        dataGridView1.Columns["RemainingQty"].HeaderText = "库存余量";
        dataGridView1.Columns["LatestPurchaseDate"].HeaderText = "最后采购日期";
        dataGridView1.Columns["LatestSalesDate"].HeaderText = "最后销售日期";
    }
}

如果一定要用DataReader手动添加行,代码如下(需确保DataGridView已添加对应列):

cn.Open();
OleDbCommand cmd = new OleDbCommand(inventorySql, cn);
OleDbDataReader dr = cmd.ExecuteReader();

// 先清空现有行
dataGridView1.Rows.Clear();

while (dr.Read())
{
    // 新增一行
    int rowIdx = dataGridView1.Rows.Add();
    // 赋值单元格,注意处理空值
    dataGridView1.Rows[rowIdx].Cells["Bcode"].Value = dr["Bcode"].ToString();
    dataGridView1.Rows[rowIdx].Cells["Pname"].Value = dr["Pname"].ToString();
    dataGridView1.Rows[rowIdx].Cells["TotalPurchase"].Value = dr["TotalPurchase"].ToString();
    dataGridView1.Rows[rowIdx].Cells["TotalSales"].Value = dr["TotalSales"].ToString();
    dataGridView1.Rows[rowIdx].Cells["RemainingQty"].Value = dr["RemainingQty"].ToString();
    dataGridView1.Rows[rowIdx].Cells["LatestPurchaseDate"].Value = dr["LatestPurchaseDate"] != DBNull.Value ? dr["LatestPurchaseDate"].ToString() : "";
    dataGridView1.Rows[rowIdx].Cells["LatestSalesDate"].Value = dr["LatestSalesDate"] != DBNull.Value ? dr["LatestSalesDate"].ToString() : "";
}

dr.Close();
cn.Close();

三、额外注意事项

  1. 数据一致性:确保同一个Bcode在采购表和销售表中对应的Pname一致,否则统计结果会出现产品名称分裂的情况;
  2. 连接安全性:始终用using包裹数据库连接、命令等对象,避免资源泄漏;
  3. 空值处理:用Nz和DBNull.Value判断处理空值,避免程序抛出异常。

按照上面的方案修改后,就能正确统计所有产品的库存并显示在DataGridView里了。

内容的提问来源于stack exchange,提问作者Joey Arnanzo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:42:10