将三个表合并为一个或多列?

时间:2010-07-20 03:07:24

标签: sql sql-server optimization query-optimization

我正在跟踪三个时间段内的点击次数:过去一天,过去一周和过去一个月。

为此,我有三个表:

  • 每小时表,包含列link_id,其他两个属性以及hour_1到hour_24,以及计算列给出总和

  • 工作日表,其中包含click_id列,其他两个属性,以及day_1到day_7,以及计算列总和

  • 如上所述的月日表,其中包含第day_1天至第_31天

当点击进入时,我将其关键属性(如href,description等)存储在其他表中,并插入或更新上述每个表中与link_id相对应的行。

每个链接在上述每小时/工作日/月日表中可以有多个条目,具体取决于其他两个属性(例如用户所在的位置)。

因此,如果用户是A类并且坐在X中,则会在上表中创建或添加三行 - 第一行记录该时间段内该链接的所有点击,第二行记录所有点击次数“键入A人“,第三个”人们在X中的所有点击“。

我已经设计了这种方式,因为我不想每小时/每天/每周/每月移动数据。我只保留“当前时间”(1-24),“当前日期”(1-31)和“当前工作日”(1-7)的指针,并写入表格中的相应单元格。当我们进入一个新的时期(例如“下午3点至下午4点”)时,我可以删除当前列(例如hour_15),然后在链接进入时开始递增它。我经常删除掉落的旧行。到“全为零”。

这样我就不必移动列数据,这对于可能成千上万行的内容来说可能非常昂贵。

我只会根据属性选择当前日/工作日/小时行(插入/更新之前)或计算列中的TOP 20值(并且可能会将这些结果缓存一小时左右)

填充表后,UPDATES将远远超过INSERT,因为没有那么多独特的href。

三个问题:

  • 将三个大表组合成一个月平日/工作日/小时的大表可以吗?这将给出一个包含64列的表,我不确定是否有点过分。另一方面,保持它们像现在一样分开三倍所需的INSERT / UPDATE语句数。我不太了解SQL服务器,知道哪个最好。

  • 这种做法是否明智?我当然使用的大多数数据集每个项目都有一个单独的行,然后按日期排序 - 但是当跟踪来自数千个用户的点击时,这将给我数十万行,我将不得不剔除经常,订购和总结它们将是可怕的。一旦跟踪器被证实,我计划将点击监听器推出数百页,因此需要扩展。

  • 在设计方面,显然在工作日和工作日都存在一些冗余。但是,这是我能想到的唯一方法来维护指向列的指针并快速更新它,并使用计算列。如果我删除了工作日表,我需要在“月日”中获得一个额外的计算列,它总结前7天 - (例如,如果今天是21日,那么总和day_14,day_15,day_16 ... day_20)。计算必须每天更新,我认为这将是昂贵的。因此,额外的“工作日”表用于简单的静态计算。我认为简单快速的计算比小数据存储更重要。

提前致谢!

3 个答案:

答案 0 :(得分:4)

每当您看到名称中包含数字的列时,例如column_1,column_2,column_3 ......您的'可怕的数据库设计'标志应该会引发。 (仅供参考,这里你打破了1NF,特别是你repeating groups across columns

现在,这种实现在生产中可能是可接受的(甚至是必要的),但从概念上讲它确实是错误的。

正如Geert所说,概念上两个表就足够了。如果性能是一个问题,你可以对每周/每月统计数据进行非规范化,但我仍然不会像上面那样对它们进行建模,但我会保留

CREATE TABLE base_stats ( link_id INT, click_time DATETIME )
CREATE TABLE daily_stats ( link_id INT, period DATETIME, clicks INT )

您始终可以与

汇总
SELECT link_id, count(*) as clicks, DATE(click_time) as day
FROM base_stats
GROUP_BY link_id, day

可以定期运行以填充daily_stats。如果你想让它保持最新,你可以在触发器中实现它(或者如果你真的必须在应用程序端执行它)。如有必要,您还可以在不同级别上对数据进行非规范化(通过创建更多聚合表,或在聚合数据表中引入另一列),但这可能是过早优化。

上述设计对于未来的临时分析来说会更加清晰(将使用统计数据)。有关其他好处,请参阅维基百科重复小组。

编辑: 即使接受了包含两个表base_statsaggregated_stats的解决方案,也采用以下策略:

  • base_stats
  • 中插入每次点击
  • 定期将base_stats的数据汇总到daily_stats并清除完整的详细信息

它可能不是最佳解决方案。 根据对要求的讨论和澄清,似乎没有必要使用表base_stats。还应研究以下方法:

CREATE TABLE period_stats ( link_id INT, period DATETIME, ...)

更新很容易
UPDATE period_stats 
SET clicks = clicks + 1 
WHERE period = @dateTime AND link_id = @url AND ...

正确索引更新此表的成本与在base_table中插入行的效率一样高,而且任何它也很容易用于分析

SELECT link_id, SUM(clicks)
FROM period_stats
WHERE period between @dateTime1 AND @dateTime2
GROUP BY ...

答案 1 :(得分:3)

您在数据库中进行的非规范化可以很好地解决某些问题。但是在我的情况下,我不会选择上述解决方案,主要是因为您丢失了将来可能需要的信息,也许您希望将来以半小时的间隔报告。 所以看看你的描述你只能用2个表:链接(ahref和描述)和点击链接(包含点击的日期和时间,也许还有其他一些数据)。当然,缺点是您必须存储数千条记录,并且查询这些数据可能需要花费大量时间。如果是这种情况,您可以考虑将这两个表的聚合数据存储在单独的表中,并定期更新这些表。

答案 2 :(得分:2)

那个设计非常糟糕。不合理的提议更好。
如果你想让它变得简单易行,你也可以拥有一个包含2个字段的表:

   timeSlice  
   clickCount  
   location
   userType 

使用TimeSlice将日期和时间四舍五入到小时。 所有其余的都可以从中扣除,而你只有 24 * 365 *地点#*类型#
每年的记录。

总是依赖于配置和可行性,使用此表设计,您最终可以在内存中累积值,并且每10秒仅更新一次表。或任何时间长度<= 1小时,取决于可接受的风险