Подтвердить что ты не робот

Повысить производительность запросов SQL Server на больших таблицах

Я имею относительно большую таблицу (в настоящее время 2 миллиона записей) и хотел бы знать, возможно ли повысить производительность для специальных запросов. Здесь ключевое слово ad-hoc. Добавление индексов не является опцией (уже есть индексы для столбцов, которые чаще всего запрашиваются).

Запуск простого запроса для возврата 100 последних обновленных записей:

select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc

Занимает несколько минут. См. План выполнения ниже:

enter image description here

Дополнительная информация из сканирования таблицы:

enter image description here

SQL Server Execution Times:
  CPU time = 3945 ms,  elapsed time = 148524 ms.

Сервер довольно мощный (из памяти 48 ГБ RAM, 24-ядерный процессор), работающий на сервере sql server r2 x64.

Обновление

Я нашел этот код для создания таблицы с 1 000 000 записей. Я подумал, что могу запустить SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESC на нескольких разных серверах, чтобы узнать, были ли на сервере ограниченные скорости доступа на диск.

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;

Но на трех тестовых серверах запрос выполнялся почти мгновенно. Кто-нибудь может это объяснить?

enter image description here

Обновление 2

Благодарим вас за комментарии - пожалуйста, продолжайте их... они заставили меня попробовать изменить индекс первичного ключа из некластеризованных в кластеризованные с довольно интересными (и неожиданными?) результатами.

некластерированных:

enter image description here

SQL Server Execution Times:
  CPU time = 3634 ms,  elapsed time = 154179 ms.

Кластерный:

enter image description here

SQL Server Execution Times:
  CPU time = 2650 ms,  elapsed time = 52177 ms.

Как это возможно? Без индекса в столбце er101_upd_date_iso, как можно использовать сканирование с кластерным индексом?

Обновление 3

Как запрошено - это таблица create script:

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]

Таблица имеет размер 2,8 ГБ с индексом размером 3,9 ГБ.

4b9b3361

Ответ 1

Есть несколько проблем с этим запросом (и это относится к каждому запросу).

Недостаток индекса

Недостаток индекса в столбце er101_upd_date_iso наиболее важен, поскольку Oded уже упоминался.

Без соответствия индекса (который не может вызвать сканирование таблицы) нет возможности запускать быстрые запросы в больших таблицах.

Если вы не можете добавить индексы (по разным причинам, в том числе нет смысла создавать индекс только для одного специального запроса), я бы предложил несколько обходных путей (которые можно использовать для специальных запросов):

1. Использовать временные таблицы

Создайте временную таблицу в подмножестве (строках и столбцах) интересующих вас данных. Временная таблица должна быть намного меньше исходной исходной таблицы, может быть легко проиндексирована (если необходимо) и может кэшировать подмножество данных, которые вас интересуют.

Чтобы создать временную таблицу, вы можете использовать код (не проверенный), например:

-- 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

Плюсы:

  • Легко сделать для любого подмножества данных.
  • Простота управления - это временная и эта таблица.
  • Не влияет на общую производительность системы, например view.
  • Временная таблица может быть проиндексирована.
  • Вам не нужно заботиться об этом - это временно:).

Минусы:

  • Это моментальный снимок данных - но, вероятно, это достаточно хорошо для большинства специальных запросов.

2. Общее табличное выражение - CTE

Лично я часто использую CTE с специальными запросами - он очень помогает в построении (и тестировании) запроса по частям.

См. пример ниже (запрос, начинающийся с WITH).

Плюсы:

  • Легко создавать, начиная с большого вида, а затем выбирать и фильтровать то, что вам действительно нужно.
  • Легко проверить.

Минусы:

  • Некоторым людям не нравятся запросы CDE - CDE, кажется, долго и трудно понять.

3. Создать представления

Аналогично выше, но создавайте представления вместо временных таблиц (если вы часто играете с одинаковыми запросами, и у вас есть версия MS SQL, которая поддерживает индексированные представления.

Вы можете создавать представления или индексированные представления на подмножество данных, которые вас интересуют и запускать запросы на просмотр - которые должны содержать только интересный поднабор данных, намного меньший, чем вся таблица.

Плюсы:

  • Легко сделать.
  • Он обновляется с исходными данными.

Минусы:

  • Возможно только для определенного подмножества данных.
  • Может быть неэффективным для больших таблиц с высокой скоростью обновлений.
  • Нелегко управлять.
  • Может влиять на общую производительность системы.
  • Я не уверен, что индексированные представления доступны в каждой версии MS SQL.

Выбор всех столбцов

Запуск запроса на звезду (SELECT * FROM) на большой таблице не очень хорошо...

Если у вас большие столбцы (например, длинные строки), для чтения их с диска требуется много времени и передавать по сети.

Я бы постарался заменить * на имена столбцов, которые вам действительно нужны.

Или, если вам нужны все столбцы, попробуйте переписать запрос на что-то вроде (с использованием общего выражения данных):

;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 

Грязные чтения

Последняя вещь, которая может ускорить запрос ad-hoc, позволяет грязным чтениям с подсказкой таблицы WITH (NOLOCK).

Вместо подсказки вы можете установить уровень изоляции транзакций для чтения без указания:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

или установите правильную настройку SQL Management Studio.

Я предполагаю, что для ad-hoc-запросов грязные чтения достаточно хороши.

Ответ 2

Простой ответ: НЕТ. Вы не можете помочь ad hoc-запросам в таблице столбцов 238 с 50% -ным коэффициентом заполнения в кластерном индексе.

Подробный ответ:

Как я уже говорил в других ответах на эту тему, дизайн индекса - это как искусство, так и наука, и есть так много факторов, которые считают, что существует несколько, если таковые имеются, жестких и быстрых правил. Вы должны учитывать: объем операций DML против SELECT, дисковой подсистемы, других индексов/триггеров в таблице, распределение данных внутри таблицы, - это запросы с использованием условий SARGable WHERE и несколько других вещей, которые я даже не могу запомнить Теперь.

Я могу сказать, что никакая помощь не может быть предоставлена ​​для вопросов по этой теме без понимания самой таблицы, ее индексов, триггеров и т.д. Теперь, когда вы разместили определение таблицы (все еще ожидая индексов, но определение таблицы один указывает на 99% проблемы). Я могу предложить некоторые предложения.

Во-первых, если точное определение таблицы (238 столбцов, 50% Fill Factor), вы можете в значительной степени игнорировать остальные ответы/рекомендации здесь;-). Извините, что здесь здесь меньше, чем политический, но серьезно, это дикая охота на гусей, не зная специфики. И теперь, когда мы видим определение таблицы, становится немного понятнее, почему простой запрос занимает очень много времени, даже когда тестовые запросы (Update # 1) выполняются так быстро.

Основная проблема здесь (и во многих ситуациях с низкой эффективностью) - плохое моделирование данных. 238 столбцов не запрещено, так как 999 индексов не запрещены, но это также не очень разумно.

Рекомендации:

  • Во-первых, эта таблица действительно нуждается в реконструировании. Если это таблица хранилища данных, то, возможно, но если нет, то эти поля действительно должны быть разбиты на несколько таблиц, которые могут иметь один и тот же PK. У вас будет таблица основных записей, а дочерние таблицы - только зависимая информация, основанная на общепринятых атрибутах, а PK этих таблиц совпадает с PK главной таблицы и, следовательно, FK в главной таблице. Между основными и всеми дочерними таблицами будет связь 1 к 1.
  • Использование ANSI_PADDING OFF вызывает беспокойство, не говоря уже о несогласованности внутри таблицы из-за различных добавлений столбцов с течением времени. Не уверен, что вы можете исправить это сейчас, но в идеале вы всегда будете иметь ANSI_PADDING ON или, по крайней мере, иметь одинаковые настройки для всех операторов ALTER TABLE.
  • Рассмотрим создание двух дополнительных групп файлов: таблицы и индексы. Лучше не помещать ваши вещи в PRIMARY, так как SQL SERVER хранит все свои данные и метаданные о ваших объектах. Вы создаете таблицу и кластерный индекс (как это данные для таблицы) на [Tables] и все индексы без кластеризации на [Indexes]
  • Увеличьте коэффициент заполнения от 50%. Это небольшое число, вероятно, почему ваше пространство индекса больше вашего пространства данных. Выполнение перестроения индекса воссоздает страницы данных с максимальным размером 4k (из общего размера страницы 8k), используемым для ваших данных, чтобы ваша таблица была распределена по широкой области.
  • Если у большинства или всех запросов есть условие "ER101_ORG_CODE" в WHERE, рассмотрите возможность переместить его в ведущий столбец кластерного индекса. Предполагая, что он используется чаще, чем "ER101_ORD_NBR". Если "ER101_ORD_NBR" используется чаще, то сохраняйте его. Похоже, предполагается, что названия полей означают "OrganizationCode" и "OrderNumber", что "OrgCode" - лучшая группировка, которая может содержать в ней несколько "OrderNumbers".
  • Незначительная точка, но если "ER101_ORG_CODE" всегда 2 символа, используйте CHAR(2) вместо VARCHAR(2), поскольку он сохранит байт в заголовке строки, который отслеживает размеры ширины и суммирует более миллиона строк.
  • Как упоминалось выше, использование SELECT * может повредить производительность. Не только из-за этого требуется, чтобы SQL Server возвращал все столбцы и, следовательно, с большей вероятностью выполнял Clustered Index Scan независимо от ваших других индексов, но также требуется время SQL Server, чтобы перейти к определению таблицы и перевести * во все имена столбцов. Это должно быть немного быстрее, чтобы указать все имена столбцов 238 в списке SELECT, хотя это не поможет проблеме сканирования. Но в любом случае вам действительно нужны все 238 столбцов?

Удачи!

UPDATE
Ради полноты вопроса о том, как повысить производительность на большой таблице для специальных запросов, следует отметить, что, хотя это не поможет в этом конкретном случае, если кто-то использует SQL Server 2012 (или новее, когда это время приходит) и ЕСЛИ таблица не обновляется, а использование индексов столбцов является опцией. Подробнее об этой новой функции смотрите здесь: http://msdn.microsoft.com/en-us/library/gg492088.aspx (я считаю, что они были обновлены, начиная с SQL Server 2014).

ОБНОВЛЕНИЕ 2
Дополнительные соображения:

  • Включить сжатие в кластерном индексе. Этот параметр стал доступен в SQL Server 2008, но как функция Enterprise Edition. Однако с SQL Server 2016 SP1 сжатие данных было доступно во всех выпусках! Для получения подробной информации о сжатии строк и страниц см. Страницу MSDN для Сжатие данных.
  • Если вы не можете использовать сжатие данных или если это не принесет большой пользы для конкретной таблицы, то ЕСЛИ у вас есть столбец типа фиксированной длины (INT, BIGINT, TINYINT, SMALLINT, CHAR, NCHAR, BINARY, DATETIME, SMALLDATETIME, MONEY и т.д.), а более 50% строк - NULL, затем рассмотрите возможность включения опции SPARSE который стал доступен в SQL Server 2008. Подробнее см. на странице MSDN для Использовать разреженные столбцы.

Ответ 3

Вы получаете сканирование таблицы, что означает, что у вас нет индекса, определенного в er101_upd_date_iso, или если этот столбец является частью существующего индекса, индекс не может быть использован (возможно, это не основной столбец индексатора).

Добавление отсутствующих индексов поможет производительности без конца.

уже есть индексы по столбцам, которые чаще всего запрашиваются

Это не значит, что они используются в этом запросе (и, вероятно, нет).

Я предлагаю прочитать "Поиск причин низкой производительности в SQL Server" Гейл Шоу, часть 1 и часть 2.

Ответ 4

В конкретном вопросе указывается, что производительность должна быть улучшена для запросов ad-hoc, и что индексы не могут быть добавлены. Так что, принимая это за чистую монету, что можно сделать для повышения производительности на любой таблице?

Поскольку мы рассматриваем ad-hoc-запросы, предложение WHERE и предложение ORDER BY могут содержать любую комбинацию столбцов. Это означает, что почти независимо от того, какие индексы размещены в таблице, будут выполняться некоторые запросы, требующие сканирования таблицы, как показано выше в плане запроса плохо выполняемого запроса.

Учитывая это, допустим, что на таблице нет индексов, кроме кластерного индекса первичного ключа. Теперь рассмотрим, какие параметры мы должны максимизировать производительность.

  • Дефрагментация таблицы

    Пока у нас есть кластерный индекс, мы можем дефрагментировать таблицу, используя DBCC INDEXDEFRAG (устаревший) или предпочтительно ALTER INDEX. Это позволит свести к минимуму количество считываемых дисков, необходимых для сканирования таблицы, и улучшит скорость.

  • Используйте самые быстрые диски. Вы не говорите, какие диски используете, но можете использовать SSD.

  • Оптимизируйте tempdb. Положите tempdb на самые быстрые диски, снова SSD. См. Эту статью SO и эту статью RedGate.

  • Как указано в других ответах, использование более избирательного запроса будет возвращать меньше данных, и поэтому должно быть быстрее.

Теперь рассмотрим, что мы можем сделать, если нам разрешено добавлять индексы.

Если бы мы не говорили о специальных запросах, мы бы добавили индексы специально для ограниченного набора запросов, выполняемых против таблицы. Поскольку мы обсуждаем специальные запросы, что можно сделать, чтобы улучшить скорость большую часть времени?

  • Добавить индекс столбца в каждый столбец. Это должно дать SQL Server хотя бы кое-что, с чем можно работать, чтобы улучшить скорость для большинства запросов, но не будет оптимальным.
  • Добавьте определенные индексы для наиболее распространенных запросов, чтобы они были оптимизированы.
  • Добавьте дополнительные конкретные индексы, необходимые для мониторинга для плохо выполняемых запросов.

Edit

Я провел несколько тестов на "большой" таблице из 22 миллионов строк. Моя таблица имеет только шесть столбцов, но содержит 4 ГБ данных. Моя машина - респектабельный рабочий стол с 8 Гб оперативной памятью и четырехъядерным процессором и имеет единый SSD-модуль Agility 3.

Я удалил все индексы отдельно от первичного ключа в столбце Id.

Аналогичный запрос к задаче, заданной в вопросе, занимает 5 секунд, если сначала перезагружается сервер SQL и через 3 секунды. Советник по настройке базы данных, очевидно, рекомендует добавить индекс для улучшения этого запроса с предполагаемым улучшением > 99%. Добавление индекса приводит к эффективному нулю времени запроса.

Интересно также, что мой план запроса идентичен вашему (с кластерным сканированием индексов), но сканирование индекса составляет 9% от стоимости запроса и сортировка оставшихся 91%. Я могу только предположить, что ваша таблица содержит огромное количество данных и/или ваши диски очень медленные или расположены по очень медленному сетевому соединению.

Ответ 5

Даже если у вас есть индексы для некоторых столбцов, которые используются в некоторых запросах, тот факт, что ваш "ad-hoc" запрос вызывает сканирование таблицы, показывает, что у вас недостаточно индексов, чтобы этот запрос мог эффективно функционировать.

Для диапазонов дат, в частности, трудно добавить хорошие индексы.

Просто глядя на ваш запрос, db должен сортировать все записи по выбранному столбцу, чтобы иметь возможность возвращать первые n записей.

Делает ли db полное сканирование таблицы без предложения order by? В таблице есть первичный ключ - без ПК, для выполнения сортировки db придется больше работать?

Ответ 6

Как это возможно? Без индекса в столбце er101_upd_date_iso, как можно использовать сканирование с кластерным индексом?

Индекс - это B-Tree, где каждый лист node указывает на "группу строк" ​​(называемую "страницей" в внутренней терминологии SQL). Именно тогда индекс является некластеризованным индексом.

Кластеризованный индекс - это особый случай, в котором листовые узлы имеют "группу строк" ​​(а не указывают на них). вот почему...

1) В таблице может быть только один кластеризованный индекс.

это также означает, что вся таблица хранится как кластеризованный индекс, поэтому вы начали видеть сканирование индекса, а не сканирование таблицы.

2) Операция, которая использует кластерный индекс, обычно быстрее, чем некластеризованный индекс

Подробнее на http://msdn.microsoft.com/en-us/library/ms177443.aspx

Для данной проблемы вам стоит подумать о том, чтобы добавить этот столбец в индекс, поскольку, как вы сказали, добавление нового индекса (или столбца к существующему индексу) увеличивает затраты INSERT/UPDATE. Но может быть возможно удалить некоторый недоиспользуемый индекс (или столбец из существующего индекса), чтобы заменить его на "er101_upd_date_iso".

Если изменения индекса невозможны, я рекомендую добавлять статистику по столбцу, он может закрепить ситуацию, когда столбцы имеют некоторую корреляцию с индексированными столбцами

http://msdn.microsoft.com/en-us/library/ms188038.aspx

BTW, вам будет оказана дополнительная помощь, если вы сможете опубликовать схему таблицы ER101_ACCT_ORDER_DTL. и существующие индексы тоже... вероятно, запрос может быть переписан для использования некоторых из них.

Ответ 7

Одна из причин, по которой ваш тест 1M запускался быстрее, вероятен, потому что временные таблицы полностью хранятся в памяти и будут поступать только на диск, если ваш сервер испытывает давление памяти. Вы можете либо повторно обработать свой запрос, чтобы удалить заказ, добавьте хороший кластеризованный индекс и покрывающий индекс (ы), как упоминалось ранее, либо запросите DMV, чтобы проверить давление IO, чтобы увидеть, связано ли оборудование.

-- 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

Ответ 8

Я знаю, что вы сказали, что добавление индексов не является вариантом, но это единственный вариант для устранения сканирования таблицы, которое у вас есть. Когда вы выполняете сканирование, SQL Server считывает все 2 миллиона строк в таблице для выполнения вашего запроса.

эта статья содержит дополнительную информацию, но помните: Seek = good, Scan = bad.

Во-вторых, вы не можете устранить выбор * и выбрать только нужные столбцы? В-третьих, нет предложения "where"? Даже если у вас есть индекс, поскольку вы читаете все, лучшее, что вы получите, это сканирование индекса (что лучше, чем сканирование таблицы, но оно не является поиском, к чему вы должны стремиться)

Ответ 9

Я знаю, что это было довольно давно с самого начала... Во всех этих ответах есть много мудрости. Хорошая индексация - это первое, что пытается улучшить запрос. Ну, почти первый. Самым первым (так сказать) является внесение изменений в код, чтобы он был эффективным. Итак, после того, как все было сказано и сделано, если у вас есть запрос без WHERE или когда условие WHERE не является достаточно избирательным, есть только один способ получить данные: TABLE SCAN (INDEX SCAN). Если вам нужны все столбцы из таблицы, тогда будет использоваться TABLE SCAN - не вопрос об этом. Это может быть сканирование кучи или кластерное сканирование индексов в зависимости от типа организации данных. Единственный последний способ ускорить процесс (если это вообще возможно) - убедиться в том, что как можно больше ядер используется для сканирования: OPTION (MAXDOP 0). Конечно, я игнорирую предмет хранения, но нужно убедиться, что у вас есть неограниченная оперативная память, что само собой разумеется:)