0

目标是存储诸如插入、更新和删除业务记录之类的活动。

我正在考虑的一种解决方案是对每条记录使用一个表进行跟踪。这是一个简化的示例:

CREATE TABLE ActivityTypes
(
    TypeId              int IDENTITY(1,1)       NOT NULL,
    TypeName            nvarchar(50)            NOT NULL,

    CONSTRAINT PK_ActivityTypes          PRIMARY KEY (TypeId),
    CONSTRAINT UK_ActivityTypes          UNIQUE (TypeName)
)

INSERT INTO ActivityTypes (TypeName) VALUES ('WidgetRotated');
INSERT INTO ActivityTypes (TypeName) VALUES ('WidgetFlipped');
INSERT INTO ActivityTypes (TypeName) VALUES ('DingBatPushed');
INSERT INTO ActivityTypes (TypeName) VALUES ('ButtonAddedToDingBat');

CREATE TABLE Activities
(
    ActivityId          int IDENTITY(1,1)       NOT NULL,
    TypeId              int                     NOT NULL,
    AccountId           int                     NOT NULL,
    TimeStamp           datetime                NOT NULL,

    CONSTRAINT PK_Activities                      PRIMARY KEY (ActivityId),
    CONSTRAINT FK_Activities_ActivityTypes        FOREIGN KEY (TypeId)
                                                  REFERENCES ActivityTypes (TypeId),
    CONSTRAINT FK_Activities_Accounts             FOREIGN KEY (AccountId)
                                                  REFERENCES Accounts (AccountId)
)

CREATE TABLE WidgetActivities
(
    ActivityId          int                     NOT NULL,
    WidgetId            int                     NOT NULL,

    CONSTRAINT PK_WidgetActivities                  PRIMARY KEY (ActivityId),
    CONSTRAINT FK_WidgetActivities_Activities       FOREIGN KEY (ActivityId)
                                                    REFERENCES Activities (ActivityId),
    CONSTRAINT FK_WidgetActivities_Widgets          FOREIGN KEY (WidgetId)
                                                    REFERENCES Widgets (WidgetId)
)

CREATE TABLE DingBatActivities
(
    ActivityId          int                     NOT NULL,
    DingBatId           int                     NOT NULL,
    ButtonId            int,

    CONSTRAINT PK_DingBatActivities                  PRIMARY KEY (ActivityId),
    CONSTRAINT FK_DingBatActivities_Activities       FOREIGN KEY (ActivityId)
                                                     REFERENCES Activities (ActivityId),
    CONSTRAINT FK_DingBatActivities_DingBats         FOREIGN KEY (DingBatId)
                                                     REFERENCES DingBats (DingBatId)
    CONSTRAINT FK_DingBatActivities_Buttons          FOREIGN KEY (ButtonId)
                                                     REFERENCES Buttons (ButtonId)
)

该解决方案似乎适用于获取给定小部件或 dingbat 记录 ID 的所有活动,但是对于获取所有活动然后尝试确定它们引用的记录似乎不太好。

也就是说,在此示例中,所有帐户名称和时间戳都存储在单独的表中,因此可以轻松创建专注于用户和专注于时间间隔的报告,而无需知道具体的活动是什么。

但是,如果您确实想特别按类型报告活动,则此解决方案将需要确定一般活动表所指的活动类型。

我可以将所有活动类型放在一个表中,但是 ID 不能受外键约束,而是可以将表名用作 id,这将导致我使用动态查询。

注意示例中的 DingBatActivity 有一个可选的按钮 ID。如果按钮名称在添加到 dingbat 后已被编辑,则活动将能够引用该按钮并知道其名称,因此如果报告按 dingbat 和按名称按按钮列出所有活动,则按钮名称会更改将自动反映在活动描述中。

寻找其他一些想法以及这些想法如何在编程工作、数据完整性、性能和报告灵活性之间取得平衡。

4

5 回答 5

1

我将冒险出去,对你真正想要完成的事情进行一些疯狂的猜测。

您说您正在尝试跟踪“商店活动” 我将假设您有以下活动: 购买新商品 出售商品 注销商品 雇用员工 支付员工 解雇员工 更新员工记录

好的,对于这些活动,您需要几个不同的表:一张用于库存,一张用于部门,一张用于员工

库存表可能包含以下信息:

inventory:
  item_id (pk)
  description (varchar)
  number_in_stock (number)
  cost_wholesale (number)
  retail_price (number)
  dept_id (fk)

department:
  dept_id (pk)
  description (varchar)

employee
  emp_id (pk)
  first_name (varchar)
  last_name (varchar)
  salary (number)
  hire_date (date)
  fire_date (date)

因此,当您购买新商品时,您将更新库存表中的 number_in_stock,或者如果它是您以前从未拥有过的商品,则创建一个新行。当您出售一件商品时,您会扣除该商品的 number_in_stock(也适用于您注销一件商品时)。

当您雇用新员工时,您会将他们的记录添加到员工表中。当您向他们付款时,您会从工资栏中获取他们的工资。当您解雇他们时,您填写该栏以供他们记录(并停止支付他们)。


在所有这一切中,这一切都不是由数据库完成的。应该使用 SQL 来跟踪信息。编写执行这些更新的程序(一个新的发票程序,更新发票记录中的所有项目)很好。但是你不需要一张桌子来做事。事实上,一张桌子什么也做不了。

在设计数据库时,您需要问的问题不是“我需要做什么?” 它是“我需要跟踪哪些信息?”

于 2009-12-19T01:43:20.553 回答
1

新答案,基于对问题的不同解释。

你只是想记录发生的事情吗?如果你只需要一个有序的过去事件列表,你只需要一张表:

action_list
  action_list_id (pk)
  action_desc (varchar)

event_log:
  event_log_id (pk)
  event_time (timestamp)
  action_list_id (fk)
  new_action_added (fk)
  action_details_or_description (varchar)

在这种情况下,action_list 将类似于:

1   'WidgetRotated'
2   'WidgetFlipped'
3   'DingBatPushed'
4   'AddNewAction'
5   'DeleteExistingAction'

event_log 将是发生了什么活动以及何时发生的列表。您的一项操作是“添加新操作”,并且只要采取的操作是“添加新操作”,就需要在事件表中填写“new_action_added”列。

您可以创建更新、删除、添加等操作。

编辑:我将 action_details_or_description 列添加到事件。通过这种方式,您可以提供有关操作的更多信息。例如,如果您有“产品更改颜色”操作,则说明可能是新颜色的“红色”。

更广泛地说,您需要提前考虑并绘制出您将要采取的所有不同类型的操作,这样您就可以以能够准确包含您想要的数据的方式设置您的表格放入其中。

于 2009-12-19T01:54:26.663 回答
1

我通常为这个问题设计解决方案的方式类似于对象中的继承。如果您有在某些实体上发生的“活动”并且您想跟踪这些活动,那么所涉及的实体几乎肯定有一些共同点。这是你的基表。从那里您可以从基表创建子表以跟踪特定于该子类型的事物。例如,您可能有:

CREATE TABLE Objects   -- Bad table name, should be more specific
(
     object_id     INT          NOT NULL,
     name          VARCHAR(20)  NOT NULL,
     CONSTRAINT PK_Application_Objects PRIMARY KEY CLUSTERED (application_id)
)

CREATE TABLE Widgets
(
     object_id     INT           NOT NULL,
     height        DECIMAL(5, 2) NOT NULL,
     width         DECIMAL(5, 2) NOT NULL,
     CONSTRAINT PK_Widgets PRIMARY KEY CLUSTERED (object_id),
     CONSTRAINT FK_Widgets_Objects
     FOREIGN KEY (object_id) REFERENCES Objects (object_id)
)

CREATE TABLE Dingbats
(
     object_id     INT           NOT NULL,
     label         VARCHAR(50)   NOT NULL,
     CONSTRAINT PK_Dingbats PRIMARY KEY CLUSTERED (object_id),
     CONSTRAINT FK_Dingbats_Objects
     FOREIGN KEY (object_id) REFERENCES Objects (object_id)
)

现在为您的活动:

CREATE TABLE Object_Activities
(
     activity_id     INT          NOT NULL,
     object_id       INT          NOT NULL,
     activity_type   INT          NOT NULL,
     activity_time   DATETIME     NOT NULL,
     account_id      INT          NOT NULL,
     CONSTRAINT PK_Object_Activities PRIMARY KEY CLUSTERED (activity_id),
     CONSTRAINT FK_Object_Activities_Objects
     FOREIGN KEY (object_id) REFERENCES Objects (object_id),
     CONSTRAINT FK_Object_Activities_Activity_Types
     FOREIGN KEY (activity_type) REFERENCES Activity_Types (activity_type),
)

CREATE TABLE Dingbat_Activities
(
     activity_id     INT     NOT NULL,
     button_id       INT     NOT NULL,
     CONSTRAINT PK_Dingbat_Activities PRIMARY KEY CLUSTERED (activity_id),
     CONSTRAINT FK_Dingbat_Activities_Object_Activities
     FOREIGN KEY (activity_id) REFERENCES Object_Activities (activity_id),
     CONSTRAINT FK_Dingbat_Activities_Buttons
     FOREIGN KEY (button_id) REFERENCES Object_Activities (button_id),
)

如果您想为它正在影响的对象的类型添加类型代码,您可以将类型代码添加到基本活动,或者您可以通过在子表中查找存在来确定它。

不过,这里有一个很大的警告:确保对象/活动确实有一些共同点,这些共同点将它们联系起来,并要求你走这条路。您不想将脱节的、不相关的数据存储在同一个表中。例如,您可以使用此方法创建一个包含银行账户交易和天体事件的表,但这不是一个好主意。在基本层面,他们需要有共同点。

Also, I assumed that all of your activities were related to an account, which is why it's in the base table. Anything in common to ALL activities goes in the base table. Things relevant to only a subtype go in those tables. You could even go multiple levels deep, but don't get carried away. The same goes for the objects (again, bad name here, but I'm not sure what you're actually dealing with). If all of your objects have a color then you can put it in the Objects table. If not, then it would go into sub tables.

于 2009-12-19T04:32:54.110 回答
0

SQL 日志呢?

于 2009-12-19T01:22:31.233 回答
0

上次我需要一个数据库事务记录器时,我在数据库中使用了一个替代触发器,这样它就不会只是更新记录,而是将一条新记录插入到日志表中。这种技术意味着我需要一个额外的表来保存数据库中每个表的日志,并且日志表有一个带有时间戳的额外列。使用这种技术,您甚至可以根据需要存储记录的更新前和更新后状态。

于 2009-12-19T02:40:30.763 回答