【问题标题】:Changes of product price in database design数据库设计中产品价格的变化
【发布时间】:2016-08-21 03:54:10
【问题描述】:

我的 POS 数据库设计有一些问题,假设我有 products 表,其中包含列 id、product_name、价格和库存,然后是表 transactionstransaction_details,如果有人买了一些产品,我有一个交易记录和一些交易明细记录,现在我将product表中的价格值复制到transaction_details,所以如果产品价格发生变化,它们不会影响交易历史/报告,但我考虑将价格单独放入另一个表,比如说product_prices,每个产品都有很多价格,而与product_prices 相关的transaction_details 则直接指向产品本身。我的方法是更好还是更差对应于数据本身的数据完整性、性能或效率。我在products 表中有库存,是否需要或者我只是从购买交易中获取从transaction_details 中减去unit_price。谢谢你的回答。

-- -----------------------------------------------------
-- Table `mydb`.`transaction`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`transaction` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `date` DATETIME NOT NULL,
  `total` DOUBLE UNSIGNED NOT NULL,
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`products`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`products` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `product_name` VARCHAR(255) NOT NULL,
  `description` VARCHAR(1000) NULL,
  `price` DOUBLE UNSIGNED NOT NULL,
  `stocks` INT NOT NULL DEFAULT 0,
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`transaction_details`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`transaction_details` (
  `transaction_id` INT NOT NULL,
  `products_id` INT NOT NULL,
  `discount` DOUBLE NOT NULL,
  `unit_price` DOUBLE NOT NULL,
  `quantity` INT NOT NULL DEFAULT 1,
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`transaction_id`, `products_id`),
  INDEX `fk_transaction_details_products1_idx` (`products_id` ASC),
  CONSTRAINT `fk_transaction_details_transaction`
    FOREIGN KEY (`transaction_id`)
    REFERENCES `mydb`.`transaction` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_transaction_details_products1`
    FOREIGN KEY (`products_id`)
    REFERENCES `mydb`.`products` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`purchasing`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`purchasing` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `products_id` INT NOT NULL,
  `date` DATETIME NOT NULL,
  `purchasing_price` DOUBLE NOT NULL,
  `quantity` INT NOT NULL,
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`, `products_id`),
  INDEX `fk_purchasing_products1_idx` (`products_id` ASC),
  CONSTRAINT `fk_purchasing_products1`
    FOREIGN KEY (`products_id`)
    REFERENCES `mydb`.`products` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

【问题讨论】:

  • 如果您希望能够提出诸如“ 的价格是多少”之类的问题,那么您的设计似乎是合理的。否则它可能会变得不必要地复杂。
  • hmm 所以如果我将产品表中的价格复制到 transaction_details 就可以了,感谢向我保证,是的,我考虑跟踪产品销售数据的未来要求,特别是特定价格的最高销售统计期间。
  • 我推荐一个订单明细表,它可以捕获当时交易的价格。如果你发布一个实际的模式,我可以反对它(意味着优点和缺点)
  • @Drew 我有更新可能会质疑包含简单 ERD 的图像
  • 能否提供文字show create table eachTable

标签: mysql database database-design


【解决方案1】:

我知道您似乎通过将价格保留在交易表上来对价格进行非规范化,并且这种非规范化感觉“不好”,因为我们喜欢遵循最佳做法。但是,在这里考虑您的问题的更好方法是,在这种情况下,价格没有被非规范化。

您的产品表(或产品历史表)上的价格在语义上与您的交易表中的价格不同。

一个是建议零售价(即“应该”的价格),一个是支付的(“实际”)价格。是的,这些大部分时间都是一样的,但这是巧合。

无论您是否将价格保存在历史表中,您都应该将实际支付的价格保存在交易表中。这样做的原因是事务表是实际发生的事情的记录。在某种程度上,它是一种一次写入的日志。如果您可以证明实际支付的价格不能在以后根据您的代码的工作方式重述,那么审核员会更喜欢它。在传统的会计系统中,甚至更正都是使用冲销交易而不是编辑来应用的。

要考虑的另一件事是价格可能有例外。例如,如果您决定使用优惠券进行促销怎么办?或者,如果您为“开箱”商品提供 20% 的折扣?这种一次性价格很难在价格历史表中跟踪。

由于这些原因,将实际支付的价格保留在事务表中是一个有效的设计决定,而不仅仅是性能或代码简单性的权宜之计。

【讨论】:

  • 用真实世界的例子做了一个很好的解释,谢谢@joel-bworn,所以从产品表中获取价格并将其保存到交易细节中是完全没有错误的。
【解决方案2】:

我们学到了很多东西......您可能会想到的是,除了产品详细信息本身和交易详细信息(或基本上是历史记录)之外,您在哪里需要价格。如果您有一些关于产品定价的统计数据,或者您的产品价格可能会随着时间的变化而波动很大(很好的例子是货币交易),那么需要价格历史记录。但如果不是这种情况,您可能应该选择分开的产品和交易细节。否则,您将减慢整个系统的速度,而不是每笔交易 1 个重复(在大多数情况下)价值。我希望这是有道理的......

更新 #1

但是股票需要不同的方法,但要记住同样的事情:始终尽可能少地计算和加载数据。如果您想共同计算一些值,请为此创建 Cron 脚本并将结果存储在 db 中,因为在大多数情况下,从数据库加载比在用户请求中从历史记录中计算诸如库存之类的东西要快。

另外请记住,产品可以有不同的颜色/尺寸/其他属性,您绝对不希望将 3 种颜色和 5 种尺寸的相同 T 恤作为 3*5=15 种不同的产品。 因此,您需要在某处存储组合和属性,并在每个组合上都有 productId(或者作为优化的一部分,您将在组合中存储比在 prodct 表中更多的数据,以便从一个表中选择而不是连接,但这是另一个问题) .

现在,每种颜色/尺寸都有不同的库存,因此每个属性组合都有您的库存价值。 我在自己的结构中唯一的问题是我是否想将库存存储在产品表中(如果产品没有变化,我可以通过分配“默认”组合轻松标准化结构),或者我应该直接在组合表中存储组合的库存并制作 cron 来计算所有组合的总库存并更新产品中的库存。同样,这完全取决于您的数据使用情况,就我而言,tbh 我想不出任何地方我需要类似于商店中的产品库存的东西(不要犯错,有很多不同的类似产品)。

更新 #2

不要忘记未来的代码扩展。我从来没有见过一个项目没有在以后甚至公开发布之后实现新功能。因此,您不仅应该优化数据,还应该为新模块做好准备,并足够灵活以保持速度。不好的例子是我们当前的 CMS 有产品,然后有人添加了缓存表,其中只包含产品详细信息的重要数据,但后来其他人发现产品需要更多的变量,所以他们返回了产品表。但是这已经不够了,因为缓存包含产品没有的重要缓存数据,所以它们完全连接了东西,现在我们没有优化缓存加载,而是在其中实现了慢速 LEFT JOIN 的三重连接......

【讨论】:

  • 所以基本上如果我在真正需要之前不需要价格历史表,例如直到我的客户要求我提供能够跟踪价格的功能。谈论重复如何数学运算这样的股票,我需要放在products 表上还是只计算程序从购买历史运行时 - 从开始到今天的数据的交易(进出)。
  • 嗯,股票要复杂得多。在某些情况下,您必须考虑组合相同产品的可能性。衬衫有不同的尺码,但您不会为每个尺码都创建新的衬衫产品,对吧?但话又说回来,每种尺寸都有不同的库存,因此它们需要不同的值。处理它的方法很少,坦率地说,我不能说 100% 确定哪个更好(我目前正在为公司的 CMS 开发新架构)。您绝对不应该每次都计算价格,让我为您编辑答案...
  • @AnggaAriWijaya 我更新了我的答案,是你要找的吗?
  • 我得到了这样的答案,“所以如果我对股票有简单的设计,我只需要更新products 表上的股票字段,取决于交易(进出)?”。我记得在阅读您的回答后我经常遇到的另一个问题,是的,如果我们有不同的产品,单位计量不同,例如件、公斤、米,库存会变得更加复杂。
  • 没错,因此您需要 +1 键 - unitId,其中包含对带有单位描述的单位表的引用。我计划在下一个 cms 的结构中做的是让整个标准化结构具有适当的 sql 标准化和 1 个位于主结构之外的表,其中包含预先计算的值。我会在几个小时内执行一次 cron 脚本来重新计算每个产品的正确缓存。但话又说回来,我不太确定服务器负载,前面有很多测试......
猜你喜欢
  • 2022-01-25
  • 2020-01-27
  • 2011-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多