1

我在處理數據庫中的一個非常大的表格時遇到了一些麻煩。在討論這個問題之前,先談談我想達到的目標。在SQL Server性能上處理非常大的表格

我有兩個源表:

  • 源1:SALES_MAN (ID_SMAN, SM_LATITUDE, SM_LONGITUDE)
  • 源2:CLIENT (ID_CLIENT, CLATITUDE, CLONGITUDE)

  • 目標:DISTANCE (ID_SMAN, ID_CLIENT, SM_LATITUDE, SM_LONGITUDE, CLATITUDE, CLONGITUDE, DISTANCE)

的想法是找到頂部對於使用012的每個客戶,N最接近SALES_MAN目標表中的。

我在做什麼目前正在計算每一個客戶和每一個銷售人之間的距離:

INSERT INTO DISTANCE ([ID_SMAN], [ID_CLIENT], [DISTANCE], 
         [SM_LATITUDE], [SM_LONGITUDE], [CLATITUDE], [CLONGITUDE]) 
    SELECT 
     [ID_SMAN], [ID_CLIENT], 
     geography::STGeomFromText('POINT('+IND_LATITUDE+' '+IND_LONGITUDE+')',4326).STDistance(geography::STGeomFromText('POINT('+DLR.[DLR_N_GPS_LATTITUDE]+' '+DLR.[DLR_N_GPS_LONGITUDE]+')',4326))/1000 as distance, 
     [SM_LATITUDE], [SM_LONGITUDE], [CLATITUDE], [CLONGITUDE] 
    FROM 
     [dbo].[SALES_MAN], [dbo].[CLIENT] 

DISTANCE表中包含大約1 milliards行。

第二步讓每個客戶端我5最近的銷售人是無法運行此查詢:

SELECT * 
FROM 
    (SELECT 
     *, 
     ROW_NUMBER() OVER(PARTITION BY ID_CLIENT ORDER BY DISTANCE) rang 
    FROM DISTANCE) TAB 
WHERE rang < 6 

最後查詢確實是一件費時的。所以爲了避免SORT運算符,我嘗試在DISTANCE和ID_CLIENT中創建一個排序的非聚集索引,但它不起作用。我也嘗試在兩個索引中包含所有需要的列。

但是,當我在DISTANCE上創建聚簇索引並將非聚簇排序索引保留在ID_CLIENT中時,情況變得更好。

那麼在這種情況下非聚類排序索引不起作用?

但是,當我使用聚集索引,我有加載數據的其他問題,我有點強迫刪除它開始加載過程之前。

那麼你怎麼看?以及我們如何處理這種表格,以便能夠選擇,插入或更新數據而不存在性能問題?

非常感謝

+1

[不良習慣踢:使用舊樣式的JOIN(http://sqlblog.com/ blogs/aaron_bertrand/archive/2009/10/08/bad-habits-to-kick-using-old-style-joins.aspx) - 舊式*逗號分隔的table * style列表被替換爲* proper *在ANSI - ** 92 ** SQL標準(** 25年**前)中使用ANSI'JOIN'語法,不鼓勵使用 –

回答

0

ROW_NUMBER是一個窗口函數需要與ORDER BY的列相關的整個行,以便其更好地過濾ROW_NUMBER之前,你的結果,

,你已經改變以下代碼:

SELECT * FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY ID_CLIENT ORDER BY DISTANCE) 
rang FROM DISTANCE 
) TAB 

WHERE rang < 6 

到這一點:

WITH DISTANCE_CLIENT_IDS (CLIENT_ID) AS 
(
    SELECT DISTINCT CLIENT_ID 
    FROM DISTANCE 
) 

SELECT Dx.* 
FROM DISTANCE_CLIENT_IDS D1, 
(
    SElECT * , ROW_NUMBER(ORDER BY DISTANCE) RANGE 
    FROM (
    SELECT TOP(5) * 
    FROM DISTANCE D2 
    WHERE D1.CLIENT_ID = D2.CLIENT_ID 
    ) Dt 
) Dx 

,並確保您在CLIENT_ID和DISTANCE列中添加了索引

+0

您好,感謝您的回覆。我可以過濾我的數據,因爲我必須按距離對它進行排序,並且只選擇響應<6的行。想法是爲每個ID_CLIENT選擇5個最近的銷售人員。 –

+0

爲了獲得良好的性能,應使用ROW_NUMBER在結果**處使用相同的數字進行過濾** –

+0

在您的查詢中不需要使用ROW_NUMBER(),因爲您只選擇前五位的距離 –

1

評論的時間太長,但請考慮以下幾點。

項目1)考慮在每個源表格中添加一個Geography字段。這將消除冗餘GEOGRAPHY::Point()函數調用

Update YourTable Set GeoPoint = GEOGRAPHY::Point([Lat], [Lng], 4326) 

所以然後用於距離計算將僅僅是

,InMeters = C.GeoPoint.STDistance(S.GeoPoint) 
    ,InMiles = C.GeoPoint.STDistance(S.GeoPoint)/1609.344 


項目2)而不是生成每個可能的組合,考慮給JOIN添加一個條件。請記住,Lat或Lng的每個「1」大約爲69英里,因此您可以縮小搜索範圍。例如

From CLIENT C 
Join SALES_MAN S 
    on S.Lat between C.Lat-1 and C.Lat+1 
and S.Lng between C.Lng-1 and C.Lng+1 

+/- 1可以是任何合理的價值...(即0.5甚至2.0

+0

謝謝,我會嘗試第一個技巧,以避免多餘的函數調用。另外第二個技巧是非常出色的,我會看到如果業務規則不會被違反這樣做。那麼索引呢?你認爲他們沒有必要嗎? –

+0

@HadriaMehdi我懷疑大部分是產生1MM記錄,而你只需要一小部分。一個指數就像一個感冒的雞湯....不能;不受傷害, –