我有一个包含很多表的数据库。给定当前记录的键,我需要一个查询来按键顺序读取下一条记录。 [注意:我正在模拟一个基于旧 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
以下查询为我生成了一个非常高效的执行计划。
如果您已经在使用此模式,请检查您的参数数据类型是否与列数据类型完全匹配
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