如何在SQL Server中重新设计现有的MS Access表

时间:2011-08-03 21:54:41

标签: sql-server database-design ms-access normalization

我正在重写一个当前未规范化且设计非常糟糕的MS Access数据库。我在重新设计中遇到的问题是围绕着他们在表之间每天移动数据的方式。

目前的设置与此类似。我有一个文本文件加载到表中。我需要的记录被添加到Table1,第二天我将Table1中的一些数据加载到Table2中。然后在导入当天后使用Table2更新Table1。

6/1/11
File > Table1
       field1
       field2

在6/1/11,该文件将更新Table1并填充字段

6/2/11
Step 1                  Step 2                Step 3
Table1 > Table2         File  > Table1        Table2 > Table1  
field1   field1                 newdata1      field1   newdata1
field2   field2                 newdata2      field2   newdata2

在2011年6月2日,第一步是将field1 / field2移动到table2(临时表)。然后,我们从table1中删除数据,然后在步骤2中导入该文件。在步骤3中,如果帐户存在,我们使用table2中的数据对table1执行更新。基本上,如果账户存在,我们今天将今天的数据转接到今天。

在我的新设计中,我有一张类似于此的表格。表中的主键是BusinessDate和Account,因为它们每天都不同。

CREATE TABLE [dbo].[Table](
    [BusinessDate] [smalldatetime] NOT NULL,
    [Account] [int] NOT NULL,
    [Guid] [uniqueidentifier] NOT NULL,
    [InitialAmount] [money] NULL,
    [LetterDate] [smalldatetime] NULL,
    [LetterType] [varchar](50) NULL,
    [Status] [varchar](50) NULL,
    [Reason] [varchar](50) NULL,
    [FollowUpDate] [smalldatetime] NULL,
    [LastModifiedBy] [varchar](50) NULL,
    [LastModifiedDate] [datetime] NULL
) ON [PRIMARY]

如果帐户已添加6/1然后在6/2导入,我将使用上面字段中6/1的数据执行更新。我被告知,继续每天向前推进同样的复制数据是一个糟糕的设计。但我不知道如何实现这一点,因为日常的记录被认为是新的,但一些原始数据需求仍然附加在所有未来的项目上。

这种类型的设置在当前设置中使用,我对如何设计它感到困惑。任何人都可以提供有关如何进行此设计的任何建议吗?这很难解释,如果不清楚请问。

修改

我的部分问题是,我需要能够访问任何一天前进的详细信息。如果我在帐户中有一个初始金额为8/1/11的帐户,则用户会分配一个状态,字母类型。然后同样的帐户出现在8/2/11,8/2的起点是从8/1开始的日终值。然后他们将处理当天的帐户。在8/2,帐户将重新开始,但不是从零开始,而是从昨天的值开始。

用户仍然需要能够从8/1访问数据并查看日终值。

示例:

  • 8/1/11帐户1234567890添加了借记卡 - $ 10,代表分配状态=已审核,原因=检查问题
  • 8/2/11帐户1234567890添加了新的借记--50美元,开始状态=已审核,原因=检查
  • 8/2/11 - 用户使用帐户1234567890上的数据进行借记$ -50并更改状态=已解决,原因=无
  • 8/3/11 - 用户搜索帐户1234567890的营业日期8/1/11,他们需要查看状态=已审核的数据,原因=检查问题

希望额外的细节会有所帮助。很抱歉很长的解释。

2 个答案:

答案 0 :(得分:1)

好的,所以看过你的问题的编辑,这是我的下一次尝试!

这背后的想法是我们使用一个表来存储所有内容,而这个表实际上是所有发生的事情的更改日志。然后,我们使用视图为每个帐户提供最新信息,而不是直接在桌面上工作。我还提供了一个视图,可以在白天为每个帐户提供“关闭”状态。

您可以轻松地将此设计修改为每天只有一行,而不是完整的更改日志。 (在这种情况下,您不需要第二个视图,因为它实际上与表格相同。)

CREATE TABLE Accounts (
    ID                  int IDENTITY(1,1),
    BusinessDate        smalldatetime NOT NULL,
    Account             int NOT NULL,
    InitialAmount       money NULL,
    LetterDate          smalldatetime NULL,
    LetterType          varchar(50) NULL,
    Status              varchar(50) NULL,
    Reason              varchar(50) NULL,
    FollowUpDate        smalldatetime NULL,
    LastModifiedBy      varchar(50) NULL,
    LastModifiedDate    datetime NULL
)

insert into Accounts
values ({d '2011-08-01'}, 1234567890, -10, NULL, NULL, 'Reviewed', 'Check issue', NULL, 'User', NULL),
    ({d '2011-08-02'}, 1234567890, -50, NULL, NULL, 'Reviewed', 'Check issue', NULL, 'DataLoad', NULL),
    ({d '2011-08-02'}, 1234567890, -50, NULL, NULL, 'Resolved', 'None', NULL, 'User', NULL),
    ({d '2011-08-02'}, 1234567891, -20, NULL, NULL, 'Reviewed', 'Check issue', NULL, 'DataLoad', NULL)
GO

create view AccountsLatest
as
select a.*
from Accounts a inner join
    (   
        select a.Account, MAX(a.ID) as ID
        from Accounts a
        group by a.Account
    ) mx on a.ID = mx.ID
GO

create view AccountLatestByDate
as
select a.*
from Accounts a inner join
    (   
        select a.Account, a.BusinessDate, MAX(a.ID) as ID
        from Accounts a
        group by a.Account, a.BusinessDate
    ) mx on a.ID = mx.ID
GO

至于你的文件已被加载,并且需要“复制”一些数据,那么可以这样处理:

--For simplicity's sake, I'm creating a load table that you simply clear and append your file contents to every night
create table MyFile (
    BusinessDate        smalldatetime NOT NULL,
    Account             int NOT NULL,
    InitialAmount       money NULL,
)

insert into Accounts
select f.BusinessDate, f.Account, f.InitialAmount, a.LetterDate, a.LetterType, a.Status, a.Reason, a.FollowUpDate, a.LastModifiedBy, a.LastModifiedDate
from MyFile f inner join
    AccountsLatest a on f.Account = a.Account

我希望这对您来说是一个不错的起点 - 它显然需要精炼以满足您的确切需求。

我认为你对这背后的过程提出质疑是正确的,因为我认为现有的解决方案有点鱿鱼。它可能有用,但听起来很不理想。我对你的业务一无所知,但它背后的业务流程也可能有点狡猾 - 可能值得利用这个平台迁移的机会,至少探讨清理这方面的想法。

原始答案(为了后人的缘故)

如果您有关于每天保持静态的帐户的数据,以及其他特定日期的数据,那么我会将这些数据集分成他们自己的表。这样就不需要将数据从一天复制到下一天。所以你的表可能看起来像这样(我猜测哪些列保持静态):

CREATE TABLE dbo.Accounts (
    Account int NOT NULL PRIMARY KEY,
    InitialAmount money NULL,
    Status varchar(50) NULL,
    Reason varchar(50) NULL,
    LastModifiedBy varchar(50) NULL,
    LastModifiedDate datetime NULL
)

CREATE TABLE dbo.AccountDetails (
    BusinessDate smalldatetime NOT NULL,
    Account int NOT NULL,
    Guid uniqueidentifier NOT NULL,
    LetterDate smalldatetime NULL,
    LetterType varchar(50) NULL,
    FollowUpDate smalldatetime NULL,
    LastModifiedBy varchar(50) NULL,
    LastModifiedDate datetime NULL
)

--This table will contain all the historic data that used to be in AccountDetails
CREATE TABLE dbo.AccountDetailsHistory (
    BusinessDate smalldatetime NOT NULL,
    Account int NOT NULL,
    Guid uniqueidentifier NOT NULL,
    LetterDate smalldatetime NULL,
    LetterType varchar(50) NULL,
    FollowUpDate smalldatetime NULL,
    LastModifiedBy varchar(50) NULL,
    LastModifiedDate datetime NULL
)

现在,每天晚上AccountDetails的内容被移动到AccountDetailsHistory,并且文件的内容被加载到AccountDetails中。您必须“向前复制”的数据存储在“帐户”中,因此未更改。

如果需要,您也可以只使用一个AccountDetails表并对其进行分区。这是一篇关于分区如何工作以及是否应该使用它的好文章:http://msdn.microsoft.com/en-us/library/ms345146%28v=sql.90%29.aspx,这个文章向您展示了设置的简单性:http://blogs.msdn.com/b/manisblog/archive/2009/01/18/easy-table-partitions-with-sql-server-2008.aspx

希望这有帮助!

答案 1 :(得分:0)

您似乎混合了数据存储要求和工作流程要求。 SQL Server非常适合存储数据,但您可以使用多个选项来管理工作流元素。您可以使用存储过程来管理工作流或SQL Server Integration Services(SSIS)。

概述的问题似乎只需要最新的每日快照(数据每天更新一次),因此每日查看最新快照就足够了。或者,如果您确实需要记录并能够跟踪所有日内事务,那么您可能希望将表拆分为两个(事务和每日快照),但我觉得这可能不是必需的。

存储过程解决方案

要保留SQL Server中的所有内容,您将使用一个过程来相应地创建,插入和复制数据,具体取决于已存在的内容,

以下脚本使用单个样本数据帐户设置名为AccountDetails的表;

    CREATE TABLE [dbo].AccountDetails(
    [BusinessDate] [smalldatetime] NOT NULL,
    [Account] [int] NOT NULL,
    [Guid] [uniqueidentifier] NOT NULL,
    [InitialAmount] [money] NULL,
    [LetterDate] [smalldatetime] NULL,
    [LetterType] [varchar](50) NULL,
    [Status] [varchar](50) NULL,
    [Reason] [varchar](50) NULL,
    [FollowUpDate] [smalldatetime] NULL,
    [LastModifiedBy] [varchar](50) NULL,
    [LastModifiedDate] [datetime] NULL
) ON [PRIMARY]

INSERT INTO AccountDetails
VALUES ('2011-01-01', 123, NEWID(), 20, '2011-01-01', 'initial', 'reviewed', 'check issue', '2011-01-02', 'dwb',GETDATE())

存储过程AccountDetailsController使您能够

  • 更新具有今天条目的现有帐户。作为null传入的字段将采用先前的记录值。
  • 根据以前输入的帐户详细信息插入新记录。这适用于已经设置帐户但希望更新某些详细信息的方案。传入null的字段将采用先前的记录值
  • 插入新记录,之前未输入帐户详细信息。新帐户,首次插入所有字段

CREATE PROCEDURE AccountDetailsController
    @BusinessDate smalldatetime ,
    @Account [int] ,
    @Guid [uniqueidentifier] ,
    @InitialAmount [money] ,
    @LetterDate smalldatetime,
    @LetterType varchar(50) ,
    @Status varchar(50) ,
    @Reason varchar(50) ,
    @FollowUpDate smalldatetime,
    @LastModifiedBy varchar(50)
 AS
 BEGIN

/*
 -- test bed
 -- remove when finished testing
    DECLARE 
    @BusinessDate smalldatetime = '2011-01-01',
    @Account [int] =123,
    @Guid [uniqueidentifier] =null ,
    @InitialAmount [money] =30 ,
    @LetterDate smalldatetime = '2011-01-01',
    @LetterType varchar(50) ='initial',
    @Status varchar(50) =  'reviewed',
    @Reason varchar(50) = 'check issue',
    @FollowUpDate smalldatetime = null,
    @LastModifiedBy varchar(50) = 'dwb'
*/

    IF EXISTS (SELECT * FROM AccountDetails WHERE DateDiff(DAY,BusinessDate, @BusinessDate) = 0 AND @Account = [account])
    BEGIN
        --RECORD ALREADY EXISTS FOR TODAY, UPDATE
        UPDATE  AccountDetails
        SET     BusinessDate = ISNULL(@BusinessDate, BusinessDate),
                [guid] = ISNULL(@Guid, [guid]),
                InitialAmount = ISNULL(@InitialAmount, InitialAmount),
                LetterDate = ISNULL(@LetterDate, LetterDate),
                LetterType = ISNULL(@LetterType, LetterType)  ,
                [Status] = ISNULL(@Status ,[Status] ) ,
                Reason = ISNULL(@Reason, reason) ,
                FollowUpDate = ISNULL(@FollowUpDate, FollowUpDate),
                LastModifiedBy = ISNULL(@LastModifiedBy, LastModifiedBy) ,
                LastModifiedDate = GETDATE()
        FROM    AccountDetails ad 
        WHERE   DateDiff(DAY,BusinessDate, @BusinessDate) = 0 
        AND     @Account = [account]
    END 
    ELSE IF EXISTS (SELECT * FROM AccountDetails WHERE @Account = [account])
    BEGIN
        --RECORD ALREADY EXISTS, BUT FOR ANOTHER DAY.  USE THIS AS THE BASIS OF TODAYS RECORD
        INSERT INTO AccountDetails
        SELECT @BusinessDate,
                @Account,
                ISNULL (@Guid, ad.guid),
                ISNULL(@InitialAmount, InitialAmount),
                ISNULL(@LetterDate, LetterDate),
                ISNULL(@LetterType, LetterType)  ,
                ISNULL(@Status ,[Status] ) ,
                ISNULL(@Reason, reason) ,
                ISNULL(@FollowUpDate, FollowUpDate),
                ISNULL(@LastModifiedBy, LastModifiedBy) ,
                GETDATE()
        FROM    AccountDetails ad
        WHERE   Account = @Account
        AND     BusinessDate = 
        (   
            SELECT  MAX(BusinessDate)
            FROM    AccountDetails 
            WHERE   @Account = [account]
        )
    END 
    ELSE 
    BEGIN
        --NO PREVIOUS RECORD EXISTS
        INSERT INTO AccountDetails
        VALUES (@BusinessDate,
                @Account,
                @Guid,
                @InitialAmount,
                @LetterDate,
                @LetterType,
                @Status,
                @reason ,
                @FollowUpDate,
                @LastModifiedBy ,
                GETDATE())

    END

END

以下内容应满足所有情况;

--update a record that exists for today
exec AccountDetailsController '2011-01-01', 123, null, 30, '2011-01-01', 'initial', 'reviewed', 'check issue', null, 'dwb'

--create a new record based on the last date entered for account
exec AccountDetailsController '2011-01-02', 123, null, 50, null, 'considered', null, null, null, 'dwb'

--create an entirely new record, no previous account exists
exec AccountDetailsController '2011-01-02', 456, 'C9CA5430-6D0E-47A3-BC89-D95C53B888E4' , 10, '2011-01-03', 'opened', 'new account', 'unknown','2011-01-03','dwb'

这将产生以下结果; Results of test data applied to AccountDetailsController

SSIS解决方案

如果您的业务逻辑和工作流程比您提供的示例更复杂,您可能需要考虑SSIS来管理和控制数据。这将采用SQL解决方案,并且最初会给您一个设置开销,但会更清晰地查看数据发生的情况,并且随着时间的推移可能更易于管理 - 特别是对于导入的文件。

相关问题