今天在做SSIS的ETL工作时,其中一个left join组件的运行结果总是会多出一些记录。分析了一下,该问题的原因是右表中作为关联的那一列数据有重复。left join的运行策略可以理解为根据左表的每一条记录的关联字段去对照右表的关联字段,如果右表的关联字段存在重复,就会生成重复的记录。如果左表存在重复而右表无重复,则不会多出来记录。举个例子,如果左表a和右表b的数据分别如下所示
ID | Name |
1 | 张三 |
2 | 李四 |
3 | 王五 |
4 | 王陆 |
ID | Description |
1 | 内联部 |
1 | 系学生会 |
2 | 外联部 |
3 | 团委 |
这时如果用ID作为关联字段用a表left join b表,结果会产生5条记录,比左表多一条。(顺便提一下,如果右表不重复,则left join的结果数会与左表相等)
ID | Name | Description |
1 | 张三 | 内联部 |
1 | 张三 | 系学生会 |
2 | 李四 | 外联部 |
3 | 王五 | 团委 |
4 | 王陆 | NULL |
实际上,我想要的结果是与左表a一一对应,不要有重复的记录。这可以通过SSIS的lookup组件实现,但是效率会很低。因此就想到把右表中的重复记录去除掉再join两张表。首先自然地想到用distinct函数去重
SELECT DISTINCT ID, Description FROM B
结果却是1条记录都没去掉,因为Distinct是作用于多列的,也就是说必须要ID和Description全都相同的才会被剔除。
在网上搜了一下,有人说用 select *, count(distinct name) from table group by name 这样的语句是可行的,但我在SQL Server里面试了一下会报错。只好自己动手,丰衣足食啦,想了一下,其实可以用下面的语句
SELECT ID, (SELECT Max(Description) FROM B) AS Description FROM BGROUP BY ID
进一步的思考后发现,SQL Server中有First_Value和Last_Value函数,也可以实现
SELECT DISTINCT ID, FIRST_VALUE(Description) OVER (PARTITION BY ID ORDER BY Description) AS Description FROM B
第二种方法中Partition by的参数必须是ID,Order by的参数可以调整,这就使得该方法更加灵活。这两种方法经实测效率差不多,第一种稍微快一点点。不过遗憾的是SSIS中不支持第二种方法,只能用第一种group by的方式。