缓慢变化的维度 - 准确的 SQL 查询实现以检索正确的数据
Posted
技术标签:
【中文标题】缓慢变化的维度 - 准确的 SQL 查询实现以检索正确的数据【英文标题】:Slowly Changing Dimensions - exact SQL query implementation to retrieve correct data 【发布时间】:2015-08-21 21:51:52 【问题描述】:我对 BI 开发/数据仓库有点陌生,但我面临着旧的缓慢变化维度的困境。我已经阅读了很多关于类型和理论的内容,但在我认为针对这些实现的最常见的 SELECT 查询方面几乎没有发现。
我将保持我的示例简单。假设您有四个销售原因,东、西、北和南。您有一组销售人员每天进行销售,并且(可能一年一次)被重新分配到一个新区域。
因此您将获得如下原始数据:
name; sales; revenue; date
John Smith; 10; 5400; 2015-02-17
你每天都有这样的数据。
您最初也可能有一个如下所示的维度表:
name; region
John Smith; East
Nancy Ray; West
Claire Faust; North
所以销售总监想知道东部地区 2015 年 5 月的月销售收入。您将执行一个查询:
SELECT region, month(date), sum(revenue)
from Fact_Table inner join Dim_Table on name = name
where region = East and date between ....
[group by region, month(date)]
你明白了。让我们忽略我使用自然键而不是代理整数键;我显然会使用代理键。
现在,很明显,销售人员可能会在年中调换地区。或月中。因此,您必须创建一个 SCD 类型才能运行此查询。就我个人而言,Type 2 最有意义。所以说你实现了。假设 John Smith 于 2015 年 5 月 15 日从东部地区更改为西部地区。您实施下表:
name; region; start_date; end_date
John Smith; East; 2015-01-01; 2015-05-15
John Smith; West; 2015-5-15; 9999-12-31
现在销售总监问同样的问题。 2015 年 5 月东部地区的总销售收入是多少?或者,向我显示全年按月按地区划分的总数。您将如何构建查询?
SELECT region, month(date), sum(reveneue)
from Fact_Table inner join Dim_Table
on name = name
and date between start_date and end_date
group by region, month(date)
这会给出正确的结果吗?我想它可能 --- 我的问题可能更多的是 --- 好吧,现在假设你在 Fact 表中有 100 万条记录......这个内部连接会非常低效,还是有更快的方法来实现这个结果?
将 SCD(类似区域)直接写入“非规范化”事实表是否更有意义 --- 并且当维度发生变化时,可能会追溯更新一周或两周的事实记录“区域”?
【问题讨论】:
我认为您的示例没有帮助,因为区域和销售人员显然是不同的维度。您概述的结构使得为您要提出的基本问题有效地构建查询非常困难,这表明该模型是错误的。一旦你需要一个复杂的查询,你就必须重新评估你的维度结构。 实际上,这是大多数 SCD 文献中相当常见的示例。我的“现实生活”示例几乎相同。属于各个区域办事处(美国、欧洲和亚洲)的名称列表……某些应用程序没有直接的“区域”数据点……甚至没有“部门”数据点……这些是单独编码的.您所拥有的只是应用程序中的员工姓名。将此类员工转回区域办事处或部门(营销与客户服务)的“主要参考”……是基于时间的。我不明白你怎么说数据模型是错误的。 本质上,在我的示例中,从收入到区域的一个链接必须始终首先通过员工,作为数据的情况。如果将“地区”替换为“部门”,可能有助于理解。 该模型适用于 OLTP 系统,不适用于多维系统。当您将事实添加到表中时,您已经有了名称,并且您知道它们附加到哪个区域,因此您将两者添加为不同的键。显然,从报告的角度来看,重要的问题是按地区划分的收入、按名称划分的收入以及按区域和名称划分的收入。您的 SCD 结构使第二个相当棘手。或者,如果您有维度的合成键,则只需为名称和区域的每个有效组合创建一个新的 dim 值。 是的,最好在生成事实表时写入正确的“区域”(因为员工 ID 将在事实表中)......但是......我正在工作假设有必要进行追溯更正。 Aka 有人会改变地区和人力资源/技术团队/我们的流程可能在一两个星期内不会注意到这一点。我想我仍然可以在编写事实表时将区域 ID 附加到事实表,但为了保持数据完整性并能够进行追溯更新,我仍然需要一个“生效日期”表。也许只是为了更正与稳定连接 【参考方案1】:如果您的业务需求具有 Region->Seller 层次结构,则您的概念是正确的,如您的示例所示。
当前查询的性能可能具有挑战性,但可以通过使用适当的维度键和属性来改进。
使用包含日期->月份的日期维度层次结构,您将能够避免范围查询。
在两个维度中使用整数、代理、键,您的索引性能将会提高。
一百万行很小,您不会在任何合格的 DBMS 上遇到性能问题 :)
【讨论】:
是的,这很有道理——我会记住这些提示。日期维度层次结构是什么意思?您的意思是在具有“有效”日期的维度表中——在插入/表写入时也将月份写入表中? 对于日期,你应该有一个类似这样的维度: dim_date(id,date_business_key,day,month,year,day_of_week, month_name, etc) 你的事实表应该只包含一个外键引用到 dim_date 表。要查找给定月份的销售额,请查询您的 fact + dim_date 的连接,其中年份和月份是您需要的值。 这是有道理的,除了,如果一个推销员只在“东部”半个月,从 6 月 1 日到 6 月 15 日......那么对东部范围的“总”求和仍然需要按日期粒度拆分数据,而不仅仅是月份。 这实际上是由日期维度处理的。我不能在这里进行格式化,所以我希望你能理解这个查询:) select Seller,sum(sale_amount) from fact_sale inner join dim_date on dim_date.id = fact_sale.date_id inner join dim_seller on dim_seller.id = fact_sale.seller_id where dim_date.year = 2015 和 dim_date.month = 6 和 dim_seller.region = 'Northern'。这将为您提供每个卖家在 2015 年 6 月在北部地区进行销售时的总销售额。以上是关于缓慢变化的维度 - 准确的 SQL 查询实现以检索正确的数据的主要内容,如果未能解决你的问题,请参考以下文章