数据库设计中产品价格的变化

时间:2016-08-21 03:54:11

标签: mysql database database-design

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

database design image

-- -----------------------------------------------------
-- 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;

2 个答案:

答案 0 :(得分:3)

我知道看起来你通过将价格保留在交易表上来反价格化,而非正规化感觉“糟糕”,因为我们喜欢遵循最佳实践。但是,在这里考虑问题的更好方法是在这种情况下价格被非规范化。

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

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

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

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

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

答案 1 :(得分:2)

我们学得很难......你可能会想到的是除了产品细节本身和转换细节(或基本上是历史)之外你还需要什么价格。如果您有关于产品定价的一些统计数据,或者您的产品价格可能会根据时间而变化很多(很好的例子是货币交易),那么价格历史就是必需的。但如果情况并非如此,那么您可能应该选择分离的产品和交易细节。否则,您将减慢整个系统的速度,而不是每个事务的1个重复(在大多数情况下)值。我希望它有意义......

更新#1

但是库存需要采用不同的方法,并且要记住:始终计算和加载尽可能少的数据。如果你想要计算一些值,为它创建Cron脚本并将结果存储在db中,因为从数据库加载在大多数情况下比从用户请求中的历史记录计算库存等内容更快。

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

现在,每种颜色/尺寸都有不同的库存,因此每个属性组合都有您的库存价值。 我自己的结构中唯一的问题是我是否想在产品表中存储库存(我可以通过分配"默认"产品没有变量的组合来轻松规范化结构),或者我应该存储库存直接在组合表中组合并制作cron,计算所有组合的总库存并更新产品中的库存。同样,这一切都取决于你的数据使用情况,在我的情况下,我不能想到任何地方我需要类似于商店里的产品库存与衣服(并没有犯错,有很多不同的类似产品)

更新#2

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