前言
本文主要是總結平時工作學習中遇到的使用Sql Server的去除重復的心得體會。
由於平時工作使用Sql並不多,此次在寫本文的測試過程中,就遇到了問題,如能有幸得到高手點播,將不勝感激。
高手可以直接看個開頭,直接跳過文章內容,點到后面的遇到的問題,辛苦!
准備
本文使用的工具是SQL SERVER 2008,使用的是微軟的案例Northwind,選取的數據集以Products表的前10條數據為例,如下圖:
Distinct
根據之后緊跟關鍵字distinct后的字段去除重復,而distinct只能放在所有要查詢字段的前面。distinct后的字段有一個不一樣即為不同。
示例:根據SupplierID,CategoryID去除重復的內容
Select distinct a.SupplierID,a.CategoryID from (SELECT TOP 10 [ProductID] ,[ProductName] ,[SupplierID] ,[CategoryID] ,[QuantityPerUnit] ,[UnitPrice] ,[UnitsInStock] ,[UnitsOnOrder] ,[ReorderLevel] ,[Discontinued] FROM [Northwind].[dbo].[Products]) a
獲得結果:
Group by
指定由查詢 (SELECT) 表達式返回的對象要分入的組。使用group by時可以巧妙地使用聚合函數達到去除重復的目的。
Select Max(a.ProductID) as ID,a.CategoryID ,a.SupplierID from (SELECT TOP 10 [ProductID] ,[ProductName] ,[SupplierID] ,[CategoryID] ,[QuantityPerUnit] ,[UnitPrice] ,[UnitsInStock] ,[UnitsOnOrder] ,[ReorderLevel] ,[Discontinued] FROM [Northwind].[dbo].[Products]) a group by a.CategoryID ,a.SupplierID
獲得結果:
這次可以獲得去除重復過程中ID最大(獲取ID最小列可以使用Min函數)的數據行,有了ID唯一標識列就可以解決上面distinct遺留下來的問題。
內聯原來的表就可以獲取想要的任意字段的值了。
順帶附上Min函數的結果:
Row_Number() over()
over()里面有兩個參數
Partition by value_expression
將 FROM 子句生成的結果集划入應用了 ROW_NUMBER 函數的分區。 value_expression 指定對結果集進行分區所依據的列。 如果未指定 PARTITION BY,則此函數將查詢結果集的所有行視為單個組。
Select a.ProductID,a.SupplierID,a.CategoryID, ROW_NUMBER() over(partition by CategoryID ,SupplierID order by ProductID)as RowN from ( SELECT TOP 10 [ProductID] ,[ProductName] ,[SupplierID] ,[CategoryID] ,[QuantityPerUnit] ,[UnitPrice] ,[UnitsInStock] ,[UnitsOnOrder] ,[ReorderLevel] ,[Discontinued] FROM [Northwind].[dbo].[Products]) a
獲得結果:
Note:此處的數據稍微有點問題,最后會說到。
此次並沒有達到去除重復的結果,但稍微看下就發現了多了一行RowN。
這個是根據SupplierID,CategoryID分區並根據ProductID升序獲得的行號。所以去除重復也就非常容易了。
Select* from ( Select a.ProductID,a.SupplierID,a.CategoryID, ROW_NUMBER() over(partition by CategoryID ,SupplierID order by ProductID)as RowN from ( SELECT TOP 10 [ProductID] ,[ProductName] ,[SupplierID] ,[CategoryID] ,[QuantityPerUnit] ,[UnitPrice] ,[UnitsInStock] ,[UnitsOnOrder] ,[ReorderLevel] ,[Discontinued] FROM [Northwind].[dbo].[Products]) a) b where b.RowN=1
獲得結果:
順帶附上b.RowN=2結果:
面試問題
取出某年某月每一天的記錄的第一條
姑且認為每天第一條記錄是當天ID最小的那條,以下為測試使用數據集
SELECT [OrderID] ,[CustomerID] ,[EmployeeID] ,[OrderDate] FROM [Northwind].[dbo].[Orders] where DATEPART(YEAR,OrderDate)=1997 AND DATEPART(MONTH,OrderDate)=1
方法一:Group by
with Dataset as (SELECT [OrderID] ,[CustomerID] ,[EmployeeID] ,[OrderDate] FROM [Northwind].[dbo].[Orders] where DATEPART(YEAR,OrderDate)=1997 AND DATEPART(MONTH,OrderDate)=1) Select a.* from Dataset a, (SELECT Min([OrderID]) as ID ,DATEPART(DAYOFYEAR,OrderDate) as dayofOrder FROM [Northwind].[dbo].[Orders] where DATEPART(YEAR,OrderDate)=1997 AND DATEPART(MONTH,OrderDate)=1 group by DATEPART(DAYOFYEAR,OrderDate)) b where a.OrderID=b.ID
獲得結果:
方法二:Row_Number() over()
with Dataset as (SELECT [OrderID] ,[CustomerID] ,[EmployeeID] ,[OrderDate] FROM [Northwind].[dbo].[Orders] where DATEPART(YEAR,OrderDate)=1997 AND DATEPART(MONTH,OrderDate)=1) select a.* from(Select *,ROW_NUMBER() over(Partition by DatePart(dayofyear,OrderDate) order by OrderID) as RowN from Dataset) a where a.RowN=1
獲得結果:
小結:從以上兩種方法可以明顯感覺到第二種方法的優勢,更強的靈活性,可以獲得每天的第二條甚至更多,而且Order by排序有更多選擇。
總結
本次關於Sql去除重復的總結就寫完了,如果大家還有其他好的方法,還請分享出來。
文中如有錯誤或者描述不當的地方,還請指出!謝謝!
如有興趣,繼續看下面的問題,幫忙解決了,我將不勝感激!
參考資料:http://stackoverflow.com/questions/3800551/select-first-row-in-each-group-by-group
怪異的問題
測試過程中發現看下圖:
這里面的獲取的數據明顯不是我想要的數據(可以參照准備里的數據集),我測試其他字段都沒有問題,只有單獨獲取CategoryID字段的時候有問題,求高手指點。
附:測試數據庫下載