SQL Server分页查询使用where子句选择空间几何数据与不使用相比非常慢

问题描述 投票:0回答:1

我遇到了奇怪的需求组合,这些组合会降低查询的性能。这是由 ESRI 的 ArcGIS 服务器在 SQL Server 中创建的表,查询来自对表中数据进行分页的要素服务。

这很慢(粘贴计划)

SELECT
    OBJECTID,
    Shape
FROM 
    dbo.PARCELS   
WHERE  
    dbo.PARCELS.GDB_ARCHIVE_OID IN 
        (SELECT GDB_ARCHIVE_OID 
         FROM  
             (SELECT 
                  GDB_ARCHIVE_OID,
                  ROW_NUMBER() OVER(PARTITION BY OBJECTID ORDER BY GDB_FROM_DATE DESC) rn_, 
                  GDB_IS_DELETE 
              FROM 
                  dbo.PARCELS 
              WHERE  
                  ((GDB_BRANCH_ID = 0 AND GDB_FROM_DATE <= '2023-12-22') 
                   OR (GDB_BRANCH_ID = 1 AND GDB_FROM_DATE <= '2023-12-22'))) br__ 
         WHERE 
             br__.rn_ = 1 AND br__.GDB_IS_DELETE = 0)  
ORDER BY 
    OBJECTID ASC 
    OFFSET 2000 ROWS 
        FETCH NEXT 2000 ROWS ONLY 

没有几何数据类型的形状字段,速度很快(粘贴计划)

SELECT
    OBJECTID
    --,Shape
FROM
    dbo.PARCELS   
WHERE  
    dbo.PARCELS.GDB_ARCHIVE_OID IN  
        (SELECT GDB_ARCHIVE_OID 
         FROM  
             (SELECT 
                  GDB_ARCHIVE_OID,
                  ROW_NUMBER() OVER(PARTITION BY OBJECTID ORDER BY GDB_FROM_DATE DESC) rn_, 
                  GDB_IS_DELETE 
              FROM 
                  dbo.PARCELS 
              WHERE  
                  ((GDB_BRANCH_ID = 0 AND GDB_FROM_DATE <= '2023-12-22') 
                   OR (GDB_BRANCH_ID = 1 AND GDB_FROM_DATE <= '2023-12-22'))) br__ 
         WHERE 
             br__.rn_ = 1 AND br__.GDB_IS_DELETE = 0)  
ORDER BY 
    OBJECTID ASC 
    OFFSET 2000 ROWS 
        FETCH NEXT 2000 ROWS ONLY 

没有where子句,速度很快(粘贴计划)

SELECT
    OBJECTID,
    Shape
FROM
    dbo.PARCELS   
--    WHERE  DBO.PARCELS.GDB_ARCHIVE_OID IN  (
--      SELECT GDB_ARCHIVE_OID 
--      FROM  (
--          SELECT GDB_ARCHIVE_OID,ROW_NUMBER() OVER(PARTITION BY OBJECTID ORDER BY GDB_FROM_DATE DESC) rn_, GDB_IS_DELETE 
--          FROM DBO.PARCELS 
--          WHERE  ((GDB_BRANCH_ID = 0 AND GDB_FROM_DATE <= '2023-12-22') OR (GDB_BRANCH_ID = 1 AND GDB_FROM_DATE <= '2023-12-22'))
--          ) br__ 
--      WHERE br__.rn_ = 1 AND br__.GDB_IS_DELETE = 0
--      )  
ORDER BY 
    OBJECTID ASC 
    OFFSET 2000 ROWS 
        FETCH NEXT 2000 ROWS ONLY 

不用分页就这么快(贴计划)

SELECT
    OBJECTID,
    Shape
FROM
    dbo.PARCELS   
WHERE  
    dbo.PARCELS.GDB_ARCHIVE_OID IN  
        (SELECT 
             GDB_ARCHIVE_OID 
         FROM  
             (SELECT 
                  GDB_ARCHIVE_OID,
                  ROW_NUMBER() OVER(PARTITION BY OBJECTID ORDER BY GDB_FROM_DATE DESC) rn_, 
                  GDB_IS_DELETE 
              FROM 
                  dbo.PARCELS 
              WHERE  
                  ((GDB_BRANCH_ID = 0 AND GDB_FROM_DATE <= '2023-12-22') 
                   OR (GDB_BRANCH_ID = 1 AND GDB_FROM_DATE <= '2023-12-22'))) br__ 
         WHERE 
             br__.rn_ = 1 AND br__.GDB_IS_DELETE = 0)  
    ORDER BY 
        OBJECTID ASC 
    -- OFFSET 2000 ROWS 
    -- FETCH NEXT 2000 ROWS ONLY 

几何列、分页和 where 子句这三者的组合杀死了查询。如果没有其中任何一个,执行时间将低于一秒。三者的执行时间均为 41 秒。

下面是最后带有索引的表定义:

CREATE TABLE [dbo].[PARCELS](
    [OBJECTID] [int] NOT NULL,
    [TXID_NMBR] [nvarchar](25) NULL,
    [TAX_MAP] [nvarchar](10) NULL,
    [CONTROL] [numeric](38, 8) NULL,
    [PARCEL] [nvarchar](25) NULL,
    [TIE_BACK] [nvarchar](25) NULL,
    [LAST_NAME] [nvarchar](50) NULL,
    [FIRST_NAME] [nvarchar](50) NULL,
    [FULL_NAME] [nvarchar](255) NULL,
    [OWNER_ADD] [nvarchar](50) NULL,
    [OWNER_ADD2] [nvarchar](50) NULL,
    [OWNER_CITY] [nvarchar](50) NULL,
    [OWNER_ST] [nvarchar](2) NULL,
    [OWNER_ZIP] [nvarchar](10) NULL,
    [MUNIC] [nvarchar](25) NULL,
    [SITUS] [nvarchar](255) NULL,
    [SITUS_CITY] [nvarchar](50) NULL,
    [SITUS_ST] [nvarchar](2) NULL,
    [SITUS_ZIP] [nvarchar](10) NULL,
    [USE_] [nvarchar](10) NULL,
    [NBHD] [nvarchar](10) NULL,
    [STORIES] [nvarchar](50) NULL,
    [STYLE] [nvarchar](50) NULL,
    [EXTERIOR] [nvarchar](50) NULL,
    [YR_BUILT] [nvarchar](4) NULL,
    [GRADE] [nvarchar](50) NULL,
    [BEDROOMS] [nvarchar](2) NULL,
    [BATHS] [nvarchar](2) NULL,
    [HALF_BATHS] [nvarchar](2) NULL,
    [BSMT] [int] NULL,
    [FIN_BSMT] [int] NULL,
    [HEAT] [nvarchar](50) NULL,
    [CENT_AIR] [nvarchar](50) NULL,
    [FIREPLACE] [nvarchar](5) NULL,
    [TERRAIN] [nvarchar](10) NULL,
    [SEWER] [nvarchar](50) NULL,
    [WATER] [nvarchar](10) NULL,
    [LIV_AREA] [int] NULL,
    [GRANTOR] [nvarchar](255) NULL,
    [DEED] [nvarchar](50) NULL,
    [SALE_DATE] [datetime2](7) NULL,
    [SALE_AMT] [int] NULL,
    [VAL_2_SALE] [numeric](38, 8) NULL,
    [PP_ACRE] [numeric](38, 8) NULL,
    [PP_AC_ADJ] [numeric](38, 8) NULL,
    [PP_SQFT] [numeric](38, 8) NULL,
    [PP_SF_ADJ] [numeric](38, 8) NULL,
    [C_AND_G] [nvarchar](1) NULL,
    [DEED_ACRES] [numeric](38, 8) NULL,
    [LAND_VAL] [int] NULL,
    [BLDG_VAL] [int] NULL,
    [OUT_VAL] [int] NULL,
    [PROP_VAL] [int] NULL,
    [LycoOnline] [nvarchar](255) NULL,
    [TYPE] [nvarchar](10) NULL,
    [MUNI_NAME] [nvarchar](25) NULL,
    [MUNI_TYPE] [nvarchar](25) NULL,
    [created_user] [nvarchar](255) NULL,
    [created_date] [datetime2](7) NULL,
    [last_edited_user] [nvarchar](255) NULL,
    [last_edited_date] [datetime2](7) NULL,
    [SALE_TYPE] [nvarchar](30) NULL,
    [VALID_SALE] [nvarchar](1) NULL,
    [Shape] [geometry] NULL,
    [Calc_Acres] [numeric](38, 8) NULL,
    [Notes] [nvarchar](150) NULL,
    [GlobalID] [uniqueidentifier] NOT NULL,
    [GDB_GEOMATTR_DATA] [varbinary](max) NULL,
    [GDB_ARCHIVE_OID] [int] IDENTITY(1,1) NOT NULL,
    [GDB_FROM_DATE] [datetime2](7) NOT NULL,
    [GDB_IS_DELETE] [smallint] NOT NULL,
    [GDB_BRANCH_ID] [int] NOT NULL,
    [GDB_DELETED_AT] [datetime2](7) NULL,
    [GDB_DELETED_BY] [nvarchar](255) NULL,
 CONSTRAINT [R831_pk] PRIMARY KEY CLUSTERED 
(
    [GDB_ARCHIVE_OID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF, FILLFACTOR = 95, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

ALTER TABLE [dbo].[PARCELS] SET (LOCK_ESCALATION = DISABLE)
GO

ALTER TABLE [dbo].[PARCELS] ADD  DEFAULT ('{00000000-0000-0000-0000-000000000000}') FOR [GlobalID]
GO

ALTER TABLE [dbo].[PARCELS] ADD  CONSTRAINT [GDB_FROM_DATE831_def]  DEFAULT (CONVERT([datetime2](3),getutcdate())) FOR [GDB_FROM_DATE]
GO

ALTER TABLE [dbo].[PARCELS] ADD  CONSTRAINT [GDB_IS_DELETE831_def]  DEFAULT ((0)) FOR [GDB_IS_DELETE]
GO

ALTER TABLE [dbo].[PARCELS] ADD  CONSTRAINT [GDB_BRANCH_ID831_def]  DEFAULT ((0)) FOR [GDB_BRANCH_ID]
GO

ALTER TABLE [dbo].[PARCELS]  WITH NOCHECK ADD  CONSTRAINT [g799_ck] CHECK  (([SHAPE].[STSrid]=(2271)))
GO

ALTER TABLE [dbo].[PARCELS] NOCHECK CONSTRAINT [g799_ck]
GO

CREATE UNIQUE NONCLUSTERED INDEX [gdb_ct1_831] ON [dbo].[PARCELS]
(
    [OBJECTID] ASC,
    [GDB_FROM_DATE] ASC,
    [GDB_BRANCH_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF, FILLFACTOR = 75, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

CREATE UNIQUE NONCLUSTERED INDEX [gdb_ct2_831] ON [dbo].[PARCELS]
(
    [GDB_BRANCH_ID] ASC,
    [GDB_FROM_DATE] ASC,
    [GDB_IS_DELETE] ASC,
    [OBJECTID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF, FILLFACTOR = 75, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

ALTER TABLE [dbo].[PARCELS] ADD  CONSTRAINT [R831_pk] PRIMARY KEY CLUSTERED 
(
    [GDB_ARCHIVE_OID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF, FILLFACTOR = 95, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [R831_SDE_ROWID_UK] ON [dbo].[PARCELS]
(
    [OBJECTID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF, FILLFACTOR = 75, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

CREATE SPATIAL INDEX [S799_idx] ON [dbo].[PARCELS]
(
    [Shape]
)USING  GEOMETRY_GRID 
WITH (BOUNDING_BOX =(2009646.876269, 328920.783953, 2326307.240322, 522100.77817), GRIDS =(LEVEL_1 = MEDIUM,LEVEL_2 = MEDIUM,LEVEL_3 = MEDIUM,LEVEL_4 = MEDIUM), 
CELLS_PER_OBJECT = 16, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF, FILLFACTOR = 95) ON [PRIMARY]
GO

CREATE UNIQUE NONCLUSTERED INDEX [UUID_831] ON [dbo].[PARCELS]
(
    [GlobalID] ASC,
    [GDB_BRANCH_ID] ASC,
    [GDB_FROM_DATE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

有什么想法吗?

sql-server performance geometry query-optimization spatial
1个回答
0
投票

您的查询可以得到很大的改进,您的索引也可以。

您的主要问题基本上是没有可以支持整个查询的索引。不必要的自连接使它变得更加复杂。

首先,可以重写查询以删除自连接,这是不必要的。您可以直接从

ROW_NUMBER
查询中提取所有列。

此外,我强烈建议您考虑Keyset Pagination。这意味着您无需读取 2000 行并丢弃它们,而是跳转到索引中所需的确切位置。

所以查询可以简化如下:

SELECT TOP (2000)
  br.OBJECTID,
  br.Shape
FROM (
    SELECT 
      p.*,
      ROW_NUMBER() OVER (PARTITION BY p.OBJECTID ORDER BY p.GDB_FROM_DATE DESC) rn
    FROM 
      dbo.PARCELS p
    WHERE p.GDB_BRANCH_ID BETWEEN 0 AND 1
      AND p.GDB_FROM_DATE <= '2023-12-22'
      AND p.OBJECTID > @previousMaxID    -- remove this line for the first page
) br
WHERE
  br.rn = 1
  AND br.GDB_IS_DELETE = 0
ORDER BY
  OBJECTID ASC;

注意前一个 Max ID 的使用,以便分页到下一个键。删除第一页最后的

WHERE
子句。


对于索引,键集分页通常需要具有支持索引的确定性排序。

ROW_NUMBER
也可以,因此使用相同的是有意义的。考虑到行编号,我们不需要担心额外的唯一符列,因为保证每个
OBJECTID
只有一行。

我建议使用以下索引,它将支持所有

WHERE
子句,以及
ROW_NUMBER
和键集分页的排序要求:

CREATE NONCLUSTERED INDEX [R831_SDE_ROWID_UK] ON dbo.PARCELS
( OBJECTID, GDB_FROM_DATE DESC)
INCLUDE ( GDB_BRANCH_ID, GDB_IS_DELETE, Shape );

如果

p.GDB_BRANCH_ID BETWEEN 0 AND 1
大大减少了行数,那么使用不同的索引可能是有意义的,可能与上面类似,但在
GDB_BRANCH_ID
上使用过滤器。


如您所见,查询计划非常整洁。

db<>小提琴

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.