Mam stosunkowo dużą tabelę (obecnie 2 miliony rekordów) i chciałbym wiedzieć, czy można poprawić wydajność zapytań ad-hoc. Kluczowe jest tutaj słowo ad-hoc . Dodawanie indeksów nie jest opcją (istnieją już indeksy w kolumnach, które są najczęściej zadawane).
Uruchomienie prostego zapytania w celu zwrócenia 100 ostatnio zaktualizowanych rekordów:
select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc
Trwa kilka minut. Zobacz plan wykonania poniżej:
Dodatkowe szczegóły ze skanu tabeli:
SQL Server Execution Times:
CPU time = 3945 ms, elapsed time = 148524 ms.
Serwer jest dość wydajny (z pamięci 48 GB RAM, 24-rdzeniowy procesor) z systemem SQL Server 2008 R2 x64.
Aktualizacja
Znalazłem ten kod, aby utworzyć tabelę z 1 000 000 rekordów. Pomyślałem, że mógłbym wtedy uruchomić SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESC
na kilku różnych serwerach, aby sprawdzić, czy moje prędkości dostępu do dysku na serwerze są słabe.
WITH t1(N) AS (SELECT 1 UNION ALL SELECT 1),
t2(N) AS (SELECT 1 FROM t1 x, t1 y),
t3(N) AS (SELECT 1 FROM t2 x, t2 y),
Tally(N) AS (SELECT TOP 98 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM t3 x, t3 y),
Tally2(N) AS (SELECT TOP 5 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM t3 x, t3 y),
Combinations(N) AS (SELECT DISTINCT LTRIM(RTRIM(RTRIM(SUBSTRING(poss,a.N,2)) + SUBSTRING(vowels,b.N,1)))
FROM Tally a
CROSS JOIN Tally2 b
CROSS APPLY (SELECT 'B C D F G H J K L M N P R S T V W Z SCSKKNSNSPSTBLCLFLGLPLSLBRCRDRFRGRPRTRVRSHSMGHCHPHRHWHBWCWSWTW') d(poss)
CROSS APPLY (SELECT 'AEIOU') e(vowels))
SELECT IDENTITY(INT,1,1) AS ID, a.N + b.N AS N
INTO #testNames
FROM Combinations a
CROSS JOIN Combinations b;
SELECT IDENTITY(INT,1,1) AS ID, firstName, secondName
INTO #testNames2
FROM (SELECT firstName, secondName
FROM (SELECT TOP 1000 --1000 * 1000 = 1,000,000 rows
N AS firstName
FROM #testNames
ORDER BY NEWID()) a
CROSS JOIN (SELECT TOP 1000 --1000 * 1000 = 1,000,000 rows
N AS secondName
FROM #testNames
ORDER BY NEWID()) b) innerQ;
SELECT firstName, secondName,
firstName + '.' + secondName + '@fake.com' AS eMail,
CAST((ABS(CHECKSUM(NEWID())) % 250) + 1 AS VARCHAR(3)) + ' ' AS mailAddress,
(ABS(CHECKSUM(NEWID())) % 152100) + 1 AS jID,
IDENTITY(INT,1,1) AS ID
INTO #testNames3
FROM #testNames2
SELECT IDENTITY(INT,1,1) AS ID, firstName, secondName, eMail,
mailAddress + b.N + b.N AS mailAddress
INTO testEnvironment
FROM #testNames3 a
INNER JOIN #testNames b ON a.jID = b.ID;
--CLEAN UP USELESS TABLES
DROP TABLE #testNames;
DROP TABLE #testNames2;
DROP TABLE #testNames3;
Ale na trzech serwerach testowych zapytanie przebiegło niemal natychmiast. Czy ktoś może to wyjaśnić?
Zaktualizuj 2
Dziękuję za komentarze - proszę, nie przestawajcie ich przekazywać ... skłoniły mnie do zmiany indeksu klucza podstawowego z nieklastrowego na klastrowy z dość interesującymi (i nieoczekiwanymi?) Wynikami.
Nieklastrowy:
SQL Server Execution Times:
CPU time = 3634 ms, elapsed time = 154179 ms.
Klastrowany:
SQL Server Execution Times:
CPU time = 2650 ms, elapsed time = 52177 ms.
Jak to jest możliwe? Bez indeksu w kolumnie er101_upd_date_iso, jak można użyć skanowania indeksu klastrowego?
Zaktualizuj 3
Zgodnie z żądaniem - to jest skrypt tworzenia tabeli:
CREATE TABLE [dbo].[ER101_ACCT_ORDER_DTL](
[ER101_ORG_CODE] [varchar](2) NOT NULL,
[ER101_ORD_NBR] [int] NOT NULL,
[ER101_ORD_LINE] [int] NOT NULL,
[ER101_EVT_ID] [int] NULL,
[ER101_FUNC_ID] [int] NULL,
[ER101_STATUS_CDE] [varchar](2) NULL,
[ER101_SETUP_ID] [varchar](8) NULL,
[ER101_DEPT] [varchar](6) NULL,
[ER101_ORD_TYPE] [varchar](2) NULL,
[ER101_STATUS] [char](1) NULL,
[ER101_PRT_STS] [char](1) NULL,
[ER101_STS_AT_PRT] [char](1) NULL,
[ER101_CHG_COMMENT] [varchar](255) NULL,
[ER101_ENT_DATE_ISO] [datetime] NULL,
[ER101_ENT_USER_ID] [varchar](10) NULL,
[ER101_UPD_DATE_ISO] [datetime] NULL,
[ER101_UPD_USER_ID] [varchar](10) NULL,
[ER101_LIN_NBR] [int] NULL,
[ER101_PHASE] [char](1) NULL,
[ER101_RES_CLASS] [char](1) NULL,
[ER101_NEW_RES_TYPE] [varchar](6) NULL,
[ER101_RES_CODE] [varchar](12) NULL,
[ER101_RES_QTY] [numeric](11, 2) NULL,
[ER101_UNIT_CHRG] [numeric](13, 4) NULL,
[ER101_UNIT_COST] [numeric](13, 4) NULL,
[ER101_EXT_COST] [numeric](11, 2) NULL,
[ER101_EXT_CHRG] [numeric](11, 2) NULL,
[ER101_UOM] [varchar](3) NULL,
[ER101_MIN_CHRG] [numeric](11, 2) NULL,
[ER101_PER_UOM] [varchar](3) NULL,
[ER101_MAX_CHRG] [numeric](11, 2) NULL,
[ER101_BILLABLE] [char](1) NULL,
[ER101_OVERRIDE_FLAG] [char](1) NULL,
[ER101_RES_TEXT_YN] [char](1) NULL,
[ER101_DB_CR_FLAG] [char](1) NULL,
[ER101_INTERNAL] [char](1) NULL,
[ER101_REF_FIELD] [varchar](255) NULL,
[ER101_SERIAL_NBR] [varchar](50) NULL,
[ER101_RES_PER_UNITS] [int] NULL,
[ER101_SETUP_BILLABLE] [char](1) NULL,
[ER101_START_DATE_ISO] [datetime] NULL,
[ER101_END_DATE_ISO] [datetime] NULL,
[ER101_START_TIME_ISO] [datetime] NULL,
[ER101_END_TIME_ISO] [datetime] NULL,
[ER101_COMPL_STS] [char](1) NULL,
[ER101_CANCEL_DATE_ISO] [datetime] NULL,
[ER101_BLOCK_CODE] [varchar](6) NULL,
[ER101_PROP_CODE] [varchar](8) NULL,
[ER101_RM_TYPE] [varchar](12) NULL,
[ER101_WO_COMPL_DATE] [datetime] NULL,
[ER101_WO_BATCH_ID] [varchar](10) NULL,
[ER101_WO_SCHED_DATE_ISO] [datetime] NULL,
[ER101_GL_REF_TRANS] [char](1) NULL,
[ER101_GL_COS_TRANS] [char](1) NULL,
[ER101_INVOICE_NBR] [int] NULL,
[ER101_RES_CLOSED] [char](1) NULL,
[ER101_LEAD_DAYS] [int] NULL,
[ER101_LEAD_HHMM] [int] NULL,
[ER101_STRIKE_DAYS] [int] NULL,
[ER101_STRIKE_HHMM] [int] NULL,
[ER101_LEAD_FLAG] [char](1) NULL,
[ER101_STRIKE_FLAG] [char](1) NULL,
[ER101_RANGE_FLAG] [char](1) NULL,
[ER101_REQ_LEAD_STDATE] [datetime] NULL,
[ER101_REQ_LEAD_ENDATE] [datetime] NULL,
[ER101_REQ_STRK_STDATE] [datetime] NULL,
[ER101_REQ_STRK_ENDATE] [datetime] NULL,
[ER101_LEAD_STDATE] [datetime] NULL,
[ER101_LEAD_ENDATE] [datetime] NULL,
[ER101_STRK_STDATE] [datetime] NULL,
[ER101_STRK_ENDATE] [datetime] NULL,
[ER101_DEL_MARK] [char](1) NULL,
[ER101_USER_FLD1_02X] [varchar](2) NULL,
[ER101_USER_FLD1_04X] [varchar](4) NULL,
[ER101_USER_FLD1_06X] [varchar](6) NULL,
[ER101_USER_NBR_060P] [int] NULL,
[ER101_USER_NBR_092P] [numeric](9, 2) NULL,
[ER101_PR_LIST_DTL] [numeric](11, 2) NULL,
[ER101_EXT_ACCT_CODE] [varchar](8) NULL,
[ER101_AO_STS_1] [char](1) NULL,
[ER101_PLAN_PHASE] [char](1) NULL,
[ER101_PLAN_SEQ] [int] NULL,
[ER101_ACT_PHASE] [char](1) NULL,
[ER101_ACT_SEQ] [int] NULL,
[ER101_REV_PHASE] [char](1) NULL,
[ER101_REV_SEQ] [int] NULL,
[ER101_FORE_PHASE] [char](1) NULL,
[ER101_FORE_SEQ] [int] NULL,
[ER101_EXTRA1_PHASE] [char](1) NULL,
[ER101_EXTRA1_SEQ] [int] NULL,
[ER101_EXTRA2_PHASE] [char](1) NULL,
[ER101_EXTRA2_SEQ] [int] NULL,
[ER101_SETUP_MSTR_SEQ] [int] NULL,
[ER101_SETUP_ALTERED] [char](1) NULL,
[ER101_RES_LOCKED] [char](1) NULL,
[ER101_PRICE_LIST] [varchar](10) NULL,
[ER101_SO_SEARCH] [varchar](9) NULL,
[ER101_SSB_NBR] [int] NULL,
[ER101_MIN_QTY] [numeric](11, 2) NULL,
[ER101_MAX_QTY] [numeric](11, 2) NULL,
[ER101_START_SIGN] [char](1) NULL,
[ER101_END_SIGN] [char](1) NULL,
[ER101_START_DAYS] [int] NULL,
[ER101_END_DAYS] [int] NULL,
[ER101_TEMPLATE] [char](1) NULL,
[ER101_TIME_OFFSET] [char](1) NULL,
[ER101_ASSIGN_CODE] [varchar](10) NULL,
[ER101_FC_UNIT_CHRG] [numeric](13, 4) NULL,
[ER101_FC_EXT_CHRG] [numeric](11, 2) NULL,
[ER101_CURRENCY] [varchar](3) NULL,
[ER101_FC_RATE] [numeric](12, 5) NULL,
[ER101_FC_DATE] [datetime] NULL,
[ER101_FC_MIN_CHRG] [numeric](11, 2) NULL,
[ER101_FC_MAX_CHRG] [numeric](11, 2) NULL,
[ER101_FC_FOREIGN] [numeric](12, 5) NULL,
[ER101_STAT_ORD_NBR] [int] NULL,
[ER101_STAT_ORD_LINE] [int] NULL,
[ER101_DESC] [varchar](255) NULL
) ON [PRIMARY]
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PRT_SEQ_1] [varchar](12) NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PRT_SEQ_2] [varchar](120) NULL
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAX_BASIS] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_RES_CATEGORY] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DECIMALS] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAX_SEQ] [varchar](7) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MANUAL] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_LC_RATE] [numeric](12, 5) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_FC_RATE] [numeric](12, 5) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_PL_RATE] [numeric](12, 5) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_DIFF] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_UNIT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_EXT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_MIN_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_MAX_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_UNIT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_EXT_CHRG] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_MIN_CHRG] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_MAX_CHRG] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAX_RATE_TYPE] [char](1) NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ORDER_FORM] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_FACTOR] [int] NULL
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MGMT_RPT_CODE] [varchar](6) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ROUND_CHRG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_WHOLE_QTY] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SET_QTY] [numeric](15, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SET_UNITS] [numeric](15, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SET_ROUNDING] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SET_SUB] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TIME_QTY] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_GL_DISTR_PCT] [numeric](7, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REG_SEQ] [int] NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC] [varchar](255) NULL
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REG_ACCT] [varchar](8) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DAILY] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_AVG_UNIT_CHRG] [varchar](1) NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC2] [varchar](255) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_CONTRACT_SEQ] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ORIG_RATE] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DISC_PCT] [decimal](17, 10) NULL
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DTL_EXIST] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ORDERED_ONLY] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_STDATE] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_STTIME] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_ENDATE] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_ENTIME] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_RATE] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_UNITS] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_BASE_RATE] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_COMMIT_QTY] [numeric](11, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MM_QTY_USED] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MM_CHRG_USED] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_TEXT_1] [varchar](50) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_1] [numeric](13, 3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_2] [numeric](13, 3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_3] [numeric](13, 3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_BASE_RATE] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REV_DIST] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_COVER] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_RATE_TYPE] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_USE_SEASONAL] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAX_EI] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAXES] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_FC_TAXES] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_TAXES] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_FC_QTY] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_LEAD_HRS] [numeric](6, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_STRIKE_HRS] [numeric](6, 2) NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_CANCEL_USER_ID] [varchar](10) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ST_OFFSET_HRS] [numeric](7, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_EN_OFFSET_HRS] [numeric](7, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_FLAG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_EXT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_EXT_CHRG_PL] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_EXT_CHRG_TR] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_EXT_CHRG_FC] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TIME_QTY_EDIT] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SURCHARGE_PCT] [decimal](17, 10) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_INCL_EXT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_INCL_EXT_CHRG_FC] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_CARRIER] [varchar](6) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_ID2] [varchar](8) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHIPPABLE] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_CHARGEABLE] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_ALLOW] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_START] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_END] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_SUPPLIER] [varchar](8) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TRACK_ID] [varchar](40) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REF_INV_NBR] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_NEW_ITEM_STS] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MSTR_REG_ACCT_CODE] [varchar](8) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC3] [varchar](255) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC4] [varchar](255) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC5] [varchar](255) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_ROLLUP] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MM_COST_USED] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_AUTO_SHIP_RCD] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_FIXED] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_EST_TBD] [varchar](3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ROLLUP_PL_UNIT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ROLLUP_PL_EXT_CHRG] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_GL_ORD_REV_TRANS] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DISCOUNT_FLAG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_RES_TYPE] [varchar](6) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_RES_CODE] [varchar](12) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PERS_SCHED_FLAG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PRINT_STAMP] [datetime] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_EXT_CHRG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PRINT_SEQ_NBR] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PAY_LOCATION] [varchar](3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MAX_RM_NIGHTS] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_USE_TIER_COST] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_UNITS_SCHEME_CODE] [varchar](6) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ROUND_TIME] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_LEVEL] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_PARENT_ORD_LINE] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_BADGE_PRT_STS] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_EVT_PROMO_SEQ] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REG_TYPE] [varchar](12) NULL
/****** Object: Index [PK__ER101_ACCT_ORDER] Script Date: 04/15/2012 20:24:37 ******/
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD CONSTRAINT [PK__ER101_ACCT_ORDER] PRIMARY KEY CLUSTERED
(
[ER101_ORD_NBR] ASC,
[ER101_ORD_LINE] ASC,
[ER101_ORG_CODE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 50) ON [PRIMARY]
Tabela ma rozmiar 2,8 GB, a rozmiar indeksu wynosi 3,9 GB.
źródło
Table Scan
wskazuje stertę (brak indeksu klastrowego) - więc pierwszym krokiem byłoby dodanie dobrego, szybkiego indeksu klastrowego do tabeli. Drugim krokiem może być zbadanie, czy indeks nieklastrowy naer101_upd_date_iso
pomógłby (i nie spowodowałby innych wad wydajności)er101_upd_date_iso
kolumny, prawdopodobnie możesz również pozbyć się operacji „Sortuj” w swoim planie wykonania i jeszcze bardziejOdpowiedzi:
Prosta odpowiedź: NIE. Nie można pomóc w zapytaniach ad hoc dotyczących tabeli 238 kolumnowej z 50% współczynnikiem wypełnienia w indeksie klastrowym.
Szczegółowa odpowiedź:
Jak już wspomniałem w innych odpowiedziach na ten temat, projektowanie indeksu to zarówno sztuka, jak i nauka i należy wziąć pod uwagę tak wiele czynników, że istnieje niewiele, jeśli w ogóle, sztywnych i szybkich reguł. Musisz wziąć pod uwagę: wielkość operacji DML vs SELECT, podsystem dyskowy, inne indeksy / wyzwalacze w tabeli, dystrybucja danych w tabeli, to zapytania korzystające z warunków SARGable WHERE i kilka innych rzeczy, których nawet nie pamiętam dobrze teraz.
Mogę powiedzieć, że nie można udzielić pomocy w przypadku pytań na ten temat bez zrozumienia samej tabeli, jej indeksów, wyzwalaczy itp. Teraz, gdy opublikowałeś definicję tabeli (wciąż czekam na indeksy, ale sama definicja tabeli wskazuje na 99% problemu) Mogę podać kilka sugestii.
Po pierwsze, jeśli definicja tabeli jest dokładna (238 kolumn, współczynnik wypełnienia 50%), możesz zignorować pozostałe odpowiedzi / porady tutaj ;-). Przepraszam, że jestem tu mniej niż polityczny, ale poważnie, to dziki pogoń bez znajomości szczegółów. A teraz, gdy widzimy definicję tabeli, staje się nieco bardziej jasne, dlaczego proste zapytanie miałoby trwać tak długo, nawet jeśli zapytania testowe (aktualizacja nr 1) były wykonywane tak szybko.
Głównym problemem tutaj (iw wielu sytuacjach o niskiej wydajności) jest złe modelowanie danych. 238 kolumn nie jest zabronione, tak jak posiadanie indeksów 999 nie jest zabronione, ale generalnie nie jest też zbyt mądre.
Zalecenia:
ANSI_PADDING OFF
jest niepokojące, nie wspominając o niespójności w tabeli z powodu różnych dodawanych kolumn w czasie. Nie jestem pewien, czy możesz to teraz naprawić, ale idealnie byłoby, gdybyś miał zawszeANSI_PADDING ON
lub przynajmniej miał takie samo ustawienie we wszystkichALTER TABLE
instrukcjach.PRIMARY
ponieważ jest to miejsce, w którym SQL SERVER przechowuje wszystkie swoje dane i metadane dotyczące twoich obiektów. Tworzysz tabelę i indeks klastrowany (ponieważ są to dane dla tabeli)[Tables]
i wszystkie indeksy nieklastrowane na[Indexes]
WHERE
, rozważ przeniesienie go do pierwszej kolumny indeksu klastrowego. Zakładając, że jest używany częściej niż „ER101_ORD_NBR”. Jeśli częściej używany jest „ER101_ORD_NBR”, zachowaj go. Po prostu wydaje się, zakładając, że nazwy pól oznaczają „OrganizationCode” i „OrderNumber”, że „OrgCode” jest lepszą grupą, która może zawierać wiele „OrderNumbers”.CHAR(2)
zamiast,VARCHAR(2)
ponieważ spowoduje to zapisanie bajtu w nagłówku wiersza, który śledzi różne rozmiary i dodaje do milionów wierszy.SELECT *
zaszkodzi wydajności. Nie tylko dlatego, że wymaga od SQL Server zwrócenia wszystkich kolumn, a tym samym bardziej prawdopodobne jest wykonanie skanowania indeksu klastrowego niezależnie od innych indeksów, ale także przejście do definicji tabeli i przetłumaczenie*
na wszystkie nazwy kolumn zajmuje SQL Serverowi trochę czasu. . Powinno być nieco szybsze określenie wszystkich 238 nazw kolumn naSELECT
liście, ale to nie pomoże w problemie ze skanowaniem. Ale czy kiedykolwiek naprawdę potrzebujesz wszystkich 238 kolumn w tym samym czasie?Powodzenia!
AKTUALIZACJA
W celu uzupełnienia pytania „jak poprawić wydajność dużej tabeli dla zapytań ad-hoc”, należy zauważyć, że chociaż nie pomoże to w tym konkretnym przypadku, JEŚLI ktoś używa SQL Server 2012 (lub nowszego kiedy nadejdzie ten czas) i JEŻELI tabela nie jest aktualizowana, można użyć indeksów magazynu kolumn. Aby uzyskać więcej informacji na temat tej nowej funkcji, spójrz tutaj: http://msdn.microsoft.com/en-us/library/gg492088.aspx (uważam, że można je było aktualizować począwszy od SQL Server 2014).
UPDATE 2
Dodatkowe uwagi:
INT
,BIGINT
,TINYINT
,SMALLINT
,CHAR
,NCHAR
,BINARY
,DATETIME
,SMALLDATETIME
,MONEY
, etc), a także ponad 50 % wierszy jestNULL
, a następnie rozważ włączenieSPARSE
opcji, która stała się dostępna w SQL Server 2008. Szczegółowe informacje można znaleźć na stronie MSDN w sekcji Użyj rzadkich kolumn .źródło
*
bez tej wątpliwej jednegoJest kilka problemów z tym zapytaniem (i dotyczy to każdego zapytania).
Brak indeksu
Brak indeksu w
er101_upd_date_iso
kolumnie jest najważniejszy, o czym wspomniał już Oded .Bez pasującego indeksu (którego brak mógłby spowodować skanowanie tabeli) nie ma szans na wykonanie szybkich zapytań na dużych tabelach.
Jeśli nie możesz dodać indeksów (z różnych powodów, w tym nie ma sensu tworzyć indeksu tylko dla jednego zapytania ad-hoc ), proponuję kilka obejść (które można zastosować do zapytań ad-hoc):
1. Użyj tabel tymczasowych
Utwórz tabelę tymczasową na podzbiorze (wierszach i kolumnach) danych, które Cię interesują. Tabela tymczasowa powinna być znacznie mniejsza od oryginalnej tabeli źródłowej, może być łatwo indeksowana (w razie potrzeby) i może buforować podzbiór danych, które Cię interesują.
Aby utworzyć tabelę tymczasową, możesz użyć kodu (nie testowano) takiego jak:
-- copy records from last month to temporary table INSERT INTO #my_temporary_table SELECT * FROM er101_acct_order_dtl WITH (NOLOCK) WHERE er101_upd_date_iso > DATEADD(month, -1, GETDATE()) -- you can add any index you need on temp table CREATE INDEX idx_er101_upd_date_iso ON #my_temporary_table(er101_upd_date_iso) -- run other queries on temporary table (which can be indexed) SELECT TOP 100 * FROM #my_temporary_table ORDER BY er101_upd_date_iso DESC
Plusy:
view
.Cons:
2. Wspólne wyrażenie tabeli - CTE
Osobiście często używam CTE przy zapytaniach ad-hoc - jest to bardzo pomocne przy budowaniu (i testowaniu) zapytania kawałek po kawałku.
Zobacz przykład poniżej (zapytanie zaczynające się od
WITH
).Plusy:
Cons:
3. Utwórz widoki
Podobnie jak powyżej, ale twórz widoki zamiast tabel tymczasowych (jeśli często grasz tymi samymi zapytaniami i masz wersję MS SQL obsługującą widoki indeksowane).
Możesz tworzyć widoki lub indeksowane widoki podzbioru danych, które Cię interesują, i uruchamiać zapytania na widoku - które powinny zawierać tylko interesujący podzbiór danych znacznie mniejszy niż cała tabela.
Plusy:
Cons:
Wybór wszystkich kolumn
Uruchamianie zapytania gwiazdkowego (
SELECT * FROM
) na dużym stole nie jest dobre ...Jeśli masz duże kolumny (na przykład długie łańcuchy), odczytanie ich z dysku i przekazanie ich przez sieć zajmuje dużo czasu.
Spróbowałbym zastąpić
*
nazwami kolumn, których naprawdę potrzebujesz.Lub, jeśli potrzebujesz wszystkich kolumn, spróbuj przepisać zapytanie na coś takiego (używając wspólnego wyrażenia danych ):
;WITH recs AS ( SELECT TOP 100 id as rec_id -- select primary key only FROM er101_acct_order_dtl ORDER BY er101_upd_date_iso DESC ) SELECT er101_acct_order_dtl.* FROM recs JOIN er101_acct_order_dtl ON er101_acct_order_dtl.id = recs.rec_id ORDER BY er101_upd_date_iso DESC
Brudne czyta
Ostatnią rzeczą, która może przyspieszyć zapytanie ad-hoc, jest zezwolenie na brudne odczyty ze wskazówką dotyczącą tabeli
WITH (NOLOCK)
.Zamiast podpowiedzi możesz ustawić poziom izolacji transakcji na odczyt niezatwierdzonych:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
lub ustaw odpowiednie ustawienie SQL Management Studio.
Zakładam, że dla zapytań ad-hoc brudne odczyty są wystarczająco dobre.
źródło
SELECT *
- wymusza na SQL Server użycie indeksu klastrowego. A przynajmniej powinno. Nie widzę żadnego prawdziwego powodu dla nieklastrowego wskaźnika pokrycia ... obejmującego całą tabelę :)Otrzymujesz tam skanowanie tabeli , co oznacza, że nie masz zdefiniowanego indeksu
er101_upd_date_iso
lub jeśli ta kolumna jest częścią istniejącego indeksu, indeks nie może być użyty (prawdopodobnie nie jest to główna kolumna indeksująca).Dodanie brakujących indeksów poprawi wydajność bez końca.
Nie oznacza to, że są one używane w tym zapytaniu (i prawdopodobnie nie są).
Proponuję przeczytać Gail Shaw: Finding the Causes of Sied Performance in SQL Server by Gail Shaw, część 1 i część 2 .
źródło
er101_upd_date_iso
jest ogromny varchar lub int, znacząco zmieni wydajność.Pytanie konkretnie stwierdza, że wydajność należy poprawić w przypadku zapytań ad-hoc i że nie można dodawać indeksów. Biorąc to pod uwagę, co można zrobić, aby poprawić wydajność na dowolnym stole?
Ponieważ rozważamy zapytania ad hoc, klauzula WHERE i klauzula ORDER BY mogą zawierać dowolną kombinację kolumn. Oznacza to, że prawie niezależnie od tego, jakie indeksy zostaną umieszczone w tabeli, pojawią się zapytania wymagające skanowania tabeli, jak widać powyżej w planie zapytań dla słabo wykonującego się zapytania.
Biorąc to pod uwagę, załóżmy, że w tabeli nie ma żadnych indeksów poza indeksem klastrowym na kluczu podstawowym. Zastanówmy się teraz, jakie mamy opcje, aby zmaksymalizować wydajność.
Zdefragmentuj tabelę
Dopóki mamy indeks klastrowy, możemy zdefragmentować tabelę za pomocą DBCC INDEXDEFRAG (przestarzałe) lub najlepiej ALTER INDEX . Zminimalizuje to liczbę odczytów dysku wymaganych do skanowania tabeli i poprawi szybkość.
Używaj najszybszych możliwych dysków. Nie mówisz, jakich dysków używasz, ale czy możesz używać dysków SSD.
Zoptymalizuj tempdb. Umieść tempdb na najszybszych możliwych dyskach, znowu na dyskach SSD. Zobacz ten artykuł SO i ten artykuł RedGate .
Jak stwierdzono w innych odpowiedziach, użycie bardziej wybiórczego zapytania zwróci mniej danych i dlatego powinno być szybsze.
Zastanówmy się teraz, co możemy zrobić, jeśli wolno nam dodawać indeksy.
Gdybyśmy nie mówili o zapytaniach ad hoc, dodalibyśmy indeksy specjalnie dla ograniczonego zestawu zapytań uruchamianych względem tabeli. Ponieważ omawiamy zapytania ad hoc , co można zrobić, aby w większości przypadków poprawić szybkość ?
Edytować
Przeprowadziłem kilka testów na „dużej” tabeli zawierającej 22 miliony wierszy. Moja tabela ma tylko sześć kolumn, ale zawiera 4 GB danych. Moja maszyna to przyzwoity komputer stacjonarny z 8Gb RAM i czterordzeniowym procesorem oraz pojedynczy dysk SSD Agility 3.
Usunąłem wszystkie indeksy oprócz klucza podstawowego w kolumnie Id.
Zapytanie podobne do problemu zadanego w pytaniu zajmuje 5 sekund, jeśli serwer SQL jest restartowany jako pierwszy i 3 sekundy później. Doradca strojenia bazy danych oczywiście zaleca dodanie indeksu w celu ulepszenia tego zapytania, z szacowaną poprawą o> 99%. Dodanie indeksu powoduje, że czas zapytania wynosi efektywnie zero.
Interesujące jest również to, że mój plan zapytań jest identyczny z twoim (ze skanowaniem indeksu klastrowego), ale skanowanie indeksu stanowi 9% kosztu zapytania, a sortowanie pozostałe 91%. Mogę tylko założyć, że twoja tabela zawiera ogromną ilość danych i / lub twoje dyski są bardzo wolne lub znajdują się w bardzo wolnym połączeniu sieciowym.
źródło
Nawet jeśli masz indeksy w niektórych kolumnach, które są używane w niektórych zapytaniach, fakt, że zapytanie „ad-hoc” powoduje skanowanie tabeli, pokazuje, że nie masz wystarczających indeksów, aby umożliwić sprawne wykonanie tego zapytania.
W szczególności w przypadku zakresów dat trudno jest dodać dobre indeksy.
Po prostu patrząc na zapytanie, baza danych musi posortować wszystkie rekordy według wybranej kolumny, aby móc zwrócić pierwsze n rekordów.
Czy baza danych wykonuje również pełne skanowanie tabeli bez klauzuli order by? Czy tabela ma klucz podstawowy - bez PK db będzie musiał pracować ciężej, aby wykonać sortowanie?
źródło
select top 100 * from ER101_ACCT_ORDER_DTL
Indeks to drzewo B, w którym każdy węzeł liścia wskazuje na „zbiór wierszy” (nazywany „stroną” w wewnętrznej terminologii SQL). To znaczy, gdy indeks jest indeksem nieklastrowym.
Indeks klastrowy to szczególny przypadek, w którym węzły liści mają „kilka wierszy” (zamiast wskazywać je). dlatego...
1) W tabeli może znajdować się tylko jeden indeks klastrowy.
oznacza to również, że cała tabela jest przechowywana jako indeks klastrowy, dlatego zacząłeś widzieć skanowanie indeksu zamiast skanowania tabeli.
2) Operacja korzystająca z indeksu klastrowego jest zazwyczaj szybsza niż w przypadku indeksu nieklastrowego
Przeczytaj więcej na http://msdn.microsoft.com/en-us/library/ms177443.aspx
W przypadku problemu, który masz, naprawdę powinieneś rozważyć dodanie tej kolumny do indeksu, jak powiedziałeś, dodanie nowego indeksu (lub kolumny do istniejącego indeksu) zwiększa koszty INSERT / UPDATE. Ale może być możliwe usunięcie niewykorzystanego indeksu (lub kolumny z istniejącego indeksu) i zastąpienie go „er101_upd_date_iso”.
Jeśli zmiany indeksu nie są możliwe, zalecam dodanie statystyk do kolumny, może to przyspieszyć, gdy kolumny mają pewną korelację z indeksowanymi kolumnami
http://msdn.microsoft.com/en-us/library/ms188038.aspx
BTW, uzyskasz znacznie większą pomoc, jeśli możesz opublikować schemat tabeli ER101_ACCT_ORDER_DTL. a także istniejące indeksy ... prawdopodobnie można by przepisać zapytanie, aby wykorzystać niektóre z nich.
źródło
Jednym z powodów, dla których test 1M przebiegał szybciej, jest prawdopodobnie to, że tabele tymczasowe znajdują się w całości w pamięci i zostaną przeniesione na dysk tylko wtedy, gdy serwer odczuje presję pamięci. Możesz ponownie utworzyć zapytanie, aby usunąć zamówienie, dodać dobry indeks klastrowy i indeksy obejmujące, jak wspomniano wcześniej, lub zapytać DMV, aby sprawdzić ciśnienie we / wy, aby sprawdzić, czy jest związane ze sprzętem.
-- From Glen Barry -- Clear Wait Stats (consider clearing and running wait stats query again after a few minutes) -- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR); -- Check Task Counts to get an initial idea what the problem might be -- Avg Current Tasks Count, Avg Runnable Tasks Count, Avg Pending Disk IO Count across all schedulers -- Run several times in quick succession SELECT AVG(current_tasks_count) AS [Avg Task Count], AVG(runnable_tasks_count) AS [Avg Runnable Task Count], AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count] FROM sys.dm_os_schedulers WITH (NOLOCK) WHERE scheduler_id < 255 OPTION (RECOMPILE); -- Sustained values above 10 suggest further investigation in that area -- High current_tasks_count is often an indication of locking/blocking problems -- High runnable_tasks_count is a good indication of CPU pressure -- High pending_disk_io_count is an indication of I/O pressure
źródło
Wiem, że powiedziałeś, że dodanie indeksów nie jest opcją, ale byłaby to jedyna opcja, aby wyeliminować skanowanie tabeli, którą masz. Podczas skanowania SQL Server odczytuje wszystkie 2 miliony wierszy w tabeli, aby spełnić zapytanie.
ten artykuł zawiera więcej informacji, ale pamiętaj: Szukaj = dobrze, Skanuj = źle.
Po drugie, czy nie możesz wyeliminować funkcji select * i wybrać tylko potrzebne kolumny? Po trzecie, brak klauzuli „gdzie”? Nawet jeśli masz indeks, ponieważ czytasz wszystko, co najlepsze, co otrzymasz, to skanowanie indeksu (które jest lepsze niż skanowanie tabeli, ale nie jest to wyszukiwanie, do czego powinieneś dążyć)
źródło
Wiem, że minęło sporo czasu od początku ... We wszystkich tych odpowiedziach jest dużo mądrości. Dobre indeksowanie to pierwsza rzecz przy próbie ulepszenia zapytania. Cóż, prawie pierwszy. Przede wszystkim (że tak powiem) wprowadzanie zmian w kodzie, aby był wydajny. Tak więc, po tym wszystkim, co zostało powiedziane i zrobione, jeśli masz zapytanie bez WHERE lub gdy warunek WHERE nie jest wystarczająco selektywny, istnieje tylko jeden sposób na uzyskanie danych: TABLE SCAN (INDEX SCAN). Jeśli potrzebne są wszystkie kolumny z tabeli, to zostanie użyte TABLE SCAN - nie ma co do tego wątpliwości. Może to być skanowanie sterty lub skanowanie indeksu klastrowego, w zależności od typu organizacji danych. Jedynym ostatnim sposobem na przyspieszenie (jeśli to w ogóle możliwe) jest upewnienie się, że do skanowania zostanie użytych jak najwięcej rdzeni: OPTION (MAXDOP 0). Oczywiście pomijam temat przechowywania
źródło