【问题标题】:Handling very big table in SQL Server Performance在 SQL Server 性能中处理非常大的表
【发布时间】:2018-02-02 19:23:54
【问题描述】:

我在处理数据库中的一个非常大的表时遇到了一些麻烦。在说问题之前,先说一下我想要达到的目标。

我有两个源表:

  • 来源 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)

这个想法是在目标表中使用ROW_NUMBER 为每个客户端找到最接近SALES_MAN 的前N 个。

我目前正在做的是计算每个客户和每个销售人员之间的距离:

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 兆行。

让每个客户离我最近的 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 中时,事情变得更好了。

那么在这种情况下,什么非聚集排序索引不起作用?

但是当我使用聚集索引时,我在加载数据时遇到了其他问题,我不得不在开始加载过程之前将其删除。

那你怎么看?以及我们如何处理这种表以便能够选择、插入或更新数据而不会出现性能问题?

非常感谢

【问题讨论】:

标签: sql-server performance sql-server-2016 clustered-index non-clustered-index


【解决方案1】:

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 列上都添加了索引

【讨论】:

  • 您好,感谢您的回复。我可以过滤我的数据,因为我必须按距离对其进行排序,并且只选择 rang
  • 为了获得良好的性能,应该使用ROW_NUMBER在过滤后的结果中制作相同的数字
  • 在您的查询中无需使用 ROW_NUMBER() 因为您只选择距离前 5 个
  • 我在最终结果中使用 Row_Number 作为 Range 字段,虽然你需要它@HadriaMehdi
【解决方案2】:

评论太长,但请考虑以下几点。

第 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

【讨论】:

  • 谢谢,我会尝试第一个技巧以避免多余的函数调用。第二个技巧也非常出色,我会看看是否会违反业务规则。那么索引呢?你觉得没有必要吗?
  • @HadriaMehdi 我怀疑批量生成 1MM 记录,而您只需要其中的一小部分。索引就像感冒的鸡汤......不能受伤,
猜你喜欢
  • 1970-01-01
  • 2020-01-07
  • 1970-01-01
  • 2011-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多