Mejore el rendimiento de las consultas de SQL Server en tablas grandes

85

Tengo una tabla relativamente grande (actualmente 2 millones de registros) y me gustaría saber si es posible mejorar el rendimiento de las consultas ad-hoc. La palabra ad-hoc es clave aquí. Agregar índices no es una opción (ya hay índices en las columnas que se consultan con más frecuencia).

Ejecutando una consulta simple para devolver los 100 registros actualizados más recientemente:

select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc

Tarda varios minutos. Vea el plan de ejecución a continuación:

ingrese la descripción de la imagen aquí

Detalles adicionales del escaneo de la tabla:

ingrese la descripción de la imagen aquí

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

El servidor es bastante potente (memoria ram de 48 GB, procesador de 24 núcleos) y ejecuta sql server 2008 r2 x64.

Actualizar

Encontré este código para crear una tabla con 1,000,000 de registros. Pensé que podría ejecutar SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESCen algunos servidores diferentes para averiguar si las velocidades de acceso al disco eran bajas en el servidor.

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;

Pero en los tres servidores de prueba, la consulta se ejecutó casi instantáneamente. ¿Alguien puede explicar esto?

ingrese la descripción de la imagen aquí

Actualización 2

Gracias por los comentarios, por favor, sigan viniendo ... me llevaron a intentar cambiar el índice de clave principal de no agrupado a agrupado con resultados bastante interesantes (¿e inesperados?).

No agrupado:

ingrese la descripción de la imagen aquí

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

Agrupado:

ingrese la descripción de la imagen aquí

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

¿Cómo es esto posible? Sin un índice en la columna er101_upd_date_iso, ¿cómo se puede utilizar un escaneo de índice agrupado?

Actualización 3

Según lo solicitado, este es el script de creación de tabla:

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]

La tabla tiene un tamaño de 2,8 GB con un tamaño de índice de 3,9 GB.

Lee Tickett
fuente
1
Hay pequeñas sugerencias cuando pasa el cursor del mouse sobre el elemento del plan. Muestran el costo estimado de E / S y CPU. Al principio me ocuparía del costo de E / S.
Grzegorz Gierlik
4
Table Scanindica un montón (sin índice agrupado), por lo que el primer paso sería agregar un índice agrupado bueno y rápido a su tabla. El segundo paso podría ser investigar si un índice no agrupado en er101_upd_date_isoayudaría (y no causaría otros inconvenientes de rendimiento)
marc_s
1
@marc_s gracias por eso, cambié el índice pk a agrupado y ha marcado una diferencia sustancial, ¿puede explicar esto con más detalle? (mire la actualización 2)
Lee Tickett
2
Bueno, el índice agrupado simplemente cambia el diseño de almacenamiento de la tabla. El índice agrupado contiene los datos de la tabla real en sus nodos de nivel de hoja, es decir: para leer la tabla completa, SQL Server ahora está haciendo un escaneo de índice agrupado (básicamente un "escaneo de tabla" sobre una tabla con un índice agrupado). Eso casi siempre será un poco más rápido que hacer un escaneo de tabla en un montón (sin índice agrupado). Si ahora agregó un índice no agrupado en la er101_upd_date_isocolumna, probablemente también pueda deshacerse de la operación "Ordenar" en su plan de ejecución y acelerar las cosas aún más
marc_s
2
@LeeTickett, muestre sus definiciones de tabla e índice. Hay muchos factores a considerar y nadie parece estar preguntándolos (lo que me sorprende, pero quizás no debería). Puedo decirles que 2 millones de filas NO son grandes y las tablas indexadas correctamente con más de 200 millones de filas regresan más rápido que esto. Lo más probable es que el índice agrupado (ahora que tiene uno gracias a marc_s) sea una mala elección, pero es difícil de decir sin ver los detalles. NO use particiones, pero SI use SET STATISTICS IO ON y marque Lecturas lógicas en la pestaña de mensajes. Si un cambio reduce las lecturas lógicas, se está acercando.
Solomon Rutzky

Respuestas:

59

Respuesta simple: NO. No puede ayudar con las consultas ad hoc en una tabla de 238 columnas con un factor de relleno del 50% en el índice agrupado.

Respuesta detallada:

Como he dicho en otras respuestas sobre este tema, el diseño de índices es tanto arte como ciencia y hay tantos factores a considerar que hay pocas reglas estrictas, si es que hay alguna. Debe considerar: el volumen de operaciones DML frente a SELECT, el subsistema de disco, otros índices / activadores en la tabla, la distribución de datos dentro de la tabla, son consultas que usan condiciones SARGable WHERE y varias otras cosas que ni siquiera puedo recordar bien ahora.

Puedo decir que no se puede brindar ayuda para preguntas sobre este tema sin una comprensión de la Tabla en sí, sus índices, desencadenantes, etc. Ahora que ha publicado la definición de la tabla (todavía está esperando los índices, pero la definición de la tabla solo apunta a 99% del problema) Puedo ofrecer algunas sugerencias.

Primero, si la definición de la tabla es precisa (238 columnas, 50% de factor de relleno), entonces puede ignorar el resto de las respuestas / consejos aquí ;-). Lamento ser menos que político aquí, pero en serio, es una búsqueda inútil sin conocer los detalles. Y ahora que vemos la definición de la tabla, queda bastante más claro por qué una consulta simple tomaría tanto tiempo, incluso cuando las consultas de prueba (Actualización n. ° 1) se ejecutaron tan rápido.

El principal problema aquí (y en muchas situaciones de bajo rendimiento) es un mal modelado de datos. 238 columnas no está prohibido al igual que tener 999 índices no está prohibido, pero tampoco es generalmente muy prudente.

Recomendaciones:

  1. Primero, esta mesa realmente necesita ser remodelada. Si se trata de una tabla de almacén de datos, tal vez, pero si no, estos campos realmente deben dividirse en varias tablas que pueden tener el mismo PK. Tendría una tabla de registro maestro y las tablas secundarias son solo información dependiente basada en atributos comúnmente asociados y el PK de esas tablas es el mismo que el PK de la tabla maestra y, por lo tanto, también FK para la tabla maestra. Habrá una relación 1 a 1 entre la tabla principal y todas las tablas secundarias.
  2. El uso de ANSI_PADDING OFFes perturbador, sin mencionar inconsistente dentro de la tabla debido a las diversas adiciones de columnas a lo largo del tiempo. No estoy seguro de si puede solucionarlo ahora, pero lo ideal sería que siempre lo hiciera ANSI_PADDING ON, o al menos tuviera la misma configuración en todas las ALTER TABLEdeclaraciones.
  3. Considere la posibilidad de crear 2 grupos de archivos adicionales: tablas e índices. Es mejor no poner sus cosas, PRIMARYya que ahí es donde SQL SERVER almacena todos sus datos y metadatos sobre sus objetos. Usted crea su tabla e índice agrupado (ya que son los datos de la tabla) [Tables]y todos los índices no agrupados en[Indexes]
  4. Aumente el factor de relleno del 50%. Este número bajo es probablemente la razón por la que su espacio de índice es más grande que su espacio de datos. Al hacer una reconstrucción de índice, se recrearán las páginas de datos con un máximo de 4k (del tamaño total de página de 8k) utilizado para sus datos para que su tabla se extienda en un área amplia.
  5. Si la mayoría o todas las consultas tienen "ER101_ORG_CODE" en la WHEREcondición, considere moverlo a la columna principal del índice agrupado. Suponiendo que se usa con más frecuencia que "ER101_ORD_NBR". Si "ER101_ORD_NBR" se usa con más frecuencia, consérvelo. Simplemente parece, asumiendo que los nombres de campo significan "OrganizationCode" y "OrderNumber", que "OrgCode" es una mejor agrupación que puede tener múltiples "OrderNumbers" dentro de ella.
  6. Un punto menor, pero si "ER101_ORG_CODE" es siempre de 2 caracteres, utilice en CHAR(2)lugar de, VARCHAR(2)ya que guardará un byte en el encabezado de la fila que rastrea los tamaños de ancho variable y suma millones de filas.
  7. Como han mencionado otros aquí, el uso SELECT *perjudicará el rendimiento. No solo debido a que requiere que SQL Server devuelva todas las columnas y, por lo tanto, es más probable que realice un análisis de índice agrupado independientemente de sus otros índices, sino que también le toma tiempo a SQL Server ir a la definición de la tabla y traducir *a todos los nombres de columna. . Debería ser un poco más rápido especificar los nombres de las 238 columnas en la SELECTlista, aunque eso no ayudará con el problema de Escaneo. Pero, ¿alguna vez realmente necesitaste las 238 columnas al mismo tiempo?

¡Buena suerte!

ACTUALIZACIÓN
Para completar la pregunta "cómo mejorar el rendimiento en una tabla grande para consultas ad-hoc", debe tenerse en cuenta que, si bien no ayudará en este caso específico, SI alguien está usando SQL Server 2012 (o más reciente cuando llegue ese momento) y SI la tabla no se está actualizando, entonces usar Columnstore Indexes es una opción. Para obtener más detalles sobre esa nueva función, mire aquí: http://msdn.microsoft.com/en-us/library/gg492088.aspx (creo que se hicieron para que se puedan actualizar a partir de SQL Server 2014).

ACTUALIZACIÓN 2
Las consideraciones adicionales son:

  • Habilite la compresión en el índice agrupado. Esta opción estuvo disponible en SQL Server 2008, pero como una función exclusiva de Enterprise Edition. Sin embargo, a partir de SQL Server 2016 SP1 , la compresión de datos se puso a disposición en todas las ediciones . Consulte la página de MSDN para la compresión de datos para obtener detalles sobre la compresión de filas y páginas.
  • Si no puede utilizar la compresión de datos, o si no va a dar mucho beneficio para una tabla en particular, entonces, si usted tiene una columna de un tipo de longitud fija ( INT, BIGINT, TINYINT, SMALLINT, CHAR, NCHAR, BINARY, DATETIME, SMALLDATETIME, MONEY, etc) y más de 50 % de las filas son NULL, luego considere habilitar la SPARSEopción que estuvo disponible en SQL Server 2008. Consulte la página de MSDN para usar columnas dispersas para obtener más detalles.
Salomón Rutzky
fuente
En el punto 7 personalmente me imagino que debe ser más rápido para agregar 238 nombres de las columnas de metadatos de análisis sintáctico fuera del texto de la consulta y luego tiene que comprobar los metadatos de todos modos para asegurarse de que todo exist.There son fuertes argumentos suficientes contra *y sin que uno dudosa
Martin Smith
53

Hay algunos problemas con esta consulta (y esto se aplica a todas las consultas).

Falta de índice

La falta de índice en la er101_upd_date_isocolumna es lo más importante, como ya lo mencionó Oded .

Sin un índice coincidente (cuya falta podría provocar un escaneo de la tabla), no hay posibilidad de ejecutar consultas rápidas en tablas grandes.

Si no puede agregar índices (por varias razones, entre ellas, no tiene sentido crear un índice solo para una consulta ad-hoc ), sugeriría algunas soluciones (que se pueden usar para consultas ad-hoc):

1. Utilice tablas temporales

Cree una tabla temporal en un subconjunto (filas y columnas) de datos que le interesen. La tabla temporal debe ser mucho más pequeña que la tabla de origen original, puede indexarse ​​fácilmente (si es necesario) y puede almacenar en caché el subconjunto de datos que le interesan.

Para crear una tabla temporal, puede usar código (no probado) como:

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

Pros:

  • Fácil de hacer para cualquier subconjunto de datos.
  • Fácil de administrar: es temporal y es de mesa .
  • No afecta el rendimiento general del sistema como view.
  • La tabla temporal se puede indexar.
  • No tienes que preocuparte por eso, es temporal :).

Contras:

  • Es una instantánea de los datos, pero probablemente sea lo suficientemente bueno para la mayoría de las consultas ad-hoc.

2. Expresión de tabla común: CTE

Personalmente, uso mucho CTE con consultas ad-hoc; me ayuda mucho a construir (y probar) una consulta pieza por pieza.

Vea el ejemplo a continuación (la consulta que comienza con WITH).

Pros:

  • Fácil de construir comenzando desde una vista grande y luego seleccionando y filtrando lo que realmente necesita.
  • Fácil de probar.

Contras:

  • A algunas personas no les gusta CDE: las consultas de CDE parecen ser largas y difíciles de entender.

3. Crea vistas

Similar a lo anterior, pero cree vistas en lugar de tablas temporales (si juega a menudo con las mismas consultas y tiene una versión de MS SQL que admite vistas indexadas.

Puede crear vistas o vistas indexadas en un subconjunto de datos que le interesan y ejecutar consultas en la vista, que deben contener solo un subconjunto interesante de datos mucho más pequeño que la tabla completa.

Pros:

  • Fácil de hacer.
  • Está actualizado con los datos de origen.

Contras:

  • Solo es posible para un subconjunto definido de datos.
  • Podría ser ineficaz para tablas grandes con una alta tasa de actualizaciones.
  • No es tan fácil de administrar.
  • Puede afectar el rendimiento general del sistema.
  • No estoy seguro de que las vistas indexadas estén disponibles en todas las versiones de MS SQL.

Seleccionar todas las columnas

Ejecutar star query ( SELECT * FROM) en una tabla grande no es bueno ...

Si tiene columnas grandes (como cadenas largas), se necesita mucho tiempo para leerlas del disco y pasarlas por la red.

Intentaría reemplazar *con los nombres de columna que realmente necesita.

O, si necesita todas las columnas, intente reescribir la consulta en algo como (usando una expresión de datos común ):

;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 

Lecturas sucias

Lo último que podría acelerar la consulta ad-hoc es permitir lecturas sucias con sugerencia de tablaWITH (NOLOCK) .

En lugar de la sugerencia, puede establecer el nivel de aislamiento de la transacción para leer sin compromiso:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

o establezca la configuración adecuada de SQL Management Studio.

Supongo que para consultas ad-hoc, las lecturas sucias son lo suficientemente buenas.

Grzegorz Gierlik
fuente
2
+1 para SELECT *: obliga a SQL Server a utilizar el índice agrupado. Al menos debería. No veo ninguna razón real para un índice de cobertura no agrupado ... que cubra toda la tabla :)
ta.speot.is
4
Esta respuesta sólo las direcciones de mejorar la velocidad de la consulta de ejemplo, no es la cuestión, que es "¿es posible mejorar el rendimiento de consultas ad-hoc"
Phil
CDE ahora es CTE (expresión de tabla común)
sqluser
12

Está obteniendo un escaneo de tabla allí, lo que significa que no tiene un índice definido en er101_upd_date_iso, o si esa columna es parte de un índice existente, el índice no se puede usar (posiblemente no sea la columna del indexador principal).

Agregar índices faltantes ayudará al rendimiento sin fin.

ya hay índices en las columnas que se consultan con más frecuencia

Eso no significa que se utilicen en esta consulta (y probablemente no lo sean).

Sugiero leer Finding the Causes of Poor Performance in SQL Server por Gail Shaw, parte 1 y parte 2 .

Oded
fuente
mi punto es que esta no es una de las columnas que se consultan con más frecuencia :)
Lee Tickett
1
@LeeTickett - Y, sin embargo, esta es la única columna en la que posiblemente puede agregar un índice para mejorar el rendimiento de esta consulta.
Oded
2
No existen las búsquedas optimizadas no indexadas. Estás usando un índice o un escaneo de tabla completo. Si no desea escaneos completos de tablas, necesita índices. Dependiendo de su perfil de uso, podría ser lo suficientemente económico agregar índices. Ah, y el tipo de datos de la columna también es importante. Si tu er101_upd_date_isoes un varchar enorme, o un int, el rendimiento cambiará significativamente.
Cylindric
Gracias. He añadido un comentario a la pregunta. Aprecio que es poco probable que pueda optimizar la consulta, pero imagino que puede haber formas de mejorar el rendimiento de las consultas ad-hoc
Lee Tickett
Soy un alumno, ¿cómo sabemos qué columna necesita indexarse?
Virus
7

La pregunta establece específicamente que se debe mejorar el rendimiento para las consultas ad-hoc y que no se pueden agregar índices. Entonces, tomando eso al pie de la letra, ¿qué se puede hacer para mejorar el rendimiento en cualquier mesa?

Dado que estamos considerando consultas ad-hoc, la cláusula WHERE y la cláusula ORDER BY pueden contener cualquier combinación de columnas. Esto significa que casi independientemente de los índices que se coloquen en la tabla, habrá algunas consultas que requerirán un escaneo de la tabla, como se ve arriba en el plan de consulta de una consulta de bajo rendimiento.

Teniendo esto en cuenta, supongamos que no hay ningún índice en la tabla, aparte de un índice agrupado en la clave principal. Ahora consideremos qué opciones tenemos para maximizar el rendimiento.

  • Desfragmentar la mesa

    Siempre que tengamos un índice agrupado, podemos desfragmentar la tabla usando DBCC INDEXDEFRAG (obsoleto) o preferiblemente ALTER INDEX . Esto minimizará el número de lecturas de disco necesarias para escanear la tabla y mejorará la velocidad.

  • Utilice los discos más rápidos posibles. No dice qué discos está usando, pero si puede usar SSD.

  • Optimice tempdb. Coloque tempdb en los discos más rápidos posibles, nuevamente SSD. Vea este artículo de SO y este artículo de RedGate .

  • Como se indica en otras respuestas, el uso de una consulta más selectiva devolverá menos datos y, por lo tanto, debería ser más rápido.

Ahora consideremos lo que podemos hacer si se nos permite agregar índices.

Si nosotros no estábamos hablando de consultas ad-hoc, a continuación, nos gustaría añadir índices específicamente para el conjunto limitado de consultas entre en funcionamiento con la tabla. Dado que estamos discutiendo consultas ad-hoc , ¿qué se puede hacer para mejorar la velocidad la mayor parte del tiempo?

  • Agregue un índice de una sola columna a cada columna. Esto debería darle a SQL Server al menos algo con lo que trabajar para mejorar la velocidad de la mayoría de las consultas, pero no será óptimo.
  • Agregue índices específicos para las consultas más comunes para que estén optimizadas.
  • Agregue índices específicos adicionales según sea necesario mediante la supervisión de consultas de bajo rendimiento.

Editar

Ejecuté algunas pruebas en una tabla "grande" de 22 millones de filas. Mi tabla solo tiene seis columnas, pero contiene 4 GB de datos. Mi máquina es una computadora de escritorio respetable con 8 Gb de RAM y una CPU de cuatro núcleos y tiene un solo SSD Agility 3.

Eliminé todos los índices, excepto la clave principal en la columna Id.

Una consulta similar al problema dado en la pregunta toma 5 segundos si el servidor SQL se reinicia primero y 3 segundos después. El asesor de ajuste de la base de datos obviamente recomienda agregar un índice para mejorar esta consulta, con una mejora estimada de> 99%. Agregar un índice da como resultado un tiempo de consulta efectivamente cero.

Lo que también es interesante es que mi plan de consulta es idéntico al suyo (con el escaneo de índice agrupado), pero el escaneo de índice representa el 9% del costo de la consulta y la clasificación el 91% restante. Solo puedo asumir que su tabla contiene una enorme cantidad de datos y / o sus discos son muy lentos o están ubicados en una conexión de red muy lenta.

Phil
fuente
2

Incluso si tiene índices en algunas columnas que se utilizan en algunas consultas, el hecho de que su consulta 'ad-hoc' provoque un escaneo de tabla muestra que no tiene índices suficientes para permitir que esta consulta se complete de manera eficiente.

Para los rangos de fechas en particular, es difícil agregar buenos índices.

Con solo mirar su consulta, la base de datos tiene que ordenar todos los registros por la columna seleccionada para poder devolver los primeros n registros.

¿La base de datos también realiza un escaneo completo de la tabla sin la cláusula order by? ¿Tiene la tabla una clave principal? Sin una PK, la base de datos tendrá que trabajar más para realizar la clasificación.

foamdino
fuente
Hay una clave primaria sobre la mesa. Un escaneo de tabla también aparece en el plan de ejecución cuando simplemente se ejecutaselect top 100 * from ER101_ACCT_ORDER_DTL
Lee Tickett
2

¿Cómo es esto posible? Sin un índice en la columna er101_upd_date_iso, ¿cómo se puede utilizar un escaneo de índice agrupado?

Un índice es un árbol B donde cada nodo hoja apunta a un 'grupo de filas' (llamado 'Página' en la terminología interna de SQL). Es decir, cuando el índice es un índice no agrupado.

El índice agrupado es un caso especial, en el que los nodos hoja tienen el 'grupo de filas' (en lugar de apuntar a ellos). es por eso que...

1) Solo puede haber un índice agrupado en la tabla.

esto también significa que toda la tabla se almacena como el índice agrupado, por eso comenzó a ver un escaneo de índice en lugar de un escaneo de tabla.

2) Una operación que utiliza un índice agrupado es generalmente más rápida que un índice no agrupado

Leer más en http://msdn.microsoft.com/en-us/library/ms177443.aspx

Para el problema que tiene, realmente debería considerar agregar esta columna a un índice, como dijo, agregar un nuevo índice (o una columna a un índice existente) aumenta los costos de INSERTAR / ACTUALIZAR. Pero podría ser posible eliminar algún índice infrautilizado (o una columna de un índice existente) para reemplazarlo con 'er101_upd_date_iso'.

Si los cambios de índice no son posibles, recomiendo agregar una estadística en la columna, puede arreglar las cosas cuando las columnas tienen alguna correlación con las columnas indexadas

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

Por cierto, obtendrá mucha más ayuda si puede publicar el esquema de tabla de ER101_ACCT_ORDER_DTL. y los índices existentes también ..., probablemente la consulta podría reescribirse para usar algunos de ellos.

shankar_pratap
fuente
+1 por la respuesta. Sin embargo, un comentario, los índices agrupados no siempre son más rápidos como se podría leer (es posible malinterpretarlo) de su respuesta.
Gisli
Creo que entiendo la diferencia entre el índice agrupado / no agrupado, pero todavía no veo cómo se puede mejorar la consulta de una columna que no forma parte del índice agrupado al tener un índice agrupado en otras columnas.
Lee Tickett
En SQL Server, el índice agrupado contiene todas las columnas. El índice agrupado es lo que decide cómo se almacenan los datos en el disco. Es un poco difícil para mí explicarlo, pero si piensa que los índices son un árbol, el índice no agrupado es un árbol y las hojas inferiores contienen la información que definió como un índice. Para un índice agrupado, las hojas inferiores contienen todas las columnas de la tabla. Esto es por diseño en SQL Server.
Gisli
Lo entiendo. Pero pensé que las ramas se basaban en las columnas del índice agrupado. Entonces, si la columna que estoy consultando no está en el índice agrupado, ¿seguramente todas las ramas / hojas deben ser escaneadas?
Lee Tickett
1
No entiendo esto. Mi mejor suposición es que cuando tenía un índice no agrupado, se escaneaba dando como resultado una gran cantidad de E / S aleatorias. Cuando creó el índice agrupado, ¿se deshizo de esas E / S aleatorias? Pero esto es una suposición, no puedo encontrar ninguna otra razón para este comportamiento, pero no soy un experto.
Gisli
1

Una de las razones por las que su prueba de 1M se ejecutó más rápido es probablemente porque las tablas temporales están completamente en la memoria y solo irían al disco si su servidor experimenta presión de memoria. Puede volver a diseñar su consulta para eliminar el orden, agregar un buen índice agrupado e índices de cobertura como se mencionó anteriormente, o consultar al DMV para verificar la presión de IO para ver si está relacionado con el hardware.

-- 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
Ninghad
fuente
Espero que toda mi base de datos esté en la memoria. ¿Hay alguna manera de verificar eso o de decirle a sql qué tablas almacenar en la memoria? Estuve fuera por unos días, pero cuando regrese intentaré su consulta, gracias
Lee Tickett
Avg Task Count y Avg Pending DiskIO Count alcanzaron un máximo de 4. Todavía tengo curiosidad por intentar forzar la base de datos en la RAM.
Lee Tickett
0

Sé que dijiste que agregar índices no es una opción, pero esa sería la única opción para eliminar el escaneo de tablas que tienes. Cuando realiza un escaneo, SQL Server lee los 2 millones de filas de la tabla para completar su consulta.

este artículo proporciona más información, pero recuerde: Buscar = bueno, Escanear = malo.

En segundo lugar, ¿no puede eliminar la selección * y seleccionar solo las columnas que necesita? En tercer lugar, ¿ninguna cláusula "dónde"? Incluso si tiene un índice, dado que está leyendo todo, lo mejor que obtendrá es un escaneo de índice (que es mejor que un escaneo de tabla, pero no es una búsqueda, que es lo que debe buscar)

Diego
fuente
No es cierto que Seek sea siempre mejor que Scan. A veces, un escaneo es más eficiente. Si este no fuera el caso, M $ no habría incluido una sugerencia de consulta FORCESCAN a partir de SQL Server 2008 R2. Consulte aquí para obtener más detalles: msdn.microsoft.com/en-us/library/ms181714(v=sql.105).aspx e incluso aquí para alguien que busca forzar un escaneo (la tercera respuesta de Adam Haines tiene buena información): social .msdn.microsoft.com / Forums / en-US / transactsql / thread /…
Solomon Rutzky
1
Bueno, en primer lugar, las búsquedas son buenas para consultas puntuales. En segundo lugar, los análisis son buenos para consultas de rango en las que se deben recuperar muchos datos. Los sistemas OLAP no funcionarían bien sin escaneos. Los sistemas OLTP no funcionarían bien sin búsquedas. Todo tiene su lugar en el gran esquema de las cosas ...
darlove
0

Sé que ha pasado bastante tiempo desde el principio ... Hay mucha sabiduría en todas estas respuestas. Una buena indexación es lo primero cuando se intenta mejorar una consulta. Bueno, casi el primero. Lo más importante (por así decirlo) es realizar cambios en el código para que sea eficiente. Entonces, después de todo lo dicho y hecho, si uno tiene una consulta sin WHERE, o cuando la condición WHERE no es lo suficientemente selectiva, solo hay una forma de obtener los datos: TABLE SCAN (INDEX SCAN). Si se necesitan todas las columnas de una tabla, entonces se utilizará TABLE SCAN, sin duda alguna. Esto puede ser un análisis de pila o un análisis de índice agrupado, según el tipo de organización de datos. La única forma de acelerar las cosas (si es posible) es asegurarse de que se utilicen tantos núcleos como sea posible para realizar el escaneo: OPCIÓN (MAXDOP 0). Estoy ignorando el tema del almacenamiento, por supuesto,

cariño
fuente