J'ai une table relativement grande (actuellement 2 millions d'enregistrements) et j'aimerais savoir s'il est possible d'améliorer les performances pour les requêtes ad hoc. Le mot ad-hoc est ici clé. L'ajout d'index n'est pas une option (il existe déjà des index sur les colonnes qui sont le plus souvent interrogées).
Exécution d'une requête simple pour renvoyer les 100 derniers enregistrements mis à jour:
select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc
Prend plusieurs minutes. Voir le plan d'exécution ci-dessous:
Détails supplémentaires de l'analyse de la table:
SQL Server Execution Times:
CPU time = 3945 ms, elapsed time = 148524 ms.
Le serveur est assez puissant (avec une mémoire de 48 Go de RAM, un processeur à 24 cœurs) exécutant SQL Server 2008 r2 x64.
Mise à jour
J'ai trouvé ce code pour créer une table avec 1 000 000 enregistrements. J'ai pensé que je pourrais alors courir SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESC
sur quelques serveurs différents pour savoir si mes vitesses d'accès au disque étaient mauvaises sur le serveur.
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;
Mais sur les trois serveurs de test, la requête s'est exécutée presque instantanément. Quelqu'un peut-il expliquer cela?
Mise à jour 2
Merci pour les commentaires - continuez-les à venir ... ils m'ont amené à essayer de changer l'index de clé primaire de non-clusterisé à clusterisé avec des résultats plutôt intéressants (et inattendus?).
Non groupé:
SQL Server Execution Times:
CPU time = 3634 ms, elapsed time = 154179 ms.
Clustered:
SQL Server Execution Times:
CPU time = 2650 ms, elapsed time = 52177 ms.
Comment est-ce possible? Sans index dans la colonne er101_upd_date_iso, comment utiliser une analyse d'index clusterisé?
Mise à jour 3
Comme demandé, voici le script de création de table:
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 table a une taille de 2,8 Go avec une taille d'index de 3,9 Go.
la source
Table Scan
indique un tas (pas d'index clusterisé) - la première étape consiste donc à ajouter un bon index clusterisé rapide à votre table. Deuxième étape pourrait consister à vérifier si un index non clusterisé surer101_upd_date_iso
aiderions (et non la cause d' autres inconvénients de la performance)er101_upd_date_iso
colonne, vous pouvez probablement aussi vous débarrasser de l'opération "Trier" dans votre plan d'exécution et accélérer encore plus les chosesRéponses:
Réponse simple: NON. Vous ne pouvez pas aider les requêtes ad hoc sur une table de 238 colonnes avec un facteur de remplissage de 50% sur l'index clusterisé.
Réponse détaillée:
Comme je l'ai indiqué dans d'autres réponses sur ce sujet, la conception d'index est à la fois un art et une science et il y a tellement de facteurs à considérer qu'il y a peu, voire aucune, de règles strictes et rapides. Vous devez prendre en compte: le volume des opérations DML par rapport aux SELECT, le sous-système de disque, les autres index / déclencheurs sur la table, la distribution des données dans la table, sont des requêtes utilisant les conditions SARGable WHERE, et plusieurs autres choses dont je ne me souviens même pas correctement maintenant.
Je peux dire qu'aucune aide ne peut être donnée pour les questions sur ce sujet sans une compréhension de la table elle-même, de ses index, déclencheurs, etc. 99% du problème) Je peux offrir quelques suggestions.
Premièrement, si la définition du tableau est précise (238 colonnes, facteur de remplissage de 50%), vous pouvez ignorer à peu près le reste des réponses / conseils ici ;-). Désolé d'être moins que politique ici, mais sérieusement, c'est une chasse à l'oie sauvage sans connaître les détails. Et maintenant que nous voyons la définition de la table, il devient un peu plus clair de savoir pourquoi une simple requête prendrait si longtemps, même lorsque les requêtes de test (mise à jour n ° 1) ont été exécutées si rapidement.
Le principal problème ici (et dans de nombreuses situations de mauvaise performance) est une mauvaise modélisation des données. 238 colonnes n'est pas interdit, tout comme avoir 999 index n'est pas interdit, mais ce n'est généralement pas très sage.
Recommandations:
ANSI_PADDING OFF
est dérangeante, sans parler d'incohérence dans le tableau en raison des divers ajouts de colonnes au fil du temps. Je ne sais pas si vous pouvez résoudre ce problème maintenant, mais idéalement, vous auriez toujoursANSI_PADDING ON
, ou à tout le moins, le même paramètre dans toutes lesALTER TABLE
instructions.PRIMARY
car c'est là que SQL SERVER stocke toutes ses données et métadonnées sur vos objets. Vous créez votre table et votre index clusterisé (car il s'agit des données de la table) sur[Tables]
et tous les index non groupés sur[Indexes]
WHERE
condition, envisagez de le déplacer vers la première colonne de l'index clusterisé. En supposant qu'il est utilisé plus souvent que "ER101_ORD_NBR". Si "ER101_ORD_NBR" est utilisé plus souvent, conservez-le. Il semble juste, en supposant que les noms de champ signifient «OrganizationCode» et «OrderNumber», que «OrgCode» est un meilleur groupement qui pourrait avoir plusieurs «OrderNumbers» à l'intérieur.CHAR(2)
au lieu deVARCHAR(2)
car cela sauvera un octet dans l'en-tête de ligne qui suit les tailles de largeur variable et s'additionne sur des millions de lignes.SELECT *
nuira aux performances. Non seulement parce que SQL Server doit renvoyer toutes les colonnes et donc être plus susceptible d'effectuer une analyse d'index en cluster quels que soient vos autres index, mais il faut également du temps à SQL Server pour accéder à la définition de la table et la traduire*
dans tous les noms de colonne. . Il devrait être légèrement plus rapide de spécifier les 238 noms de colonnes de laSELECT
liste, mais cela n'aidera pas le problème d'analyse. Mais avez-vous jamais vraiment besoin des 238 colonnes en même temps de toute façon?Bonne chance!
MISE
À JOUR Par souci d'exhaustivité de la question «comment améliorer les performances sur une grande table pour les requêtes ad hoc», il convient de noter que même si cela n'aidera pas dans ce cas spécifique, SI quelqu'un utilise SQL Server 2012 (ou plus récent à ce moment-là) et SI la table n'est pas mise à jour, l'utilisation des index Columnstore est une option. Pour plus de détails sur cette nouvelle fonctionnalité, regardez ici: http://msdn.microsoft.com/en-us/library/gg492088.aspx (je crois que ceux-ci ont été mis à jour à partir de SQL Server 2014).
MISE À JOUR 2
Les considérations supplémentaires sont:
INT
,BIGINT
,TINYINT
,SMALLINT
,CHAR
,NCHAR
,BINARY
,DATETIME
,SMALLDATETIME
,MONEY
, etc.) et plus de 50 % des lignes sontNULL
, puis envisagez d'activer l'SPARSE
option qui est devenue disponible dans SQL Server 2008. Veuillez consulter la page MSDN pour utiliser des colonnes éparses pour plus de détails.la source
*
sans qu'un douteuxIl y a quelques problèmes avec cette requête (et cela s'applique à chaque requête).
Manque d'index
Le manque d'index sur la
er101_upd_date_iso
colonne est la chose la plus importante comme Oded l' a déjà mentionné.Sans index correspondant (dont l'absence pourrait provoquer un scan de table), il n'y a aucune chance d'exécuter des requêtes rapides sur de grandes tables.
Si vous ne pouvez pas ajouter d'index (pour diverses raisons, y compris il ne sert à rien de créer un index pour une seule requête ad hoc ), je suggérerais quelques solutions de contournement (qui peuvent être utilisées pour les requêtes ad hoc):
1. Utilisez des tables temporaires
Créez une table temporaire sur un sous-ensemble (lignes et colonnes) de données qui vous intéresse. La table temporaire doit être beaucoup plus petite que la table source d'origine, peut être indexée facilement (si nécessaire) et peut mettre en cache un sous-ensemble de données qui vous intéresse.
Pour créer une table temporaire, vous pouvez utiliser du code (non testé) comme:
-- 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
Avantages:
view
.Les inconvénients:
2. Expression de table commune - CTE
Personnellement, j'utilise beaucoup CTE avec des requêtes ad hoc - cela aide beaucoup à construire (et tester) une requête pièce par pièce.
Voir l'exemple ci-dessous (la requête commençant par
WITH
).Avantages:
Les inconvénients:
3. Créer des vues
Similaire à ci-dessus, mais créez des vues au lieu de tables temporaires (si vous jouez souvent avec les mêmes requêtes et que vous avez une version MS SQL qui prend en charge les vues indexées.
Vous pouvez créer des vues ou des vues indexées sur un sous-ensemble de données qui vous intéresse et exécuter des requêtes sur la vue - qui ne devraient contenir que des sous-ensembles intéressants de données beaucoup plus petits que la table entière.
Avantages:
Les inconvénients:
Sélection de toutes les colonnes
Lancer star query (
SELECT * FROM
) sur une grande table n'est pas une bonne chose ...Si vous avez de grandes colonnes (comme de longues chaînes), il faut beaucoup de temps pour les lire à partir du disque et passer par le réseau.
J'essaierais de remplacer
*
par des noms de colonnes dont vous avez vraiment besoin.Ou, si vous avez besoin de toutes les colonnes, essayez de réécrire la requête en quelque chose comme (en utilisant une expression de données commune ):
;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
Lecture sale
La dernière chose qui pourrait accélérer la requête ad hoc est d'autoriser les lectures sales avec une indication de table
WITH (NOLOCK)
.Au lieu d'un indice, vous pouvez définir le niveau d'isolation des transactions pour lire sans engagement:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
ou définissez le paramètre SQL Management Studio approprié.
Je suppose que pour les requêtes ad hoc, les lectures sales sont suffisantes.
la source
SELECT *
- il force SQL Server à utiliser l'index cluster. Du moins, ça devrait. Je ne vois aucune raison réelle pour un index de couverture non groupé ... couvrant l'ensemble du tableau :)Vous obtenez une analyse de table là-bas, ce qui signifie que vous n'avez pas d'index défini sur
er101_upd_date_iso
, ou si cette colonne fait partie d'un index existant, l'index ne peut pas être utilisé (ce n'est peut-être pas la colonne d'indexation principale).L'ajout d'index manquants améliorera les performances sans fin.
Cela ne signifie pas qu'ils sont utilisés dans cette requête (et ils ne le sont probablement pas).
Je suggère de lire Trouver les causes des mauvaises performances dans SQL Server par Gail Shaw, partie 1 et partie 2 .
la source
er101_upd_date_iso
un énorme varchar, ou un int, cela changera considérablement les performances.La question indique spécifiquement que les performances doivent être améliorées pour les requêtes ad hoc et que les index ne peuvent pas être ajoutés. Alors, en prenant cela pour argent comptant, que peut-on faire pour améliorer les performances sur n'importe quelle table?
Puisque nous considérons des requêtes ad hoc, la clause WHERE et la clause ORDER BY peuvent contenir n'importe quelle combinaison de colonnes. Cela signifie que presque quels que soient les index placés sur la table, certaines requêtes nécessitent une analyse de table, comme indiqué ci-dessus dans le plan de requête d'une requête peu performante.
En tenant compte de cela, supposons qu'il n'y ait aucun index sur la table à l'exception d'un index cluster sur la clé primaire. Examinons maintenant les options dont nous disposons pour maximiser les performances.
Défragmenter la table
Tant que nous avons un index clusterisé, nous pouvons défragmenter la table en utilisant DBCC INDEXDEFRAG (obsolète) ou de préférence ALTER INDEX . Cela minimisera le nombre de lectures de disque nécessaires pour analyser la table et améliorera la vitesse.
Utilisez les disques les plus rapides possible. Vous ne dites pas quels disques vous utilisez mais si vous pouvez utiliser des SSD.
Optimiser tempdb. Mettez tempdb sur les disques les plus rapides possibles, encore une fois sur les SSD. Voir cet article SO et cet article RedGate .
Comme indiqué dans d'autres réponses, l'utilisation d'une requête plus sélective renverra moins de données et devrait donc être plus rapide.
Voyons maintenant ce que nous pouvons faire si nous sommes autorisés à ajouter des index.
Si nous ne parlions pas de requêtes ad hoc, nous ajouterions des index spécifiquement pour l'ensemble limité de requêtes exécutées sur la table. Puisque nous discutons des requêtes ad hoc , que peut-on faire pour améliorer la vitesse la plupart du temps?
Éditer
J'ai exécuté des tests sur une «grande» table de 22 millions de lignes. Ma table ne comporte que six colonnes mais contient 4 Go de données. Ma machine est un ordinateur de bureau respectable avec 8 Go de RAM et un processeur quad core et dispose d'un seul SSD Agility 3.
J'ai supprimé tous les index à l'exception de la clé primaire sur la colonne Id.
Une requête similaire au problème donné dans la question prend 5 secondes si le serveur SQL est redémarré en premier et 3 secondes par la suite. Le conseiller au réglage de la base de données recommande évidemment l'ajout d'un index pour améliorer cette requête, avec une amélioration estimée de> 99%. L'ajout d'un index entraîne un temps de requête de zéro.
Ce qui est également intéressant, c'est que mon plan de requête est identique au vôtre (avec l'analyse d'index clusterisé), mais l'analyse d'index représente 9% du coût de la requête et le tri les 91% restants. Je ne peux que supposer que votre table contient une énorme quantité de données et / ou que vos disques sont très lents ou situés sur une connexion réseau très lente.
la source
Même si vous avez des index sur certaines colonnes qui sont utilisés dans certaines requêtes, le fait que votre requête «ad hoc» provoque une analyse de table montre que vous ne disposez pas d'index suffisants pour permettre à cette requête de se terminer efficacement.
Pour les plages de dates en particulier, il est difficile d'ajouter de bons index.
En regardant simplement votre requête, la base de données doit trier tous les enregistrements par la colonne sélectionnée pour pouvoir renvoyer les n premiers enregistrements.
La base de données effectue-t-elle également une analyse complète de la table sans la clause order by? La table a-t-elle une clé primaire - sans PK, la base de données devra travailler plus dur pour effectuer le tri?
la source
select top 100 * from ER101_ACCT_ORDER_DTL
Un index est un arbre B où chaque nœud feuille pointe vers un «groupe de lignes» (appelé «Page» dans la terminologie interne SQL), c'est-à-dire lorsque l'index est un index non clusterisé.
L'index clusterisé est un cas particulier, dans lequel les nœuds feuilles ont le «groupe de lignes» (plutôt que de pointer vers eux). c'est pourquoi...
1) Il ne peut y avoir qu'un seul index clusterisé sur la table.
cela signifie également que toute la table est stockée en tant qu'index clusterisé, c'est pourquoi vous avez commencé à voir une analyse d'index plutôt qu'une analyse de table.
2) Une opération qui utilise un index clusterisé est généralement plus rapide qu'un index non clusterisé
En savoir plus sur http://msdn.microsoft.com/en-us/library/ms177443.aspx
Pour le problème que vous rencontrez, vous devriez vraiment envisager d'ajouter cette colonne à un index, comme vous l'avez dit, l'ajout d'un nouvel index (ou d'une colonne à un index existant) augmente les coûts INSERT / UPDATE. Mais il peut être possible de supprimer un index sous-utilisé (ou une colonne d'un index existant) pour le remplacer par «er101_upd_date_iso».
Si les changements d'index ne sont pas possibles, je recommande d'ajouter une statistique sur la colonne, cela peut fixer les choses lorsque les colonnes ont une certaine corrélation avec les colonnes indexées
http://msdn.microsoft.com/en-us/library/ms188038.aspx
BTW, Vous obtiendrez beaucoup plus d'aide si vous pouvez publier le schéma de table de ER101_ACCT_ORDER_DTL. et les index existants aussi ..., la requête pourrait probablement être réécrite pour en utiliser certains.
la source
L'une des raisons pour lesquelles votre test 1M s'est exécuté plus rapidement est probablement parce que les tables temporaires sont entièrement en mémoire et n'iront sur le disque que si votre serveur subit une pression de mémoire. Vous pouvez soit recréer votre requête pour supprimer l'ordre par, ajouter un bon index clusterisé et des index couvrant comme mentionné précédemment, ou interroger le DMV pour vérifier la pression d'E / S pour voir si le matériel est lié.
-- 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
la source
Je sais que vous avez dit que l'ajout d'index n'est pas une option, mais que ce serait la seule option pour éliminer l'analyse de table que vous avez. Lorsque vous effectuez une analyse, SQL Server lit les 2 millions de lignes de la table pour répondre à votre requête.
cet article fournit plus d'informations mais rappelez-vous: Seek = good, Scan = bad.
Deuxièmement, ne pouvez-vous pas éliminer le select * et ne sélectionner que les colonnes dont vous avez besoin? Troisièmement, pas de clause «où»? Même si vous avez un index, puisque vous lisez tout, le mieux que vous obtiendrez est un balayage d'index (ce qui est mieux qu'un balayage de table, mais ce n'est pas une recherche, ce que vous devriez viser)
la source
Je sais que cela fait pas mal de temps depuis le début ... Il y a beaucoup de sagesse dans toutes ces réponses. Une bonne indexation est la première chose à faire lorsque vous essayez d'améliorer une requête. Eh bien, presque le premier. Le plus important (pour ainsi dire) consiste à apporter des modifications au code pour qu'il soit efficace. Donc, après que tout a été dit et fait, si l'on a une requête sans WHERE, ou lorsque la condition WHERE n'est pas assez sélective, il n'y a qu'une seule façon d'obtenir les données: TABLE SCAN (INDEX SCAN). Si l'on a besoin de toutes les colonnes d'une table, alors TABLE SCAN sera utilisé - pas de doute à ce sujet. Il peut s'agir d'une analyse de tas ou d'une analyse d'index cluster, selon le type d'organisation des données. La seule dernière façon d'accélérer les choses (si possible) est de s'assurer que le plus grand nombre possible de cœurs est utilisé pour effectuer l'analyse: OPTION (MAXDOP 0). J'ignore le sujet du stockage, bien sûr,
la source