今天在做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, Max(Description) AS Description FROM B
GROUP 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的方式。