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

如何在SQL单表中存储含不同商品数的发票(C#库存系统)

以发票号为核心在单个SQL表存储发票详情的方案

嗨,我来帮你搞定这个发票存储的问题~首先得明确:如果要把所有发票详情塞进单个SQL表,直接用发票号当唯一主键是行不通的——因为一张发票对应多个商品,同一发票号会有多条记录,主键冲突是必然的。下面给你具体的实现方案,还有一些需要注意的点:

1. 单个表的设计思路

我们需要用复合主键来解决同一发票多商品的问题,也就是把「发票号」+「行项目编号」组合起来作为唯一主键,这样既保留了发票号的核心标识作用,又能区分同一张发票里的不同商品。

推荐的表结构字段

这里列出你大概率会用到的字段,你可以根据自己的业务调整:

  • InvoiceNo (VARCHAR/CHAR):发票号,复合主键的一部分,比如INV-20240501-001
  • LineItemSeq (INT):行项目序号,复合主键的另一部分,比如1、2、3,用来标记同一张发票里的第几个商品
  • ProductID (INT/VARCHAR):商品ID,关联你的库存商品表(如果有的话)
  • ProductName (VARCHAR):商品名称,虽然冗余但单个表需要直接查看
  • Quantity (DECIMAL):商品数量
  • UnitPrice (DECIMAL):商品单价
  • LineTotal (DECIMAL):该行商品的总价(可以计算后存储,或者查询时计算)
  • InvoiceDate (DATETIME):开票日期
  • CustomerName (VARCHAR):客户名称
  • TotalAmount (DECIMAL):整张发票的总金额(可选,也可以通过SUM(LineTotal)计算)

SQL建表语句示例

CREATE TABLE InvoiceDetails (
    InvoiceNo VARCHAR(20) NOT NULL,
    LineItemSeq INT NOT NULL,
    ProductID INT NOT NULL,
    ProductName VARCHAR(100) NOT NULL,
    Quantity DECIMAL(10,2) NOT NULL,
    UnitPrice DECIMAL(10,2) NOT NULL,
    LineTotal DECIMAL(10,2) NOT NULL,
    InvoiceDate DATETIME NOT NULL,
    CustomerName VARCHAR(100) NOT NULL,
    TotalAmount DECIMAL(12,2) NOT NULL,
    -- 设置复合主键
    PRIMARY KEY (InvoiceNo, LineItemSeq)
);

C#实体类示例

对应上面的表,你可以创建这样的实体类(假设用EF Core):

public class InvoiceDetail
{
    [Key]
    [Column(Order = 0)]
    public string InvoiceNo { get; set; }
    
    [Key]
    [Column(Order = 1)]
    public int LineItemSeq { get; set; }
    
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public decimal Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal LineTotal { get; set; }
    public DateTime InvoiceDate { get; set; }
    public string CustomerName { get; set; }
    public decimal TotalAmount { get; set; }
}

2. 单个表的局限性(必看)

虽然单个表能满足你的需求,但它不符合数据库规范化设计,会带来这些问题:

  • 数据冗余:同一张发票的开票日期、客户名称、总金额会重复存储N次(N是商品数量),浪费存储空间
  • 维护困难:如果需要修改客户名称或者开票日期,你得更新该发票下的所有行记录,容易出错
  • 性能问题:当发票数量和商品数量都很大时,查询和更新的效率会下降

3. 更优的规范化设计(建议参考)

如果后续业务扩展,我强烈建议拆成两张表,这是行业通用的做法:

  • 发票主表(Invoices):以InvoiceNo为唯一主键,存储发票的整体信息(开票日期、客户信息、总金额等)
  • 发票明细表(InvoiceLineItems):以InvoiceNo+LineItemSeq为复合主键,存储每个商品的详情,通过InvoiceNo关联主表

这样既避免了冗余,又方便维护和查询,C#代码里也可以通过EF Core的关联关系轻松实现主从表的操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:58:53