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记录,而你只需要一小部分。一个指数就像一个感冒的鸡汤....不能;不受伤害, –