【问题标题】:Determining who owns a transferrable item in MySQL确定谁拥有 MySQL 中的可转让项目
【发布时间】:2015-07-21 19:42:34
【问题描述】:

我正在开发一款游戏,用户可以使用虚拟货币购买虚拟物品,并将这些物品赠送给其他用户。我们需要能够跟踪物品的历史(谁购买了它,它被赠送给了谁,等等)和当前所有者。我正在为一些表格设计而苦苦挣扎。

我的想法是有这个(表格设计稍微简化,约束/键/等省略空间):

TABLE order
id                 INT NOT NULL AUTO_INCREMENT,
buyer_id           INT NOT NULL,
paid               INT UNSIGNED NOT NULL,
owned_item_id     INT NOT NULL,
------------------------------------------------------
TABLE owned_item
id                 INT NOT NULL AUTO_INCREMENT,
store_product_id   INT NOT NULL,
owner_id           INT NOT NULL,
------------------------------------------------------
TABLE gift
id                 INT NOT NULL,
giver_id           INT NOT NULL,
receiver_id        INT NOT NULL,
owned_item_id      INT NOT NULL,

我们的想法是,在购买商品时,会同时为该商品创建 orderowned_item。如果该项目是赠品,则在 gift 表中创建一个新条目并更新 owner_id 字段。

这种方法可以很容易地确定谁当前拥有给定的项目。 但是,它具有冗余数据,并为数据完整性问题留下了空间。如果“owner_id”字段设置不正确,我们最终可能会记录到一件物品由 A 购买并赠送给 B,但现在却莫名其妙地归 C 所有。

这样的结构应该如何规范化?我考虑过删除 owned_item 表:

TABLE order
id                 INT NOT NULL AUTO_INCREMENT,
buyer_id           INT NOT NULL,
paid               INT UNSIGNED NOT NULL,
product_id         INT NOT NULL,
------------------------------------------------------
TABLE gift
id                 INT NOT NULL,
giver_id           INT NOT NULL,
receiver_id        INT NOT NULL,
order_id           INT NOT NULL,

我不喜欢这个解决方案,因为查找某个人拥有的所有物品是一项非常复杂的操作(查找收件人为 A 的每个礼物记录 X,并且同一订单不存在以后的礼物记录,结合 find每个订单记录 Y,其中买家是 A,并且该订单不存在礼品记录)但如果这是正确的解决方案,那么我会做的。

【问题讨论】:

  • 有上百万种方法可以解决这个问题,但首先想到的两种方法是:1) 创建一个跟踪事务的表。该结构可能类似于:ItemId、CurrentOwnerId、Ordinal(这是允许您维护事件顺序的列)、TransactionType。想到的另一个想法是事件溯源的概念。设置可能有点多,但审计潜力令人难以置信
  • 可以合并订单表和礼品表吗?这减少了一些冗余。订单是将物品从 null 转移到用户,成本不为零,而礼物只是从用户到用户的转移,成本为零。
  • 如果转移表可以工作,转移表和所有权表之间还有一些冗余,但为了效率,我认为这是可以接受的。您必须在每个只需要确定项目的当前所有者的查询中添加额外的子句,这可能会耗费时间。
  • FWIW 我认为这个问题还可以。我们确实在这里遇到了很多过于宽泛的问题,因此有时会遇到一些边缘案例。我会投票重新开放(我们会看看其他社区成员的想法,我不能保证它会真正开放)。
  • @halfer :谢谢,这是一个有趣的解决方案,但如果提供者和接收者都非空且成本非零,则它允许无效数据。我们的系统只允许玩家赠送物品,不允许出售

标签: mysql sql


【解决方案1】:

对于您想要做的事情,这样的 3nf 架构将是一个不错的选择。

实体和交易保持通用,以便简化所有实体/交易关系。

-- An Entity is a person or business, as a party in a transaction
CREATE TABLE entity (
id                  INT NOT NULL AUTO_INCREMENT,
entity_type         ENUM('store', 'person') NOT NULL
name                VARCHAR NOT NULL
);

-- Unique item types - items are each an instance of an item-type
-- e.g. the item "Steve's broom" may be an item of type "broom"
CREATE TABLE item_type (
id                  INT NOT NULL AUTO_INCREMENT,
name                VARCHAR NOT NULL
);

-- Non-unique item, instance of an item-type owned by an entity
CREATE TABLE item (
id                  INT NOT NULL AUTO_INCREMENT,
-- optionally include owner_id as a quick-reference to the current owner of the item
owner_id            INT NULL REFERENCES entity (id), 
-- FK to unique item types, e.g. "broom"
item_type_id        INT NOT NULL REFERENCES item_type (id), 
-- possible description, e.g. "Steve's broom"
description         VARCHAR NOT NULL 
);

-- A transaction is a sale, gift, or other method of transferrence
-- of an item between entities.  Transaction is a bad name, because
-- it's a reserved word.  That's why it's encased in ticks.
-- You'd probably be better off choosing a different generic name
CREATE TABLE `transaction` (
id
-- transaction_type can be NULL in cases of origination with entity
transaction_type    ENUM('sale', 'gift') NULL,
-- NULL in cases of origination with an entity
from_entity_id      INT NULL REFERENCES entity (id), 
to_entity_id        INT NOT NULL REFERENCES entity (id),
-- amount can be 0 in cases of gifts
amount              DECIMAL(9,2) UNSIGNED NOT NULL DEFAULT 0
);

“礼物”交易的金额为 0(或 NULL,如果您想让它可以为空)。

“来源”交易(例如,某物已完成或已找到)将没有 transaction_type,并且金额为 0。

要知道项目的当前所有者是谁,请使用在事务表中检索该项目的最后一个“to_entity_id”的视图,例如:

SELECT 
    e.name
FROM entity AS e
INNER JOIN `transaction` AS t ON e.id = t.to_entity_id
INNER JOIN
    (SELECT MAX(id) AS id
    FROM `transaction`
    WHERE item_id = 5) AS tx ON tx.id = t.id

或者,您也可以将 owner_id 存储在 item 表中(请参阅上面架构中的注释)。这有点多余,并且需要在每个事务上更新该表,但会节省大量昂贵的查询来了解谁拥有什么。

【讨论】:

    猜你喜欢
    • 2015-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多