web-dev-qa-db-fra.com

Améliorez les performances des requêtes SQL Server sur les grandes tables

J'ai un tableau relativement volumineux (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 étant la clé ici. L'ajout d'index n'est pas une option (il existe déjà des index sur les colonnes qui sont le plus souvent interrogés).

Exécuter 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:

enter image description here

Détails supplémentaires de l'analyse de la table:

enter image description here

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

Le serveur est assez puissant (mémoire vive de 48 Go, processeur 24 cœurs) exécutant SQL Server 2008 R2 x64.

Mettre à jour

J'ai trouvé ce code pour créer une table avec 1 000 000 enregistrements. Je pensais pouvoir alors exécuter SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESC sur plusieurs serveurs différents pour savoir si la vitesse d'accès à mon disque était médiocre 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’exécutait presque instantanément. Quelqu'un peut-il expliquer cela?

enter image description here

Mise à jour 2

Merci pour les commentaires - continuez de les envoyer s'il vous plaît ... ils m'ont amené à essayer de changer l'index de clé primaire de non-cluster à cluster avec des résultats plutôt intéressants (et inattendus?).

Non clusterisé:

enter image description here

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

Clustered:

enter image description here

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

Comment est-ce possible? Sans index sur la colonne er101_upd_date_iso, comment utiliser une analyse d'index en cluster?

Mise à jour 3

Comme demandé, c'est 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 taille du tableau est de 2,8 Go, la taille de l'index étant de 3,9 Go.

80
Lee Tickett

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 dit dans d’autres réponses sur ce sujet, la conception d’index est à la fois un art et une science et il ya tellement de facteurs à prendre en compte qu’il existe peu de règles strictes, voire aucune. Vous devez prendre en compte le volume d'opérations DML par rapport aux SELECT, le sous-système de disque, les autres index/déclencheurs de la table, la distribution des données dans la table, les requêtes utilisant les conditions SARGable WHERE et plusieurs autres choses dont je ne me souviens même pas bien. à présent.

Je peux dire que les questions sur ce sujet ne peuvent être résolues sans une compréhension de la table elle-même, de ses index, de ses déclencheurs, etc. Maintenant que vous avez posté la définition de la table (vous attendez toujours les index mais la définition de la table à elle seule pointe vers 99% de la question) je peux offrir quelques suggestions.

Premièrement, si la définition de la table est précise (238 colonnes, facteur de remplissage de 50%), vous pouvez alors pratiquement ignorer le reste des réponses/conseils ici ;-). Désolé d'être moins que politique ici, mais sérieusement, c'est une poursuite de 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 prend autant de temps, même lorsque les requêtes de test (mise à jour n ° 1) ont été exécutées aussi rapidement.

Le problème principal ici (et dans de nombreuses situations de performances médiocres) 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:

  1. Premièrement, cette table doit vraiment être remodelée. S'il s'agit d'une table d'entrepôt de données, alors peut-être, mais sinon, ces champs doivent vraiment être divisés en plusieurs tables pouvant toutes avoir le même PK. Vous auriez une table d'enregistrement principale et les tables enfants ne sont que des informations dépendantes basées sur des attributs généralement associés. La clé de ces tables est identique à celle de la table principale et donc également à la clé principale. Il y aura une relation un à un entre le maître et toutes les tables enfants.
  2. L'utilisation de ANSI_PADDING OFF est dérangeante, sans parler des incohérences dans la table en raison des divers ajouts de colonnes au fil du temps. Vous ne savez pas si vous pouvez résoudre ce problème maintenant, mais idéalement, vous devriez toujours avoir ANSI_PADDING ON, ou tout au moins le même paramètre dans toutes les instructions ALTER TABLE.
  3. Envisagez de créer 2 groupes de fichiers supplémentaires: les tables et les index. Il est préférable de ne pas placer vos données dans 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 cluster (car il s'agit des données de la table) sur [Tables] et tous les index non clusterisés sur [Indexes].
  4. Augmenter le facteur de remplissage de 50%. Ce faible nombre explique probablement pourquoi votre espace d'indexation est supérieur à votre espace de données. Effectuer une reconstruction d'index va recréer les pages de données avec un maximum de 4 ko (sur une taille totale de page de 8 ko) utilisées pour vos données afin que votre table soit répartie sur une zone étendue.
  5. Si la plupart des requêtes ou toutes ont "ER101_ORG_CODE" dans la condition WHERE, envisagez de le déplacer vers la colonne de tête de l'index en cluster. En supposant qu'il soit utilisé plus souvent que "ER101_ORD_NBR". Si "ER101_ORD_NBR" est utilisé plus souvent, conservez-le. Il semble simplement, en supposant que les noms de champ signifient "CodeOrganisation" et "NuméroCommande", que "CodeOrg" soit un meilleur groupement pouvant contenir plusieurs "NuméroCommande".
  6. Point mineur, mais si "ER101_ORG_CODE" est toujours composé de 2 caractères, utilisez CHAR(2) au lieu de VARCHAR(2), car il enregistre un octet dans l’en-tête de la ligne qui suit les tailles de largeur variable et s’ajoute à des millions de lignes.
  7. Comme d'autres l'ont déjà mentionné, l'utilisation de SELECT * nuira aux performances. Non seulement parce que SQL Server doit renvoyer toutes les colonnes et donc être plus susceptible de faire une analyse d'index en cluster, quels que soient vos autres index, il faut également du temps à SQL Server pour accéder à la définition de la table et traduire * en tous les noms de colonne. Il devrait être légèrement ​​plus rapide de spécifier tous les 238 noms de colonnes dans la liste SELECT bien que cela n'aidera pas le problème de Scan. Mais avez-vous toujours besoin des 238 colonnes en même temps?

Bonne chance!

UPDATE
Par souci d’exhaustivité à la question "Comment améliorer les performances sur une table volumineuse pour des requêtes ad-hoc", il convient de noter que cela n’aidera en rien ce cas particulier, SI quelqu'un utilise SQL Server 2012 (ou plus tard si cela arrive) et SI la table n'est pas mise à jour, alors l'utilisation de Columnstore Indexes est une option. Pour plus de détails sur cette nouvelle fonctionnalité, regardez ici: http://msdn.Microsoft.com/en-us/library/gg492088.aspx (Je pense que ces modifications ont été créées pour pouvoir être mises à jour à partir de SQL Server. 2014).

UPDATE 2
Autres considérations à prendre en compte:

  • Activer la compression sur l'index clusterisé. Cette option est devenue disponible dans SQL Server 2008, mais en tant que fonctionnalité destinée à Enterprise Edition uniquement. Cependant, à partir de SQL Server 2016 SP1 , la compression des données était disponible dans toutes les éditions ! Veuillez consulter la page MSDN pour Compression de données pour plus de détails sur la compression de lignes et de pages.
  • Si vous ne pouvez pas utiliser la compression de données ou si cela ne présente pas un grand avantage pour une table particulière, alors SI vous avez une colonne de type à longueur fixe (INT, BIGINT, TINYINT, SMALLINT, CHAR, NCHAR, BINARY, DATETIME, SMALLDATETIME, MONEY, etc) et bien plus de 50 % des lignes sont NULL, puis envisagez d'activer l'option SPARSE qui est devenue disponible dans SQL Server 2008. Veuillez consulter la page MSDN pour tiliser des colonnes éparpillées pour plus de détails.
53
Solomon Rutzky

Il y a quelques problèmes avec cette requête (et cela s'applique à chaque requête).

Manque d'index

L'absence d'index sur la colonne er101_upd_date_iso est la chose la plus importante car Oded a déjà été mentionné.

Sans la correspondance d'index (ce qui pourrait causer une analyse 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, dont , il est inutile de créer un index pour une seule requête ad-hoc). Je suggérerais donc quelques solutions de contournement (pouvant être utilisées pour des 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éressent ..__ La table temporaire doit être beaucoup plus petite que la table source originale, peut être indexée facilement (si nécessaire) et peut cached sous-ensemble de données qui tu es intéressé par.

Pour créer une table temporaire, vous pouvez utiliser un 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:

  • Facile à faire pour tout sous-ensemble de données.
  • Facile à gérer - c'est temporaire et table.
  • N'affecte pas les performances globales du système comme view.
  • La table temporaire peut être indexée.
  • Vous ne devez pas vous en soucier, c'est temporaire :).

Les inconvénients:

  • C'est un instantané des données - mais c'est probablement suffisant pour la plupart des requêtes ad hoc.

2. Expression de table commune - CTE

Personnellement, j'utilise CTE beaucoup 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:

  • Facile à construire à partir de big view, puis en sélectionnant et filtrant ce dont vous avez réellement besoin.
  • Facile à tester.

Les inconvénients:

  • Certaines personnes n'aiment pas CDE - les requêtes CDE semblent longues et difficiles à comprendre.

3. Créer des vues

Semblable à ce qui précède, mais créez des vues au lieu de tables temporaires (si vous jouez souvent avec les mêmes requêtes et que vous avez la 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 une vue - qui ne devrait contenir qu'un sous-ensemble de données intéressant, beaucoup plus petit que la table entière.

Avantages:

  • Facile à faire.
  • C'est à jour avec les données source.

Les inconvénients:

  • Possible uniquement pour un sous-ensemble défini de données.
  • Pourrait être inefficace pour les grandes tables avec un taux élevé de mises à jour.
  • Pas si facile à gérer.
  • Peut affecter les performances globales du système.
  • Je ne suis pas sûr que les vues indexées soient disponibles dans toutes les versions de MS SQL.

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 (telles que des chaînes longues), il faut beaucoup de temps pour les lire à partir du disque et les 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 sur quelque chose comme (en utilisant expression commune de données):

;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 

Lectures sales

La dernière chose qui pourrait accélérer la requête ad hoc est d’autoriser les lectures dirty avec indice de table WITH (NOLOCK) .

Au lieu d’un indice, vous pouvez définir le niveau d’isolation de la transaction 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, dirty reads est suffisant.

50
Grzegorz Gierlik

Vous obtenez un analyse de table} ici, 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 principale de l'indexeur).

L'ajout d'index manquants aidera les performances sans fin.

il y a déjà des index sur les colonnes qui sont le plus souvent interrogées

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 Recherche des causes des performances médiocres dans SQL Server par Gail Shaw, partie 1 et partie 2 .

12
Oded

La question précise que les performances doivent être améliorées pour les requêtes ad-hoc et que les index ne peuvent pas être ajoutés. Donc, à première vue, que peut-on faire pour améliorer les performances d'une table?

Puisque nous examinons des requêtes ad hoc, les clauses WHERE et ORDER BY peuvent contenir n’importe quelle combinaison de colonnes. Cela signifie que, indépendamment des index placés sur la table, certaines requêtes nécessiteront une analyse de la table, comme indiqué ci-dessus dans le plan de requête d'une requête peu performante.

En prenant cela en compte, supposons qu’il n’y ait aucun index sur la table, à part un index clusterisé sur la clé primaire. Examinons maintenant quelles options nous avons pour optimiser 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 sur disque nécessaires pour analyser la table et améliorera la vitesse.

  • Utilisez les disques les plus rapides possibles. Vous ne dites pas quels disques vous utilisez, mais si vous pouvez utiliser des disques SSD.

  • Optimiser tempdb. Placez tempdb sur les disques les plus rapides possibles, à nouveau les SSD. Voir ceci article SO et ceci article de 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.

Examinons 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 en cours d'exécution sur la table. Puisque nous discutons de requêtes ad-hoc , que peut-on faire pour améliorer la vitesse la plupart du temps?

  • Ajoutez un index de colonne unique à chaque colonne. Cela devrait donner à SQL Server au moins un élément de travail pour améliorer la vitesse de la majorité des requêtes, mais ne sera pas optimal.
  • Ajoutez des index spécifiques pour les requêtes les plus courantes afin qu'elles soient optimisées.
  • Ajoutez des index spécifiques supplémentaires, comme requis par la surveillance des requêtes peu performantes.

Modifier

J'ai effectué 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 bureau respectable avec 8 Go RAM, un processeur quad core et un seul SSD Agility 3.

J'ai supprimé tous les index en dehors de la clé primaire de la colonne Id.

Une requête similaire à celle indiquée dans la question prend 5 secondes si le serveur SQL est redémarré en premier et 3 secondes plus tard. Le conseiller d'optimisation 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 à> 99%. L'ajout d'un index entraîne un temps de requête égal à 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 en cluster), mais l'analyse d'index représente 9% du coût de la requête et le tri des 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.

7
Phil

Comment est-ce possible? Sans index sur la colonne er101_upd_date_iso, comment utiliser une analyse d'index en cluster?

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 terminaux ont le "groupe de lignes" (plutôt que de les pointer). c'est pourquoi...

1) Il ne peut y avoir qu'un seul index clusterisé sur la table. 

cela signifie également que la table entière est stockée en tant qu'index cluster, c'est pourquoi vous avez commencé à voir une analyse d'index plutôt qu'une analyse de table.

2) Une opération utilisant 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

En fonction de votre problème, vous devriez vraiment envisager d’ajouter cette colonne à un index, car vous avez dit que l’ajout d’un nouvel index (ou d’une colonne à un index existant) augmentait les coûts INSERT/UPDATE. Mais il pourrait ê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 modifications d'index ne sont pas possibles, je recommande d'ajouter une statistique sur la colonne, cela peut accélérer 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 aurez beaucoup plus d’aide si vous pouvez publier le schéma de table de ER101_ACCT_ORDER_DTL . Ainsi que les index existants aussi ..., la requête pourrait probablement être réécrite pour en utiliser quelques-uns.

2
shankar_pratap

Même si vous avez des index sur certaines colonnes utilisées dans certaines requêtes, le fait que votre requête "ad-hoc" provoque une analyse de table montre que vous ne disposez pas d'un nombre suffisant d'index 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 de 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 fort pour effectuer le tri?

2
foamdino

Une des raisons pour lesquelles votre test 1M a fonctionné plus rapidement est probablement due au fait que les tables temporaires sont entièrement en mémoire et ne sont enregistrées sur disque que si votre serveur est soumis à une pression mémoire. Vous pouvez reformuler votre requête pour supprimer la commande en ajoutant un bon index clusterisé et un ou plusieurs index couvrant comme indiqué précédemment, ou interroger le DMV pour vérifier la pression de IO afin de déterminer 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
1
ninghad

Je sais que ça fait un bon bout 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 pour améliorer une requête. Eh bien, presque le premier. La première chose à faire (pour ainsi dire) consiste à modifier le code pour qu'il soit efficace. Donc, une fois que tout est dit et fait, si on a une requête sans WHERE, ou si la condition WHERE n'est pas assez sélective, il n'y a qu'un seul moyen d'obtenir les données: TABLE SCAN (INDEX SCAN). Si vous avez besoin de toutes les colonnes d'une table, alors TABLE SCAN sera utilisé - aucune question à ce sujet. Cela peut être une analyse de tas ou une analyse d'index en cluster, selon le type d'organisation des données. Le dernier moyen d'accélérer les choses (dans la mesure du possible) est de s'assurer que le plus grand nombre de cœurs possible est utilisé pour effectuer l'analyse: OPTION (MAXDOP 0). J'ignore le sujet du stockage, bien sûr, mais vous devez vous assurer que vous avez une mémoire RAM illimitée, ce qui va sans dire :)

0
darlove

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. 

this article fournit plus d'informations mais rappelez-vous: Seek = good, Scan = bad.

Deuxièmement, ne pouvez-vous pas éliminer les éléments select * et ne sélectionner que les colonnes dont vous avez besoin? Troisièmement, aucune clause "où"? Même si vous avez un index, puisque vous lisez tout, le mieux que vous obtiendrez est un scan d'index (ce qui est meilleur qu'un scan de table, mais ce n'est pas une recherche, c'est ce que vous devriez viser)

0
Diego