我想在数据库中保存每个客户按工作日和小时进入网站的次数。这意味着对于每个客户,我将拥有 24 * 7 的值,这些值将不断更新,以反映客户访问次数最多的高峰时段。我已经看到了明显的建议Database structure for holding statistics by day, week, month, year to create a new line for each entry and than use the data, it won't work, we have 数百万行,我需要峰值每个客户可用的时间。此外,为每个客户创建 168 列看起来有点极端。有什么建议么?
2 回答
可能需要进行现实检查。
我将拥有 24 * 7 的值,这些值将不断更新,以反映客户访问次数最多的高峰时段。
这是假设您每天存储 24 个条目,即客户端未显示的条目。除非您的客户是一家公司,否则人们会睡觉。
为每个入口创建一个新行而不是使用数据,它不起作用,我们有数百万行
所以?30 年前,表中的数百万行并不是问题。今天绝对不是问题。是的,它可能无法在 20 年前的台式机上运行 - 但在具有半 tb 内存和适当磁盘布局的中型服务器上,您可以存储数百 GB 的数据并快速处理它们。
此外,为每个客户创建 168 列看起来有点极端。
这也是愚蠢的。我的意思是愚蠢。看 - 问题是虽然您(和您的应用程序)可以使用它,但您很快就会发现,如果您尝试将数据加载到报告工具中并查找特定小时的所有行 - 您生活在一个痛苦的世界。每个条目一行是关系数据模型所建议的。其他任何东西要么是聪明的(对于一个非常有限的用例),要么是证明工具非常快地遵循关系定理,并且你生活在一个非常痛苦的代码世界中。
并不是说它不会发生。我见过人们为每张发票写一个新表(所以发票详细信息表不会太长)......
每个入口有一个条目,您可以按星期几分组,按小时汇总 - 每行有 168 个字段,您不能轻易做到。
通常:这是 2020 年。中端台式机具有 64GB 内存。中端服务器有 1 TB 或 2 TB。SSD 存储使数据库能够轻松快速地处理数百 GB 的数据——这在硬盘时代是非常痛苦的。26 年前,在我的第一个商业级数据库项目中,数百万行是个笑话。今天,数百亿是小变化。
这是一个表结构(类似于我见过的实现),它将汇总统计信息分离为周、日期(或天)和小时表,并将主键与外键关系连接起来。它不是将一天中的不同时间存储为列(在 rdbms 中不推荐),而是将它们存储在行中。可以根据需要使用适当的索引和分区来处理每天(或每小时)数百万次访问。
像这样的东西
DDL
create table dbo.visitor_events(
v_id int identity(1,1) primary key not null,
client_id int not null references clients(client_id),
visit_dt datetime2(7) not null default sysutcdatetime());
create table dbo.visitor_event_weeks(
vsw_id int identity(1,1) primary key not null,
client_id int not null references clients(client_id),
visit_wk int not null,
visits int not null);
create table dbo.visitor_event_dates(
vsd_id int identity(1,1) primary key not null,
client_id int not null references clients(client_id),
vsw_id int not null references visitor_event_weeks(vsw_id),
visit_wk int not null,
visit_dt datetime not null,
visits int not null);
create table dbo.visitor_event_hours(
vsh_id int identity(1,1) primary key not null,
client_id int not null references clients(client_id),
vsd_id int not null references visitor_event_dates(vsd_id),
visit_hr datetime not null,
visits int not null);
变量
变量和插入/更新语句(会因最适合 OP 而异)
declare
@client_id int=123,
@visit_dt datetime2(7)=sysutcdatetime();
declare
@v_id int;
declare
@vsw table(vsw_id int unique not null);
declare
@vsd table(vse_id int unique not null);
/* Insert a visit */
insert dbo.visitor_events(client_id, visit_dt) values
(@client_id, @visit_dt);
select @v_id=scope_identity();
/* Insert/update a visit week */
update dbo.visitor_event_weeks
set visits=visits+1
output inserted.vsw_id into @vsw
where client_id=@client_id
and visit_wk=datediff(wk, 0, @visit_dt);
if @@rowcount>0
begin
insert dbo.visitor_event_weeks(client_id, visit_wk, visits)
output inserted.vsw_id into @vsw
values (@client_id, datediff(wk, 0, @visit_dt), 1);
end
/* Insert a visit date */
update dbo.visitor_event_dates
set visits=visits+1
output inserted.vsd_id into @vsd
where client_id=@client_id
and vsw_id=(select top 1 vsw_id from @vsw)
and visit_dt=cast(@visit_dt as date);
if @@rowcount>0
begin
insert dbo.visitor_event_dates(client_id, vsw_id, visit_dt, visits)
output inserted.vsd_id into @vsd
values (@client_id, (select top 1 vsw_id from @vsw), cast(@visit_dt as date), 1);
end
/* Insert a visit date hour */
update dbo.visitor_event_dates
set visits=visits+1
output inserted.vsd_id into @vsd
where client_id=@client_id
and visit_dt=cast(@visit_dt as date);
if @@rowcount>0
begin
insert dbo.visitor_event_hours(client_id, vsd_id, visit_dt, visits)
output inserted.vsd_id into @vsd
values (@client_id, (select top 1 vsw_id from @vsw), cast(@visit_dt as date), 1);
end