問題背景
在一個多表查詢的sql中正常情況下產生的數據都是唯一的,但因為數據庫中存在錯誤(某張表中存在相同的外鍵ID)導致我這邊查詢出來的數據就會有重復的問題
下面結果集中UserID:15834存在多個

查詢Sql如下:
SELECT * FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY T.USERID asc )AS Row ,T.USERID ,T.CreateTime FROM UserInfo T LEFT JOIN DiseaseInfo i ON i.UserID=T.UserID ) TT WHERE TT.Row between 0 AND 20 ORDER BY UserID DESC
解決方法:
參考下面新的解決方案
在網絡上了解到MSSql中通過關鍵字“
PARTITION BY”
可以將查詢結果集進行分區處理,然后在查詢結果集時就可以過濾掉重復的記錄了(如果有指定分區字段則區ID相同)
通過更改后的Sql,在Over中添加
PARTITION BY T.
USERID
以UserID進行分區,然后在查詢結果集時通過
DISTINCT ROW ,
過濾掉重復的分區ID號
SELECT DISTINCT ROW ,* FROM ( SELECT ROW_NUMBER() OVER (PARTITION BY T.USERID ORDER BY T.USERID asc )AS Row ,T.USERID ,T.CreateTime FROM UserInfo T LEFT JOIN DiseaseInfo i ON i.UserID=T.UserID ) TT WHERE TT.Row between 0 AND 12 ORDER BY UserID DESC
查詢時未過濾重復分區ID
DISTINCT ROW ,
下面的結果集跟上面的結果集不同(Row是進行過分區的所有有重復Row)

在查詢結果集時過
濾掉重復的分區ID號
DISTINCT ROW ,

新解決方案:
由於在Sqlserver中如果多表聯合查詢中除非所有的字段都完全相同否則在使用
DISTINCT
用進行去重時還是會當成兩個不同的數據集進行處理,因此
DISTINCT
會失效即
如下面的結果集,雖然 USERID和其他字段內容相同但HID是不相同的所以無法使用
DISTINCT
進行去重
出現這種問題是因為數據庫設計的錯誤(正常情況下關聯表
HospitalInfo
中只可能存在一條
ClinicInfo
表對應的
記錄)
Sql語句:
SELECT * FROM ( SELECT ROW_NUMBER() OVER ( order by T.USERID asc )AS Row ,T.USERID ,LEFT(T.Patient_Tel1,5)+'00000000' AS Tel ,T.CreateTime ,h.HName ,h.HID fromUserInfo T LEFT JOIN ClinicInfo c ON c.UserID=T.UserID AND C.Disabled=1 LEFT JOIN HospitalInfo H ON H.HID=c.VisitHospital WHERE T.Disabled=1 AND t.UserID>=17867 AND T.UserID<=17875 --(T.Patient_Tel1 like '%13800000000%') ) TT WHERE TT.Row between 0and20

可以看到上面的結果集中Row是有重復的,其他Row為2的是跟第一個是重復的
因為數據庫涉及到其他業務和人員因此我只能提交該問題給相關的技術,但在該問題解決前不能影響到我這邊也出現此問題
於是在原sql基礎上進行處理,雖然
HospitalInfo
表中不重復記錄但表的自增ID是不可能重復的那我只需要最新的一條記錄即可
如果通過
DISTINCT
過進行去重則就無法成功,因為數據存在差別,可以看到第一條和最后一條數據還是重復的
SELECT DISTINCT row,* FROM ( SELECT ROW_NUMBER() OVER ( partition by T.USERID order by T.USERID asc )AS Row ,T.USERID ,LEFT(T.Patient_Tel1,5)+'00000000' AS Tel ,T.CreateTime ,h.HName ,h.HID fromUserInfo T LEFT JOIN ClinicInfo c ON c.UserID=T.UserID AND C.Disabled=1 LEFT JOIN HospitalInfo H ON H.HID=c.VisitHospital WHERE T.Disabled=1 AND t.UserID>=17867 AND T.UserID<=17875 --(T.Patient_Tel1 like '%13800000000%') ) TT WHERE --row=1 AND TT.Row between 0 and 20

更改后的Sql
SELECT * FROM ( --partition by T.USERID 以UserID對結果集進行分區 SELECT ROW_NUMBER() OVER ( partition by T.USERID order by T.USERID asc )AS Row ,T.USERID ,LEFT(T.Patient_Tel1,5)+'00000000' AS Tel ,T.CreateTime ,h.HName ,h.HID fromUserInfo T LEFT JOIN ClinicInfo c ON c.UserID=T.UserID AND C.Disabled=1 LEFT JOIN HospitalInfo H ON H.HID=c.VisitHospital WHERE T.Disabled=1 AND t.UserID>=17867 AND T.UserID<=17875 --(T.Patient_Tel1 like '%13800000000%') ) TT WHERE --因為之前已經以UserID對結果集進行分區,所以如果存在重復的字段則row的值會不相同 --row=1 AND TT.Row between 0 and 20
USERID=17867相同經過分區后會存在不同的Row值

在對結果集再次過濾時添加條件 :
row=1,
已經將重復記錄中舊的數據過濾掉了
(HID:78)

根據新的解決方案解決了重復的問題,但又出現的新的問題即Row分區后都是重復的,而我再進行分頁的時候就無效了(因為此時結果集中的Row都是為1)
解決方案:在結果集再加一層查詢並加上ID號然后再對結果集進行分頁處理
-- 新增一層查詢解決過濾掉重復數據后無法分頁的問題 SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY userid) AS RowNum,* FROM ( --partition by T.USERID 以UserID對結果集進行分區 SELECT ROW_NUMBER() OVER ( partition by T.USERID order by T.USERID asc )AS Row ,T.USERID ,LEFT(T.Patient_Tel1,5)+'00000000' AS Tel ,T.CreateTime ,h.HName ,h.HID fromUserInfo T LEFT JOIN ClinicInfo c ON c.UserID=T.UserID AND C.Disabled=1 LEFT JOIN HospitalInfo H ON H.HID=c.VisitHospital WHERE T.Disabled=1 AND t.UserID>=17867 AND T.UserID<=20875 --(T.Patient_Tel1 like '%13800000000%') ) TT )AS T WHERE --過濾重復數據 Row=1 --對結果進行分頁 AND RowNum between 13 and 24

參考: