在 T-SQL 中强制索引查找而不是扫描来获取下一条记录

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

我有一个包含很多表的数据库。给定当前记录的键,我需要一个查询来按键顺序读取下一条记录。 [注意:我正在模拟一个基于旧 C-ISAM 的系统,该系统一次读取一个记录,因此我无法使用正常的基于集合的操作]。例如,给定一个具有单个键的表,我的查询可能如下所示:

SELECT TOP 1 COL1, COL2, COL3, etc.
FROM TAB1
WHERE COL1 > @P
0
ORDER BY COL1

有两个关键列,它会变得有点棘手:

SELECT TOP 1 COL1, COL2, COL3, etc.
FROM TAB1
WHERE COL1 = @P0 AND COL2 > @P1
   OR COL1 > @P0
ORDER BY COL1, COL2

添加其他键列遵循相同的模式。

现在,如果你想一想,SQL Server 应该能够使用 INDEX SEEK 运算符来查找所需的记录,事实上,对于我的大多数表来说,它都是这样做的。然而,我发现一张表,无论我尝试什么,SQL Server 都坚持执行索引扫描,这在大表(这张表就是)中当然要慢得多。

我尝试了很多东西,包括使用

WITH (FORCESEEK)
OPTIMIZE FOR
和其他几个。在所有情况下,我最终都会进行索引扫描。

但是,我确实找到了一种查询模式,该模式导致两次查找和串联:

SELECT TOP 1 COL1, COL2, COL3, etc.
FROM ( 
   SELECT TOP 1 COL1, COL2, COL3, etc.
   FROM TAB1
   WHERE COL1 = @P0 AND COL2 > @P1

   UNION ALL

   SELECT TOP 1 COL1, COL3, COL3, etc.
   FROM TAB
   WHERE COL1 > @P0
) AS Q
ORDER BY COL1, COL2

这是一项改进,但仍然会生成低效的查询计划。关键领域越多,计划就越糟糕。我不想使用这种技术,因为我的大多数表都可以很好地处理更简单的查询,从而导致计划中只有一个 INDEX SEEK。

请注意,查询代码是为每个表预先生成的,而不是根据需要手动编码,因此很难对不同的表使用不同的方案。数据库中有超过 1,000 个表,因此确定哪些表有问题、哪些表没有问题是不切实际的。

此方案将被大量使用(大约 100 多个用户),包括大量迭代整个表[再次:我不能使用集合操作],因此效率很重要。例如,我有一个进程,由于索引扫描,大约需要一个小时才能浏览完 100,000 条记录的表(平均每秒仅 27 条记录)。它开始时相当快,但随着它在表中的工作,变得越来越慢。扫描另一个没有出现问题的表,结果每秒大约 2,500 条记录,这是可以接受的。

我正在寻找一种方法来“强制”SQL Server 使用 INDEX SEEK 而不是 INDEX SCAN。

以下是有效表的架构:

CREATE TABLE [dbo].[CUSHST](
    [ID_] [bigint] IDENTITY(1,1) NOT NULL,
    [CUSTOMER_NUMBER] [nvarchar](5) NOT NULL,
    [ORDER_NUMBER] [decimal](6, 0) NOT NULL,
    [ORDER_DATE] [date] NOT NULL,
    [ORDER_TYPE] [nvarchar](1) NOT NULL,
    [SLS_CODE] [nvarchar](3) NOT NULL,
    [INVOICE_NUMBER] [decimal](6, 0) NOT NULL,
    [INVOICE_DATE] [date] NOT NULL,
    [GOLD_PRICE] [decimal](8, 2) NOT NULL,
    [SILVER_PRICE] [decimal](8, 2) NOT NULL,
    [ITEM_NUMBER] [nvarchar](10) NOT NULL,
    [PRODUCT_CATEGORY] [nvarchar](2) NOT NULL,
    [DESCRIPTION] [nvarchar](30) NOT NULL,
    [EXT_DESCRIPTION] [nvarchar](30) NOT NULL,
    [UNIT_OF_MEASURE] [nvarchar](3) NOT NULL,
    [QTY_ORDERED] [decimal](8, 2) NOT NULL,
    [QTY_SHIPPED] [decimal](8, 2) NOT NULL,
    [PRICE_PER_UNIT] [decimal](8, 2) NOT NULL,
    [DISCOUNT_PER_UNIT] [decimal](8, 2) NOT NULL,
    [LABOR_PER_UNIT] [decimal](8, 2) NOT NULL,
    [COST_PER_UNIT] [decimal](8, 2) NOT NULL,
    [SALE_AMOUNT] [decimal](9, 2) NOT NULL,
    [COST_AMOUNT] [decimal](9, 2) NOT NULL,
    [PO_NUMBER] [nvarchar](20) NOT NULL,
    [REVERSE_DATE] [decimal](7, 0) NOT NULL,
    [COMMENTS_1] [nvarchar](35) NOT NULL,
    [COMMENTS_2] [nvarchar](35) NOT NULL,
    [SHIPTO_NAME] [nvarchar](35) NOT NULL,
    [SHIPTO_ADDRESS_1] [nvarchar](35) NOT NULL,
    [SHIPTO_ADDRESS_2] [nvarchar](35) NOT NULL,
    [SHIPTO_CITY] [nvarchar](25) NOT NULL,
    [SHIPTO_STATE] [nvarchar](2) NOT NULL,
    [SHIPTO_ZIP] [nvarchar](10) NOT NULL,
    [TERM_CODE] [nvarchar](3) NOT NULL,
    [LENGTH_CODE] [nvarchar](10) NOT NULL,
    [SIZE_CODE] [nvarchar](12) NOT NULL,
    [TYPE_CODE] [nvarchar](2) NOT NULL,
    [KARAT] [nvarchar](4) NOT NULL,
    [SHIP_VIA_CODE] [nvarchar](3) NOT NULL,
    [SHIP_DATE] [date] NOT NULL,
    [PIECES_ORDERED] [decimal](7, 0) NOT NULL,
    [PIECES_SHIPPED] [decimal](7, 0) NOT NULL,
    [SURCHARGE] [decimal](8, 2) NOT NULL,
    [TIE_BREAKER] [int] NOT NULL,
    [PD_PRICE] [decimal](8, 2) NOT NULL,
    [PT_PRICE] [decimal](8, 2) NOT NULL,
    [WIDTH_CODE] [nvarchar](10) NOT NULL,
    [BACKORDER_FLAG] [nvarchar](1) NOT NULL,
    [PROD_BATCH1] [decimal](6, 0) NOT NULL,
    [PROD_BATCH2] [decimal](6, 0) NOT NULL,
    [PROD_BATCH3] [decimal](6, 0) NOT NULL,
    [PROD_BATCH4] [decimal](6, 0) NOT NULL,
    [PROD_BATCH5] [decimal](6, 0) NOT NULL,
    [PROD_BATCH6] [decimal](6, 0) NOT NULL,
    [PROD_BATCH_STRING] [nvarchar](15) NOT NULL,
    [LINE_NUMBER] [decimal](4, 0) NOT NULL,
    [BASE_SURCHARGE] [decimal](8, 2) NOT NULL,
    [SHIPTO_COUNTRY] [nvarchar](2) NOT NULL,
    [APPROVED_BY] [nvarchar](15) NOT NULL,
    [ENTERED_BY] [nvarchar](15) NOT NULL,
    [TSR_ACCOUNT_ID] [nvarchar](4) NOT NULL,
    [CC_CHARGE] [decimal](6, 2) NOT NULL,
    [QTY_ORDERED3] [decimal](10, 3) NOT NULL,
    [QTY_SHIPPED3] [decimal](10, 3) NOT NULL,
    [USE_LINE_PRICING] [nvarchar](1) NOT NULL,
    [GOLD_PRICE_LP] [decimal](8, 2) NOT NULL,
    [SILVER_PRICE_LP] [decimal](8, 2) NOT NULL,
    [PT_PRICE_LP] [decimal](8, 2) NOT NULL,
    [PD_PRICE_LP] [decimal](8, 2) NOT NULL,
    [COMMENTS_3] [nvarchar](35) NOT NULL,
    [COMMENTS_4] [nvarchar](35) NOT NULL,
    [INCO_TERM] [nvarchar](3) NOT NULL,
    [CURRENCY_FACTOR] [decimal](8, 7) NOT NULL,
    [PF_SIZE_CODE] [nvarchar](25) NOT NULL,
    [PF_BANDWIDTH] [nvarchar](20) NOT NULL,
    [LOGO] [nvarchar](1) NOT NULL,
    [SHIPTO_ATTENTION] [nvarchar](35) NOT NULL,
    [PROFORMA_AIR_DEST] [nvarchar](35) NOT NULL,
    [TIME_PREPPED] [int] NOT NULL,
    [OWN_LABEL] [nvarchar](1) NOT NULL,
    [FROM_MEMO] [nvarchar](1) NOT NULL,
    [FROM_MEMO_CUSTOMER_NUMBER] [nvarchar](5) NOT NULL,
    [PURCHASE_TO_POOL] [nvarchar](1) NOT NULL,
    [ROWVERSION] [timestamp] NOT NULL,
 CONSTRAINT [PK_CUSHST] PRIMARY KEY CLUSTERED 
(
    [CUSTOMER_NUMBER] ASC,
    [INVOICE_DATE] ASC,
    [INVOICE_NUMBER] ASC,
    [ITEM_NUMBER] ASC,
    [TIE_BREAKER] ASC,
    [ID_] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

这是失败的表:

CREATE TABLE [dbo].[GLJNL](
    [ID_] [bigint] IDENTITY(1,1) NOT NULL,
    [ACCOUNT_NUMBER] [decimal](7, 0) NOT NULL,
    [TRX_DATE] [date] NOT NULL,
    [DEBIT_AMOUNT] [decimal](11, 2) NOT NULL,
    [CREDIT_AMOUNT] [decimal](11, 2) NOT NULL,
    [SOURCE] [nvarchar](6) NOT NULL,
    [REFERENCE] [nvarchar](35) NOT NULL,
    [REF_1] [nvarchar](35) NOT NULL,
    [REF_2] [nvarchar](35) NOT NULL,
    [ROWVERSION] [timestamp] NOT NULL,
 CONSTRAINT [PK_GLJNL] PRIMARY KEY CLUSTERED 
(
    [ACCOUNT_NUMBER] ASC,
    [TRX_DATE] ASC,
    [ID_] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
sql-server t-sql select sql-execution-plan
1个回答
0
投票

以下查询为我生成了一个非常高效的执行计划。

如果您已经在使用此模式,请检查您的参数数据类型是否与列数据类型完全匹配

DECLARE @ACCOUNT_NUMBER decimal(7,0), @TRX_DATE DATE, @ID_ BIGINT

SELECT TOP 1 * 
FROM  [dbo].[GLJNL]
where (ACCOUNT_NUMBER = @ACCOUNT_NUMBER and TRX_DATE = @TRX_DATE and ID_ >= @ID_)
 or (ACCOUNT_NUMBER = @ACCOUNT_NUMBER and TRX_DATE > @TRX_DATE)
 or (ACCOUNT_NUMBER > @ACCOUNT_NUMBER)
ORDER BY 
    [ACCOUNT_NUMBER] ASC,
    [TRX_DATE] ASC,
    [ID_] ASC
© www.soinside.com 2019 - 2024. All rights reserved.