如何将项目显示为 'in transit' 而不是特定位置 ID(外键)?

How to display item as 'in transit' instead of to specific location id (foreign key)?

我对项目管理有以下要求。

  1. 可以将项目从位置 'A' 移动到 'B'。稍后它也可以从 'B' 移动到 'C' 位置。
  2. 应为每个项目维护历史记录以显示特定时期的位置明智的项目,可以显示项目明智的历史记录。
  3. 我还需要在特定日期显示项目 'in transit'。

数据库设计如下:

item_master
-----------
- ItemId
- Item name 
- etc...

item_location_history
------------------
- ItemId
- LocationId (foreign key of location_master)
- Date

在传输项目时,我想按以下方式插入数据:
1. 在运输时,我想输入要在特定日期从位置 'A' 移动到 'In Transit' 的项目。因为有可能该项目会在几天内保持 'in transit' 状态。
2. 在位置 'B' 接收时,我想插入要在特定日期从 'In Transit' 移动到位置 'B' 的项目,依此类推。

这样我就可以跟踪 'In Transit' 状态和物品位置。

实现此目标的最佳方法是什么?我需要对上述架构进行哪些更改?谢谢

您可以将 in_transit 的布尔字段添加到 item_location_history,这样当它从位置 A 移动到位置 B 时,您可以将 LocationId 设置为位置 B(这样您就知道它在哪里going) 但是当它实际到达时,您将 LocationId 记录为 LocationB 但 in_transit 为 false 的另一行。这样你也知道它什么时候到达。

如果您在 "in transit" 时不需要知道它的前进方向,那么您只需添加 "In Transit" 作为位置并保持架构不变。在过去的库存应用程序中,我尽可能地为每辆卡车设置一个位置,以便我们知道该物品所在的特定卡车。

我多年来采用的技术之一是规范化实体 table 的过渡属性(数量、状态、位置等)。如果你也想追溯历史,就对后续状态进行版本化(versionize?)table.

create table ItemLocation(
    ItemID     int,
    Effective  date,
    LocationID int,
    Remarks    varchar( 256 ),
    constraint PK_ItemLocation primary key( ItemID, Effective ),
    constraint FK_ItemLocation_Item foreign key( ItemID )
        references Items( ID ),
    constraint FK_ItemLocation_Location foreign key( LocationID )
        references Locations( ID )
);

有几个不错的设计选项,我展示了最简单的,其中隐含了 "In transit"。考虑以下数据:

ItemID  Effective  LocationID   Remarks
======  =========  ==========   ===============================
  1001  2015-04-01         15   In location 15
  1001  2015-04-02       NULL   In Transit [to location xx]
  1001  2015-04-05         17   In location 17

项目 1001 在到达位置 15 时出现在数据库中,它在那里停留了一整天。第二天它被移除并运送。三天后它到达位置 17,至今仍在那里。

暗示的意思通常不受欢迎,而且确实很容易过头。如果需要,您可以添加一个实际状态字段以包含 "In location" 和 "In Transit" 值。如果您认为以后可能会添加其他状态值(QA 测试、接收、搁置等),您可能会考虑这样的课程。但是对于两个可能的值,In Location 或 In Transit,隐含的含义有效。

无论如何,您可以通过获取具有最新生效日期的 LocationID 来了解任何物品的当前下落。您还拥有该项目在任何日期的位置的历史记录 - 两者都可以通过相同的查询获得。

declare AsOf date = sysdate;

select  i.*, il.Effective, IfNull( l.LocationName, 'In Transit' ) as Location
from    Items i
join    ItemLocation il
    on  il.ItemID = i.ID
    and il.Effective =(
        select  Max( Effective )
        from    ItemLocation
        where   ItemID = il.ItemID
            and Effective <= AsOf )
left join Locations  l
    on  l.ID = il.LocationID;

将 AsOf 值设置为 "today" 以获取最近的位置,或将其设置为任何日期以查看 截至该日期 的位置。由于当前位置远离最常见的查询,因此定义一个仅生成当前位置的视图并在连接中使用它。

join    CurrentItemLocation cil
    on  cil.ItemID = i.ID
left join Locations  l
    on  l.ID = cil.LocationID;

初步反应

What is the best way to achieve this?

这是一个简单而常见的数据建模问题,答案(至少在关系数据库上下文中)很简单。我会说,每个数据库至少都有其中的一些。不幸的是,因为撰写有关 关系模型 书籍的作者实际上对它一无所知,所以他们不会写这种简单直接的问题或简单的解决方案。

您要找的是 OR 门。在这种情况下,因为 ItemLocation XOR 中,它是 InTransit, 你需要一个 XOR 门。

在关系术语中,这是一个 Basetype::Subtype 结构 。如果实施得当,它会提供完整的完整性,并消除空值。

据我所知,它只是关系方法。当心,著名作家提供的方法是非关系的、可怕的、非常低效的,而且它们不起作用。

###记录编号

但首先...如果我没有提到你的文件现在不完整,我就不会为你服务,你有一个记录归档系统。这可能不是你的错,因为著名作家只知道 1970 年代以前的记录归档系统,所以这就是他们所能教授的全部内容,但问题是,他们将其标记为“关系”,这是不正确的。他们对RM也有各种误解,比如它不支持层次结构,等等

  • 通过在每个 table 上标记 ID 开始,数据建模过程被削弱了

  • 您没有 RDBS 要求的行唯一性。

  • ID 不是密钥。

  • 如果不明白,请阅读

我已经部分更正了这些错误:

  • Item里给了一个更有用的PK。我从未听过任何用户讨论 Item RecordId,他们总是使用 Codes.

  • 通常这些代码是由组件组成的,如果是这样,您需要将这些组件记录在单独的列中(否则您会破坏 1NF)。

  • Item 需要 Name, 上的备用键,否则您将允许重复名称。

  • Location,中我提出了一个Key,标识一个唯一的物理位置。请修改以适应。

  • 如果Location有一把Name,需要是AK.

  • 我还没有给你谓词。这些非常重要,原因有很多。这里的主要原因是,它将证明记录 ID 的疯狂。如果你想要它们,请询问。

  • 如果您想了解有关 Predicates 的更多信息,请访问 ,向下滚动(向下滚动!)至 Predicate,然后阅读该部分.还要检查他们的 ERD。

###解决方案

What changes [do] I need to apply to the above schema?

试试这个:

  • 项目历史数据模型
    (已过时,请参阅下面的更新模式,在进度的上下文中)

  • 如果您不习惯使用符号,请注意每一个小刻度、刻痕和标记,实线与虚线,方角与圆角,都具有非常具体的含义。参考IDEF1X Notation for a full explanation, or Model Anatomy.

  • 如果您之前没有遇到过正确实现的子类型,请阅读此Subtype Overview

  • 这是一个独立的文档,带有指向代码示例的链接

  • 还有一个关于 How to implement referential integrity in subtypes 的 SO 讨论。

  • 考虑亚型簇时,将每个 Basetype::Subtype 对视为一个单元,不要将它们视为两个片段或两半。每对一个事实。

  • ItemHistoryItem.

    历史上的一个事件(一个事实)
  • 每个 ItemHistory 事实要么是一个 Location 事实 XOR 一个 InTransit 事实。

  • 每个事实都有不同的属性。

  • 请注意,该模型代表了您所参与的真实世界的简单、诚实和真实。除了完整性等,如上所述,结果是简单直接的代码:每一个其他“解决方案”都会使代码复杂,以便处理异常情况。有些“解决方案”比其他的更可怕。

  • E F Codd 博士在 1970 年给了我们这个。它在 1984 年作为建模方法实现,命名为 IDEF1X。这在 1993 年成为关系数据库的标准。我从 1987 年开始就专门使用它。

  • 但是据称在关系模型上写书的作者对这些项目[=]一无所知。他们只知道 1970 年代以前的 ISAM 记录归档系统。他们甚至不知道他们没有关系数据库的完整性、能力或速度,更不用说为什么他们没有了.

  • Date、Darwen、Fagin、Zaniolo、Ambler、Fowler、Kimball 都在宣扬对 RM 的错误看法。

回复评论

1) ItemHistory, contains Discriminator column 'InTransit'.

正确。以及随之而来的所有含义:它是一个控制元素;它的价值最好受到限制;等等

Shall it be enum with the value Y / N?

首先要明白,存值是有意义的。这个意思可以用任何你喜欢的方式表达。英文意思是{Location|InTransit}.

对于存储,我知道命题 InTransit 的值是 {True|False}, ...

在SQL(如果你想要真正的文章,这是portable),我打算把它作为BITBOOLEAN.想想你想要什么出现在报告中。在本例中它是一个控制元素,因此它不会出现在用户报告中。在那里我会坚持 InTransit={0|1}.

但是,如果您更喜欢 {Y|N},,那很好。只需在整个数据库中保持一致(不要在一个地方使用 {0|1} 而在另一个地方使用 {Y|N})。

对于 do 出现在报告中的值,或者像 EventType 这样的列,我会使用 {InTransit|Location}.

在SQL中,为了实现,如果BOOLEAN,域(取值范围)已经被约束。不需要进一步的。

如果该列是其他 BOOLEAN,` 你有两个选择:

  1. CHECK约束

        CHECK @InTransit IN ( "Y", "N" )
    
  2. 引用或查找 Table

实施仅包含有效域的 table。要求是单列,即代码本身。您可以为报告中显示的短描述符添加一列。 CHAR(12)很适合我。


ENUM

SQL中没有ENUM。一些非 SQL 数据库有它。基本上它实现了上面的选项 [2],并在幕后使用 Lookup table。它没有意识到行是唯一的,因此它 枚举 行,因此得名,但它为数字添加了一列,这当然是一个 ID 充满了 AUTOINCREMENT, 所以 MySQL 属于 愚蠢的事情 的类别,如 中所述(向下滚动到 查找Table节)。

所以不,不要使用 ENUM 除非你想被粘在一个本土的、愚蠢的、非 SQL 平台上,并在数据库被重写时遭受重写移植到真正的 SQL 平台。该平台可能很愚蠢,但这不是走同样道路的好理由。即使 MySQL 是你所有的,使用上面给出的两个 SQL 设施之一,不要使用 ENUM.


2) Why is'ItemHistoryTransit' needed as 'Date' column

DATETIME,不是DATE,,但我认为这不重要。)

[It] is there in ItemHistory?

约束的标准方法(数据库中的所有内容都受到约束)Basetype::Subtype 关系的本质是,在子类型中实现与基类型完全相同的 PK。基型 PK 是(ItemCode, DateTime).

[Why] will only Discriminator not work?

这是错误的,因为它不符合标准要求,因此允许出现奇怪而奇妙的值。我想不出一个可以证明这是合理的实例,即使提供了替换约束。

其次,ItemEvents每个 ItemCode 的 InTransit 可能超过两个 occ,这是不允许的。

三、Basetype PK值不匹配

解决方案

  • 实际上,table 更好的名称是 ItemEvent. 标签是理解的关键。

  • Predicates我已经给出了,请仔细查看。

  • 数据模型已更新。

  • Item Event Data Model