2

我正在尝试设计一个表格来帮助我确定许可证是否已过期。我想要更好的方法来构建表格的建议。

具体来说,我想做以下三件事:

  • 我想知道许可证是否存在
  • 我想知道许可证是否过期
  • 如果许可证尚未过期,我想延长许可证

我想出了下表。当用户许可证延期时,该用户许可证的第一个条目被标记为过期。我认为这种方法很好,因为如果出现某种问题,我还有历史可循。

----------------------------------------------------------------------------
|   user_id   |   licence   |    start    |    stop          |   expired   |
----------------------------------------------------------------------------
|     22      |    other    |   03JUL2010 |    03JUL2012     |    true     | 
|     55      |  commercial |   03JUL2012 |    03JUL2014     |    true     | <= marked as expired because it was extended.
|     55      |  commercial |   04JUL2012 |    03JUL2016     |    false    | 
----------------------------------------------------------------------------

注意: 04JUL2012 显示许可证延长的日期。

这看起来是个好方法吗?你有什么要改变的吗?

4

2 回答 2

1

您将相同的记录存储两次。相反,您可以寻求更好的设计

表格1

user-id  |  license  |  start | stop | expired | extended

表2

prim_key | user_id | extended_date | extended_date_expiry

在 table1 中扩展列是一个布尔值 true 或 false。

如果extended 为真,那么您可以在table2 中搜索用户扩展至的日期。

在 table2 中,您可以为同一用户存储多个延长日期并获取历史记录。它延长到的最高日期将是该 user_id 的有效日期。

于 2012-07-04T05:18:56.937 回答
1

我将有三个表,因为用户和许可证之间似乎存在 N:M(多对多关系):

users
------------
user_id [PK]
...


users_has_licenses
----------------------
user_id [PK] (references users.user_id)
license_id [PK] (references licenses.license_id)
issued [PK]
expire_date


licenses
-------------------
license_id [PK]
...

表格userslicenses相当简单。它们将用户和许可证固有的信息存储为独立实体。

users_has_licenses交叉引用表中,行在三个字段中是唯一的:user_idlicense_idissuedissued是一个日期时间字段,表示许可证的颁发日期或开始日期。由于用户可以多次续订许可证,并且您需要保留许可证续订的历史记录,因此我们将此字段作为复合主键的一部分。

您可以不使用expired字段,因为您已经可以从数据本身判断它是否已过期,并且您不必进行例行更新以使数据保持最新。例如,要查找用户的过期许可证,您只需执行以下查询:

SELECT
    a.license_id
FROM
    (
        SELECT user_id, license_id, MAX(issued) AS issued
        FROM users_has_licenses
        WHERE user_id = <user_id here>
        GROUP BY user_id, license_id
    ) a
INNER JOIN
    users_has_licenses b ON
        a.user_id = b.user_id AND
        a.license_id = b.license_id AND
        a.issued = b.issued AND
        b.expire_date < NOW()   

这比您预期的要忙一些,但这只是因为您保留了过去许可证续订的数据。在这种情况下,您必须按组进行最大值,以确保您获得最新的许可期限。第一个子选择找出每个特定用户的最新许可证,然后在expire_date已经通过的条件下加入。

如果您想获取所有用户的许可证(最近一段时间),无论它们是否已过期,并且仍想知道它们是否已过期,只需将 更改INNER JOIN为 a LEFT JOIN,所有未过期的许可证都将具有NULL值连接的表。

要更新用户的许可证,您只需要一个INSERT而不是一个INSERT 一个UPDATE,就像您当前的设计一样:

INSERT INTO users_has_licenses VALUES (<user_id here>, <license_id here>, NOW(), NOW() + INTERVAL 4 YEAR)

编辑:解决提问者对此答案的评论:

我还有一个关于更新许可证的问题。如果用户在第一个许可证 6 个月后续订许可证,您将如何在上面列出的 INSERT INTO 语句中包含额外的时间?我认为最后一个值需要是 now() +remaining time + expiry period(或类似的东西)。除非我误解了什么。我只是不确定如何获得剩余时间。

假设您选择不保留过去许可期的历史记录,您可以UPDATE对当前记录执行以下操作:

UPDATE users_has_licenses a SET
    a.issued = NOW(),
    a.expire_date = (
        SELECT 
            NOW() 
            + INTERVAL CASE WHEN DATEDIFF(expire_date, NOW()) < 0 THEN 0 ELSE DATEDIFF(expire_date, NOW()) END DAY 
            + INTERVAL 4 YEAR 
        FROM 
            users_has_licenses 
        WHERE 
            user_id = a.user_id AND 
            license_id = a.license_id
    )
WHERE
    a.user_id = <user_id here> AND
    a.license_id = <license_id here>

这将更新issued为当前日期和expire_date当前日期+旧期限的剩余天数+常规到期期限(无论它可能是什么)。如果过去的许可证已经过期,那么剩余天数将为负数,在这种情况下,我们只需添加常规过期时间。

作为关于我发布到原始问题的上述架构的旁注,您不再需要将issued其作为主键的一部分。

于 2012-07-04T05:40:29.737 回答