如何设计一个记录客户在一个时间跨度内的状态的表格?

时间:2016-12-12 21:04:20

标签: sql database entity-framework database-design entity-framework-core

我有一个Customer表,其中包含IsActive列。如果"活跃",他有资格获得某些产品和服务。

我需要知道是否在特定日期,他是否符合条件。

问题是他的记录可能会发生变化,因此我必须在CustomerChanges审核表中跟踪这些更改,其中包含可以更改的属性。每当我们更改他的记录时,我们都会在该表中添加一条记录,包括更改和日期。

我需要的是"让我所有的客户,他们约会xyz,活跃"。

我如何建模和查询这样的表?

(我使用的是SQL Server和EF Core)

愚蠢的例子:给他的生日送一张礼品卡,但前提是他的帐户没有被暂停,取消或拖欠等等。换句话说,只有当他的帐户是“活跃的”#34;在那一天。

3 个答案:

答案 0 :(得分:1)

我会这样做:将CustomerId,StartingDate和EndingDate作为PrimaryKey的表。然后,您还可以获得此表中可能需要的所有其他信息,例如他是否活跃。

保存所有更改的最佳方法是在Customer.IsActive上设置更新触发器....此触发器需要在更改的起始点和更改的结束点之间进行区分。

然后,如果你想查询它,你需要在这两个表之间进行连接,看起来像这样:

SELECT * FROM CustomerChanges
INNER JOIN Customer ON Customer.Id = CustomerChanges.CustomerId
WHERE xyz BETWEEN CustomerChanges.StartingDate 
AND CustomerChanges.EndingDate; 

答案 1 :(得分:1)

我相信理论上这就是你想要实现的目标。

select Users.Id, Users.UserName, OrderedActivity.IsActive
from
(
    select UserActivity.Id
        , UserActivity.UpdateTime
        , UserActivity.IsActive
        , row_number() over         
         (partition by UserActivity.Id order by UserActivity.UpdateTime desc) as EntryNum
    from UserActivity
    where UserActivity.UpdateTime <= '20161210 10:00:00 AM'
) OrderedActivity
inner join Users
    on Users.Id = OrderedActivity.Id     
where OrderedActivity.EntryNum = 1;

基本上,它按日期对UserActivity中的条目进行排名(包含任何活动/非活动更改),不包括比所选日期更新的任何内容。然后它接受第一个条目(日期之前的最近更改)。

此时,对于每个用户记录,您都可以使用相应的IsActive标记(您可以使用添加到外部查询的另一个where子句来过滤掉您不想要的内容)。

以下是此操作的示例:http://rextester.com/QPSM50121

在C#中:

var filteredActivity = 
    Activity.Where(activity => activity.UpdateTime <= SOME_DATE)
            .GroupBy(activity => activity.Id)
            .Select(groupedActivities => groupedActivities.OrderByDescending(activity => activity.UpdateTime).FirstOrDefault()).ToList();

var results = 
        from user in Users
        join activity in filteredActivity
            on user.Id equals activity.Id
        where activity.IsActive == 'Y'
        select new { user.Id, activity.IsActive };

行动中的示例:http://rextester.com/SYZ96749

这是C#中的另一个版本,但尝试将其全部合并到LINQ-to-SQL中。不确定它是否会因为问题而帮助您解决您的EF小组问题。

var results = 
    from activity in Activity
    where activity.UpdateTime <= Convert.ToDateTime("10/12/2016 10:00:00")
    group activity by activity.Id into groupedActivities
    let mostRecentActivity =
    (
        from groupedActivity in groupedActivities
        orderby groupedActivity.UpdateTime descending
        select groupedActivity
    ).First()
    join user in Users
        on mostRecentActivity.Id equals user.Id
    where mostRecentActivity.IsActive == 'Y'
    select new { user.Id, mostRecentActivity.IsActive };

行动中:http://rextester.com/QUGR95471

答案 2 :(得分:1)

您走在正确的轨道上,可能已经拥有最佳设计。你没有显示审计表的设计,所以这是我的建议:

create table CustomerStatus(
  CustID     int  not null references Customer( ID ),
  EffDate    date not null,
  Status      char( 1 ) not null,   -- A = Active, S = Suspended, etc.
  constraint PK_CustomerStatus primary key( CustID, EffDate )
);

注意没有&#34;结束&#34;日期。一旦状态生效,它将一直有效,直到它被另一个状态替换。这是查询的关键,以查找当前状态或在任何给定日期生效的状态。

Select  c.*, cs.Status
from    Customer c
join    CustomerStatus cs
    on  cs.CustID = c.ID
    and cs.EffDate =(
        select  Max( EffDate )
        from    CustomerStatus
        where   CustID = cs.CustID
            and EffDate <= Today() );
不要让子查询吓到你。您得到的是一条记录,其中包含最新生效日期之前或当前日期/时间的状态。您可以使用查询创建视图,以返回任何或所有客户的当前状态,因为这可能是操作中最常用的状态。

这是非常酷的部分。将最后一行更改为以下内容:

            and EffDate <= :AsOf );

AsOf 变量设置为当前日期/时间,您将获得之前的当前状态。将变量设置为过去或将来的任何日期/时间,您将获得该日期/时间(或将来)生效的状态。您可以使用相同的查询检索当前状态或任何过去(或将来)状态。我认为这非常棒。

假设客户今天购买了3个月的会员资格。因此,您插入状态为Active的生效日期为今天,另一个状态为Arrears(或Lapsed或其他),生效日期为3个月。

  • 如果客户在该时间段内续订,请更新未来记录并再添加3个月(或他们购买的任何期限),一切都会继续正常工作。
  • 如果客户什么都不做,状态将继续显示为活动状态,直到时钟的滴答为止,直到&#34; future&#34;的日期/时间为止。条目。此时,该状态变为当前状态,并且将在上述查询中返回状态。