Sql 如何在列发生更改时选择列?
我试图创建一个缓慢变化的维度(类型2维度),但我对如何逻辑地写出它有点迷茫。假设我们有一个源表,其中包含Sql 如何在列发生更改时选择列?,sql,postgresql,netezza,Sql,Postgresql,Netezza,我试图创建一个缓慢变化的维度(类型2维度),但我对如何逻辑地写出它有点迷茫。假设我们有一个源表,其中包含Person | Country | Department | Login Time。我想用Person | Country | Department | Eff Start time | Eff End time创建此维度表 数据可能如下所示: Person | Country | Department | Login Time -------------------------------
Person | Country | Department | Login Time
。我想用Person | Country | Department | Eff Start time | Eff End time
创建此维度表
数据可能如下所示:
Person | Country | Department | Login Time
------------------------------------------
Bob | CANADA | Marketing | 2009-01-01
Bob | CANADA | Marketing | 2009-02-01
Bob | USA | Marketing | 2009-03-01
Bob | USA | Sales | 2009-04-01
Bob | MEX | Product | 2009-05-01
Bob | MEX | Product | 2009-06-01
Bob | MEX | Product | 2009-07-01
Bob | CANADA | Marketing | 2009-08-01
我想要的类型2维度如下所示:
Person | Country | Department | Eff Start time | Eff End Time
------------------------------------------------------------------
Bob | CANADA | Marketing | 2009-01-01 | 2009-03-01
Bob | USA | Marketing | 2009-03-01 | 2009-04-01
Bob | USA | Sales | 2009-04-01 | 2009-05-01
Bob | MEX | Product | 2009-05-01 | 2009-08-01
Bob | CANADA | Marketing | 2009-08-01 | NULL
假设Bob的姓名、国家/地区和部门自2009-08-01以来没有更新过,因此保留为NULL
什么功能在这里最有效?这是在Netezza上,它使用Postgres的风格
显然,groupby
在这里不起作用,因为后面会有相同的分组(我在最后一行添加了Bob | CANADA | Marketing
,以显示这一点)
编辑
包括一个关于个人、国家和部门的哈希列,将有意义,正确地考虑使用
SELECT PERSON, COUNTRY, DEPARTMENT
FROM table t1
where
person = person
AND t1.hash <> hash_function(person, country, department)
选择人员、国家/地区、部门
来自表t1
哪里
人
和t1.hash散列函数(个人、国家、部门)
答案
此代码返回下表
PERSON | COUNTRY | DEPARTMENT | EFF_LOGIN_START | EFF_LOGIN_END
--------+---------+------------+-----------------+---------------
Bob | CANADA | Marketing | 2009-01-01 | 2009-03-01
Bob | USA | Marketing | 2009-03-01 | 2009-04-01
Bob | USA | Sales | 2009-04-01 | 2009-05-01
Bob | MEX | Product | 2009-05-01 | 2009-08-01
Bob | CANADA | Marketing | 2009-08-01 |
解释
在过去的几年里,我一定已经解决了四五次这个问题,但一直没有把它正式写下来。我很高兴有机会这样做,所以这是一个很好的问题
在尝试这一点时,我喜欢用矩阵形式写下问题。这里是输入,假设所有值在SCD中都有相同的键
Cv | Ce
----|----
A | 10
A | 11
B | 14
C | 16
D | 18
D | 25
D | 34
A | 40
其中Cv是我们需要比较的值(同样,假设SCD的键值在此数据中相等;我们将在整个时间内对键值进行分区,因此它与解决方案无关),Ce是事件时间
首先,我们需要一个顺序主键。我在表中指定了这个Ck。这将允许我们将表连接到它自己,以获得上一个和下一个事件。我将这些列称为Pk(上一个键)、Nk(下一个键)、Pv和Nv
Cv | Ce | Ck | Pk | Pv | Nk | Nv |
----|----|----|----|----|----|----|
A | 10 | 1 | | | 2 | A |
A | 11 | 2 | 1 | A | 3 | B |
B | 14 | 3 | 2 | A | 4 | C |
C | 16 | 4 | 3 | B | 5 | D |
D | 18 | 5 | 4 | C | 6 | D |
D | 25 | 6 | 5 | D | 7 | D |
D | 34 | 7 | 6 | D | 8 | A |
A | 40 | 8 | 7 | D | | |
现在我们需要一些列来查看我们是否在连续事件块的开头或结尾。我将调用这些Pc和Nc,因为它们是连续的。Pc被定义为Pv=Cv=>true。1表示true,0表示false。Nc的定义与此类似,只是null大小写默认为true(我们将在一分钟后了解原因)
现在你可以开始看到Pc,Nc的1,1组合是一个完全无用的记录。我们直观地知道这一点,因为Bob在第6排的Mex/产品组合在构建SCD时几乎是无用的信息
所以,让我们去掉无用的信息。我将在这里添加两个新列:几乎完整的有效开始时间(称为Sn)和实际完整的有效结束时间(称为Ee)。当Pc为0时,Sn用Ce填充,当Nc为0时,Ee用Ce填充
Cv | Ce | Ck | Pk | Pv | Nk | Nv | Pc | Nc | Sn | Ee |
----|----|----|----|----|----|----|----|----|----|----|
A | 10 | 1 | | | 2 | A | 0 | 1 | 10 | |
A | 11 | 2 | 1 | A | 3 | B | 1 | 0 | | 11 |
B | 14 | 3 | 2 | A | 4 | C | 0 | 0 | 14 | 14 |
C | 16 | 4 | 3 | B | 5 | D | 0 | 0 | 16 | 16 |
D | 18 | 5 | 4 | C | 6 | D | 0 | 1 | 18 | |
D | 25 | 6 | 5 | D | 7 | D | 1 | 1 | | |
D | 34 | 7 | 6 | D | 8 | A | 1 | 0 | | 34 |
A | 40 | 8 | 7 | D | | | 0 | 1 | 40 | |
这看起来非常接近,但我们仍然存在无法按简历(个人/国家/部门)分组的问题。我们需要的是Sn用Sn的前一个值填充所有这些空值。您可以在rwn
上将此表连接到自身并获得最大值,但我会很懒,使用Netezza的分析函数和rows unbounded previous
子句。这是我刚才描述的方法的快捷方式。因此,我们将创建另一个名为Es的列,effectivestart,定义如下
case
when Sn is null
then max(Sn) over (
partition by k --key value of the SCD
order by Ck
rows unbounded preceding
)
else Sn
end Es
根据这个定义,我们得到了这个
Cv | Ce | Ck | Pk | Pv | Nk | Nv | Pc | Nc | Sn | Ee | Es |
----|----|----|----|----|----|----|----|----|----|----|----|
A | 10 | 1 | | | 2 | A | 0 | 1 | 10 | | 10 |
A | 11 | 2 | 1 | A | 3 | B | 1 | 0 | | 11 | 10 |
B | 14 | 3 | 2 | A | 4 | C | 0 | 0 | 14 | 14 | 14 |
C | 16 | 4 | 3 | B | 5 | D | 0 | 0 | 16 | 16 | 16 |
D | 18 | 5 | 4 | C | 6 | D | 0 | 1 | 18 | | 18 |
D | 25 | 6 | 5 | D | 7 | D | 1 | 1 | | | 18 |
D | 34 | 7 | 6 | D | 8 | A | 1 | 0 | | 34 | 18 |
A | 40 | 8 | 7 | D | | | 0 | 1 | 40 | | 40 |
其余的都很简单。按Es分组并获取Ee的最大值以获得此表
Cv | Es | Ee |
----|----|----|
A | 10 | 11 |
B | 14 | 14 |
C | 16 | 16 |
D | 18 | 34 |
A | 40 | |
如果要用下一个开始填充有效结束时间,请将表再次连接到表本身或使用
lead()
窗口函数来获取它。你能详细说明你的要求吗?当你说“选择列”时,你到底想做什么?举一个你想要的例子会很有帮助。是的,我可以用一个更好的标题。基本上,在我给出的例子中,我想找到一个人、他们的国家和t他们的部门在历史上的某个时刻或目前的某个时刻。为了做到这一点,由于这个维度可能会发生变化,我想找到,当这些列为您指定的编辑而发生变化时,您是否应该得到您期望的输出?与以前相同的输出?您希望代码生成维度?还是加入维度?另外,您的hash_函数
建议可以在Netezza上使用,但您也可以很容易地不使用预先计算的区域映射。只要让Netezza按照优化器想要做的任何事情进行比较即可。
Cv | Ce | Ck | Pk | Pv | Nk | Nv | Pc | Nc | Sn | Ee | Es |
----|----|----|----|----|----|----|----|----|----|----|----|
A | 10 | 1 | | | 2 | A | 0 | 1 | 10 | | 10 |
A | 11 | 2 | 1 | A | 3 | B | 1 | 0 | | 11 | 10 |
B | 14 | 3 | 2 | A | 4 | C | 0 | 0 | 14 | 14 | 14 |
C | 16 | 4 | 3 | B | 5 | D | 0 | 0 | 16 | 16 | 16 |
D | 18 | 5 | 4 | C | 6 | D | 0 | 1 | 18 | | 18 |
D | 25 | 6 | 5 | D | 7 | D | 1 | 1 | | | 18 |
D | 34 | 7 | 6 | D | 8 | A | 1 | 0 | | 34 | 18 |
A | 40 | 8 | 7 | D | | | 0 | 1 | 40 | | 40 |
Cv | Es | Ee |
----|----|----|
A | 10 | 11 |
B | 14 | 14 |
C | 16 | 16 |
D | 18 | 34 |
A | 40 | |