Büyük tablolarda SQL Server sorgu performansını iyileştirin


85

Nispeten büyük bir tablom var (şu anda 2 milyon kayıt) ve anlık sorgular için performansı iyileştirmenin mümkün olup olmadığını bilmek istiyorum. Burada geçici kelime anahtar kelimedir . Dizin eklemek bir seçenek değildir (halihazırda en sık sorgulanan sütunlarda dizinler vardır).

En son güncellenen 100 kaydı döndürmek için basit bir sorgu çalıştırma:

select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc

Birkaç dakika sürer. Aşağıdaki yürütme planına bakın:

görüntü açıklamasını buraya girin

Tablo taramasından ek ayrıntı:

görüntü açıklamasını buraya girin

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

Sunucu oldukça güçlüdür (bellek 48GB ram, 24 çekirdekli işlemci), sql server 2008 r2 x64 çalıştırır.

Güncelleme

Bu kodu 1.000.000 kayıt içeren bir tablo oluşturmak için buldum. Daha sonra SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESCdisk erişim hızımın sunucuda zayıf olup olmadığını anlamak için birkaç farklı sunucuda çalışabileceğimi düşündüm .

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;

Ancak üç test sunucusunda sorgu neredeyse anında çalıştı. Bunu kimse açıklayabilir mi?

görüntü açıklamasını buraya girin

Güncelleme 2

Yorumlar için teşekkür ederim - lütfen gelmeye devam edin ... birincil anahtar dizinini kümelenmemiş durumdan oldukça ilginç (ve beklenmedik?) Sonuçlarla kümelenmiş olarak değiştirmeyi denemeye yönlendirdiler.

Kümelenmemiş:

görüntü açıklamasını buraya girin

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

Kümelenmiş:

görüntü açıklamasını buraya girin

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

Bu nasıl mümkün olabilir? Er101_upd_date_iso sütununda bir dizin olmadan kümelenmiş bir dizin taraması nasıl kullanılabilir?

Güncelleme 3

İstendiği gibi - bu tablo oluşturma komut dosyasıdır:

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]

Tablo 2,8 GB boyutundadır ve dizin boyutu 3,9 GB'dir.


1
Fare imlecini plan öğesinin üzerine getirdiğinizde küçük ipuçları vardır. Tahmini G / Ç ve CPU maliyetini gösterirler. İlk başta I / O maliyetini dikkate alırdım.
Grzegorz Gierlik

4
Table Scanbir yığın belirtir (kümelenmiş dizin yok) - bu nedenle ilk adım , tablonuza iyi, hızlı kümelenmiş bir dizin eklemek olacaktır . İkinci adım, kümelenmemiş bir dizinin er101_upd_date_isoyardımcı olup olmayacağını (ve diğer performans dezavantajlarına neden olmayacağını) araştırmak olabilir
marc_s

1
@marc_s bunun için teşekkürler - pk endeksini kümelenmiş olarak değiştirdim ve önemli bir fark yarattı - bunu biraz daha açıklar mısınız? (2. güncellemeye bakın)
Lee Tickett

2
Kümelenmiş dizin sadece tablonun depolama düzenini değiştirir. Kümelenmiş dizin, yaprak seviyesi düğümlerinde gerçek tablo verilerini içerir - yani: tüm tabloyu okumak için, SQL Server şu anda kümelenmiş bir dizin taraması yapıyor (temelde kümelenmiş bir dizine sahip bir tablo üzerinde bir "tablo taraması"). Bu neredeyse her zaman bir yığın üzerinde (kümelenmiş indeks olmadan) tablo taraması yapmaktan biraz daha hızlı olacaktır. Şimdi er101_upd_date_isosütuna kümelenmemiş bir dizin eklediyseniz , muhtemelen yürütme planınızdaki "Sırala" işleminden kurtulabilir ve işleri daha da hızlandırabilirsiniz
marc_s

2
@LeeTickett, lütfen Tablo ve Dizin tanımlarınızı gösterin. Dikkate alınması gereken birçok faktör var ve kimse onları istemiyor gibi görünüyor (bu beni şaşırtıyor ama belki de olmamalı). 2 milyon satırın büyük OLMADIĞINI ve 200 milyondan fazla satırlık düzgün indekslenmiş tabloların bundan daha hızlı döndüğünü söyleyebilirim. Büyük olasılıkla kümelenmiş indeks (artık marc_s sayesinde bir tane olduğuna göre) kötü bir seçimdir, ancak ayrıntıları görmeden söylemek zor. Bölümlemeyi KULLANMAYIN, ancak SET STATISTICS IO ON KULLANMAYIN ve mesajlar sekmesinde Mantıksal Okumaları kontrol edin. Bir değişiklik Mantıksal Okumaları azaltırsa, yaklaşıyorsunuz demektir.
Solomon Rutzky

Yanıtlar:


59

Basit Cevap: HAYIR. Kümelenmiş Dizinde% 50 Doldurma Faktörü ile 238 sütunlu bir tablodaki ad hoc sorgulara yardımcı olamazsınız.

Ayrıntılı Cevap:

Bu konudaki diğer cevaplarda da belirttiğim gibi, İndeks tasarımı hem Sanat hem de Bilimdir ve göz önünde bulundurulması gereken o kadar çok faktör vardır ki, varsa çok az katı ve hızlı kurallar vardır. Şunları göz önünde bulundurmanız gerekir: DML işlemlerinin hacmi - SELECT'ler, disk alt sistemi, tablodaki diğer indeksler / tetikleyiciler, tablo içindeki verilerin dağılımı, SARGable WHERE koşullarını kullanan sorgulardır ve hatta doğru hatırlayamadığım diğer birkaç şeydir. şimdi.

Tablonun kendisi, indeksleri, tetikleyicileri vb. Anlaşılmadan bu konudaki sorulara yardım edilemeyeceğini söyleyebilirim. Artık tablo tanımını yayınladınız (hala Endeksleri bekliyorsunuz ama Tablo tanımı tek başına şunu gösteriyor: Sorunun% 99'u) Bazı önerilerde bulunabilirim.

İlk olarak, tablo tanımı doğruysa (238 sütun,% 50 Doldurma Faktörü), o zaman buradaki cevapların / tavsiyelerin geri kalanını hemen hemen yok sayabilirsiniz ;-). Burada politik olmaktan uzak olduğum için üzgünüm, ama cidden, ayrıntıları bilmeden vahşi bir kaz avı. Ve şimdi tablo tanımını gördüğümüze göre, test sorguları (Güncelleme # 1) bu kadar hızlı çalıştırıldığında bile, basit bir sorgunun neden bu kadar uzun sürdüğü konusunda biraz daha netleşiyor.

Buradaki (ve birçok düşük performans durumunda) temel sorun, kötü veri modellemedir. 999 dizine sahip olmak yasak olmadığı için 238 sütun yasaklanmış değildir, ancak genellikle çok da akıllıca değildir.

Öneriler:

  1. İlk olarak, bu tablonun gerçekten yeniden modellenmesi gerekiyor. Bu bir veri ambarı tablosuysa, o zaman belki, ancak değilse, o zaman bu alanların gerçekten aynı PK'ye sahip olabilecek birkaç tabloya bölünmesi gerekir. Bir ana kayıt tablonuz olur ve alt tablolar yalnızca genel olarak ilişkilendirilmiş özniteliklere dayalı bağımlı bilgilerdir ve bu tabloların PK'si ana tablonun PK'siyle aynıdır ve dolayısıyla ana tabloya da FK. Ana ve tüm alt tablolar arasında 1'e 1 ilişki olacaktır.
  2. Zaman ANSI_PADDING OFFiçinde çeşitli sütun eklemelerinden dolayı tablo içinde tutarsızlıktan bahsetmiyorum bile, kullanımı rahatsız edicidir. Bunu şimdi düzeltip düzeltemeyeceğinizden emin değilim, ancak ideal olarak her zaman ANSI_PADDING ONveya en azından hepsinde aynı ayara sahip olursunuz.ALTER TABLE ifadelerde .
  3. 2 ek Dosya Grubu oluşturmayı düşünün: Tablolar ve Dizinler. Öğelerinizi, PRIMARYSQL SUNUCUSU'nun tüm verilerini ve nesnelerinizle ilgili meta verileri depoladığı yere koymamak en iyisidir . Tablonuzu ve Kümelenmiş Dizininizi (tablonun verileri olduğu gibi) [Tables]ve tüm Kümelenmemiş dizinler üzerinde oluşturursunuz.[Indexes]
  4. Dolgu Faktörünü% 50'den artırın. Bu düşük sayı muhtemelen dizin alanınızın veri alanınızdan daha büyük olmasının nedenidir. Bir Dizin Yeniden Oluşturma işlemi yapmak, verileriniz için kullanılan maksimum 4k (toplam 8k sayfa boyutundan) veri sayfalarını yeniden oluşturarak tablonuzun geniş bir alana yayılmasını sağlar.
  5. Çoğu veya tüm sorguların koşulda "ER101_ORG_CODE" varsa WHERE, bunu kümelenmiş dizinin baştaki sütununa taşımayı düşünün. "ER101_ORD_NBR" den daha sık kullanıldığını varsayarsak. "ER101_ORD_NBR" daha sık kullanılıyorsa saklayın. Görünüşe göre, alan adlarının "OrganizasyonKodu" ve "SiparişNumarası" anlamına geldiğini varsayarsak, "OrgCode", içinde birden fazla "Sipariş Numarası" olabilecek daha iyi bir gruplama olabilir.
  6. Önemsiz bir nokta, ancak "ER101_ORG_CODE" her zaman 2 karakter ise, CHAR(2)yerine kullanınVARCHAR(2) değişken genişlik boyutlarını izleyen ve milyonlarca satırı satır başlığına bir bayt kaydedeceği .
  7. Diğerlerinin de bahsettiği gibi, kullanmak SELECT *performansa zarar verir. Yalnızca SQL Server'ın tüm sütunları döndürmesini gerektirmesi ve dolayısıyla diğer dizinlerinizden bağımsız olarak Kümelenmiş Dizin Taraması yapma olasılığından dolayı değil, aynı zamanda SQL Server'ın tablo tanımına gitmesi ve *tüm sütun adlarına çevirmesi zaman alır. . Listedeki 238 sütun adının tümünü belirtmek biraz daha hızlı olmalı , SELECTancak bu Tarama sorununa yardımcı olmayacaktır. Ama 238 sütunun hepsine aynı anda gerçekten ihtiyacınız var mı?

İyi şanslar!

GÜNCELLEME
"Geçici sorgular için büyük bir tabloda performans nasıl artırılır" sorusunun eksiksiz olması adına, bu özel durum için yardımcı olmayacak olsa da, eğer birisi SQL Server 2012 (veya daha yeni) kullanıyorsa o zaman geldiğinde) ve tablo güncellenmiyorsa, Columnstore Indexes kullanmak bir seçenektir. Bu yeni özellikle ilgili daha fazla ayrıntı için buraya bakın: http://msdn.microsoft.com/en-us/library/gg492088.aspx (Bunların SQL Server 2014'ten itibaren güncellenebilir hale getirildiğine inanıyorum).

GÜNCELLEME 2
Ek hususlar şunlardır:

  • Clustered Index üzerinde sıkıştırmayı etkinleştirin. Bu seçenek SQL Server 2008'de kullanılabilir hale geldi, ancak yalnızca Enterprise Edition özelliği olarak. Ancak, SQL Server 2016 SP1'den itibaren Veri Sıkıştırma tüm sürümlerde kullanıma sunulmuştur ! Satır ve Sayfa Sıkıştırma ile ilgili ayrıntılar için Veri Sıkıştırma için MSDN sayfasına bakın .
  • Veri Sıkıştırma kullanamıyorsanız, ya da sabit bir uzunlukta türünde bir sütun var EĞER sonra, belirli bir tablo için çok yarar sağlayabilir (etmezse INT, BIGINT, TINYINT, SMALLINT, CHAR, NCHAR, BINARY, DATETIME, SMALLDATETIME, MONEY, vs) ve 50'nin Satırların% 'si NULL, daha sonra SPARSESQL Server 2008'de kullanıma sunulan seçeneği etkinleştirmeyi düşünün . Ayrıntılar için Seyrek Sütunlar Kullanın MSDN sayfasına bakın.

Noktasında bizzat 7 günü hepsi exist.There karşı yeterince güçlü argümanlar olduğundan emin olmak için daha sonra yine meta kontrol etmek var o sorgu metni ayrıştırması daha meta verilerinden bunları 238 sütun isimlerini eklemek için daha hızlı olması gerektiğini hayal ve *o şüpheli biri olmadan
Martin Smith

53

Bu sorguda birkaç sorun vardır (ve bu her sorgu için geçerlidir).

Dizin eksikliği

Oded'in daha önce de bahsettiği er101_upd_date_isogibi , sütun üzerinde dizin eksikliği en önemli şeydir .

Eşleşen indeks olmadan (eksikliği tablo taramasına neden olabilir) büyük tablolarda hızlı sorgular çalıştırma şansı yoktur.

Dizin ekleyemiyorsanız (çeşitli nedenlerden dolayı , yalnızca bir anlık sorgu için dizin oluşturmanın bir anlamı yoktur) ) birkaç geçici çözüm öneririm (anlık sorgular için kullanılabilir):

1. Geçici tablolar kullanın

İlgilendiğiniz verilerin alt kümesinde (satırlar ve sütunlar) geçici tablo oluşturun. Geçici tablo, orijinal kaynak tablodan çok daha küçük olmalıdır, kolayca indekslenebilir (gerekirse) ve ilgilendiğiniz verilerin alt kümesini önbelleğe alabilir .

Geçici tablo oluşturmak için aşağıdaki gibi kodu (test edilmemiş) kullanabilirsiniz:

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

Artıları:

  • Herhangi bir veri alt kümesi için yapmak kolaydır.
  • Yönetimi kolay - geçicidir ve masadır .
  • Gibi genel sistem performansını etkilemez view.
  • Geçici tablo indekslenebilir.
  • Umursamanıza gerek yok - geçici :).

Eksileri:

  • Bu, verilerin anlık görüntüsüdür - ancak muhtemelen bu, çoğu anlık sorgu için yeterince iyidir.

2. Ortak tablo ifadesi - CTE

Şahsen CTE'yi anlık sorgularda çok kullanıyorum - bu, bir sorguyu parça parça oluşturmaya (ve test etmeye) çok yardımcı oluyor.

Aşağıdaki örneğe bakın (ile başlayan sorgu WITH).

Artıları:

  • Büyük görünümden başlayarak ve ardından gerçekten ihtiyacınız olanı seçip filtreleyerek oluşturmak kolaydır .
  • Test etmesi kolay.

Eksileri:

  • Bazı insanlar CDE - CDE sorgularından hoşlanmazlar ve anlaşılması zor görünür.

3. Görünümler oluşturun

Yukarıdakine benzer, ancak geçici tablolar yerine görünümler oluşturun (aynı sorgularla sık sık oynuyorsanız ve dizinlenmiş görünümleri destekleyen MS SQL sürümünüz varsa.

İlgilendiğiniz veri alt kümesinde görünümler veya dizine alınmış görünümler oluşturabilir ve tüm tablodan çok daha küçük yalnızca ilginç veri alt kümesini içermesi gereken görünümde sorgular çalıştırabilirsiniz.

Artıları:

  • Yapması kolay.
  • Kaynak verilerle güncel.

Eksileri:

  • Yalnızca tanımlanmış veri alt kümesi için mümkündür.
  • Yüksek güncelleme oranına sahip büyük tablolar için verimsiz olabilir.
  • Yönetmesi o kadar kolay değil.
  • Genel sistem performansını etkileyebilir.
  • MS SQL'in her sürümünde indekslenmiş görünümlerin mevcut olduğundan emin değilim.

Tüm sütunların seçilmesi

Koşu yıldız sorgusu ( SELECT * FROMbüyük masada) iyi bir şey değil ...

Büyük sütunlarınız varsa (uzun dizeler gibi), bunları diskten okumak ve ağdan geçmek çok zaman alır.

*Gerçekten ihtiyacınız olan sütun adlarıyla değiştirmeye çalışırdım.

Veya, tüm sütunlara ihtiyacınız varsa, sorguyu aşağıdaki gibi yeniden yazmaya çalışın ( ortak veri ifadesini kullanarak ):

;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 

Kirli okumalar

Geçici sorguyu hızlandırabilecek son şey , tablo ipucu ile kirli okumalara izin vermektirWITH (NOLOCK) .

İpucu yerine, işlem izolasyon seviyesini başlatılmamış olarak okuyacak şekilde ayarlayabilirsiniz :

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

veya uygun SQL Management Studio ayarını ayarlayın.

Geçici sorgular için kirli okumaların yeterince iyi olduğunu varsayıyorum .


2
+1 için SELECT *- SQL Server'ı kümelenmiş dizini kullanmaya zorlar. En azından öyle olmalı. Kümelenmemiş bir örtme indeksi için gerçek bir neden göremiyorum ... tüm tabloyu kapsayan :)
ta.speot.is

4
Bu yanıt, "geçici sorgular için performansı artırmak mümkün mü" sorusu DEĞİL, yalnızca örnek sorgunun hızını artırmaya yöneliktir
Phil

CDE artık CTE (Common Table Expression)
sqluser

12

Orada bir tablo taraması alıyorsunuz , yani üzerinde tanımlanmış bir dizine sahip değilsinizer101_upd_date_iso veya bu sütun mevcut bir dizinin parçasıysa, dizin kullanılamaz (muhtemelen birincil dizinleyici sütun değildir).

Eksik dizinleri eklemek, performansın sonu gelmesine yardımcı olmaz.

en sık sorgulanan sütunlarda halihazırda dizinler var

Bu, bu sorguda kullanıldıkları anlamına gelmez (ve muhtemelen kullanılmazlar).

Gail Shaw tarafından yazılan SQL Server'da Yetersiz Performansın Nedenlerini Bulma, bölüm 1 ve bölüm 2'yi okumanızı öneririm .



1
@LeeTickett - Yine de, bu sorgunun performansını artırmak için üzerine dizin ekleyebileceğiniz tek sütun budur .
Oded

2
Optimize edilmiş indekslenmemiş aramalar diye bir şey yoktur. Ya bir dizin ya da tam bir tablo taraması kullanıyorsunuz. Tam tablo taramaları istemiyorsanız, dizinlere ihtiyacınız var. Kullanım profilinize bağlı olarak, yalnızca dizin eklemek yeterince ucuz olabilir. Oh, ve sütunun veri türü de önemlidir. Eğer sizin er101_upd_date_isobüyük bir varchar veya int ise, performansı önemli ölçüde değiştirecektir.
Cylindric

Teşekkürler. soruya bir yorum ekledim. Sorguyu optimize etme ihtimalimin düşük olduğunu anlıyorum - ancak anlık sorguların performansını artırabileceğim yollar olabileceğini düşünüyorum
Lee Tickett

Ben bir öğrenciyim, hangi sütunun indekslenmesi gerektiğini nasıl bilebiliriz?
Virüs

7

Soru özellikle performansın anlık olarak iyileştirilmesi gerektiğini belirtir. sorgular ve dizinlerin eklenemeyeceğini belirtir. Öyleyse bunu ilk bakışta ele alırsak, herhangi bir masadaki performansı artırmak için ne yapılabilir?

Geçici sorguları düşündüğümüz için, WHERE yan tümcesi ve ORDER BY yan tümcesi sütunların herhangi bir kombinasyonunu içerebilir. Bu, tabloya hangi dizinlerin yerleştirildiğine bakılmaksızın, yukarıda kötü performans gösteren bir sorgunun sorgu planında görüldüğü gibi, tablo taraması gerektiren bazı sorgular olacağı anlamına gelir.

Bunu hesaba katarak, tabloda birincil anahtardaki kümelenmiş bir dizin dışında hiçbir dizin olmadığını varsayalım. Şimdi performansı en üst düzeye çıkarmak için hangi seçeneklere sahip olduğumuzu düşünelim.

  • Tabloyu birleştirin

    Kümelenmiş bir dizine sahip olduğumuz sürece, DBCC INDEXDEFRAG (kullanımdan kaldırıldı) veya tercihen ALTER INDEX kullanarak tabloyu birleştirebiliriz . Bu, tabloyu taramak için gereken disk okuma sayısını en aza indirecek ve hızı artıracaktır.

  • Mümkün olan en hızlı diskleri kullanın. Hangi diskleri kullandığınızı söylemiyorsunuz ama SSD kullanabiliyorsanız.

  • Tempdb'yi optimize edin. Tempdb'yi mümkün olan en hızlı disklere, yine SSD'lere yerleştirin. Bu SO Makalesine ve bu RedGate makalesine bakın .

  • Diğer yanıtlarda belirtildiği gibi, daha seçici bir sorgu kullanmak daha az veri döndürür ve bu nedenle daha hızlı olmalıdır.

Şimdi indeks eklememize izin verilirse ne yapabileceğimizi düşünelim.

Biz ise değildi reklamla-hoc sorguları bahsediyoruz, o zaman masaya karşı yürütülüyor sorgular sınırlı sayıda özel olarak endeksler eklersiniz. Geçici sorguları tartıştığımız için, çoğu zaman hızı artırmak için ne yapılabilir ?

  • Her sütuna tek bir sütun dizini ekleyin. Bu, SQL Server'a en azından sorguların çoğunun hızını artırmak için üzerinde çalışılacak bir şey vermelidir, ancak optimal olmayacaktır.
  • En yaygın sorgular için belirli dizinler ekleyin, böylece optimize edilirler.
  • Yetersiz performans gösteren sorguları izleyerek gerektiği şekilde ek özel dizinler ekleyin.

Düzenle

22 milyon satırlık 'geniş' bir tablo üzerinde bazı testler yaptım. Tablomda yalnızca altı sütun var, ancak 4GB veri içeriyor. Makinem, 8 Gb RAM ve dört çekirdekli bir CPU'ya sahip saygın bir masaüstü ve tek bir Agility 3 SSD'ye sahip.

Id sütunundaki birincil anahtar dışındaki tüm dizinleri kaldırdım.

Soruda verilen soruna benzer bir sorgu, SQL server önce ve ardından 3 saniye yeniden başlatılırsa 5 saniye sürer. Veritabanı ayarlama danışmanı, bu sorguyu iyileştirmek için% 99'un üzerinde bir tahmini iyileştirme ile bir dizin eklemeyi açıkça önerir. Bir dizin eklemek, etkin bir şekilde sıfır sorgu süresiyle sonuçlanır.

Ayrıca ilginç olan şey, benim sorgu planımın sizinkiyle aynı olmasıdır (kümelenmiş dizin taramasıyla), ancak dizin taraması sorgu maliyetinin% 9'unu ve sıralama kalan% 91'ini oluşturur. Sadece tablonuzun çok büyük miktarda veri içerdiğini ve / veya disklerinizin çok yavaş olduğunu veya çok yavaş bir ağ bağlantısı üzerinde bulunduğunu varsayabilirim.


2

Bazı sorgularda kullanılan bazı sütunlarda dizinleriniz olsa bile, 'geçici' sorgunuzun bir tablo taramasına neden olması, bu sorgunun verimli bir şekilde tamamlanmasını sağlamak için yeterli dizinlere sahip olmadığınızı gösterir.

Özellikle tarih aralıkları için iyi dizinler eklemek zordur.

Sadece sorgunuza bakarak, db'nin ilk n kaydı döndürebilmesi için tüm kayıtları seçilen sütuna göre sıralaması gerekir.

Db ayrıca cümle bazında sıralama olmaksızın tam bir tablo taraması yapar mı? Tablonun birincil anahtarı var mı - PK olmadan, db sıralamayı gerçekleştirmek için daha çok çalışmak zorunda kalacak mı?


Masada birincil anahtar var. Sadece yürütme sırasında yürütme planında bir tablo taraması da görünürselect top 100 * from ER101_ACCT_ORDER_DTL
Lee Tickett

2

Bu nasıl mümkün olabilir? Er101_upd_date_iso sütununda bir dizin olmadan kümelenmiş bir dizin taraması nasıl kullanılabilir?

Bir indeks, her bir yaprak düğümünün bir 'satır kümesine' işaret ettiği bir B-Ağacıdır (SQL iç terminolojisinde 'Sayfa' olarak adlandırılır). Bu, indeksin kümelenmemiş bir indeks olduğu zamandır.

Kümelenmiş indeks, yaprak düğümlerinin (onlara işaret etmek yerine) 'sıralar dizisine' sahip olduğu özel bir durumdur. bu yüzden...

1) Tabloda yalnızca bir kümelenmiş dizin olabilir.

bu aynı zamanda tüm tablonun kümelenmiş dizin olarak depolandığı anlamına gelir, bu nedenle tablo taraması yerine dizin taramasını görmeye başladınız.

2) Kümelenmiş dizini kullanan bir işlem genellikle kümelenmemiş bir dizinden daha hızlıdır

Http://msdn.microsoft.com/en-us/library/ms177443.aspx adresinde daha fazlasını okuyun

Sahip olduğunuz sorun için, bu sütunu bir dizine eklemeyi gerçekten düşünmelisiniz, çünkü yeni bir dizin (veya mevcut bir dizine bir sütun) eklemenin INSERT / UPDATE maliyetlerini artırdığını söylediğiniz gibi. Ancak, 'er101_upd_date_iso' ile değiştirmek için, yeterince kullanılmamış bazı dizini (veya mevcut bir dizinden bir sütunu) kaldırmak mümkün olabilir.

Dizin değişiklikleri mümkün değilse, sütuna bir istatistik eklemenizi öneririm, sütunların dizine alınmış sütunlarla bazı korelasyonları olduğunda işleri hızlandırabilir

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

BTW, ER101_ACCT_ORDER_DTL'nin tablo şemasını gönderebilirseniz çok daha fazla yardım alırsınız. ve mevcut indeksler de ... muhtemelen sorgu bazılarını kullanmak için yeniden yazılabilir.


Cevap için +1. Bir yorum olsa da, kümelenmiş dizinler her zaman yanıtınızdan okuyabileceğinizden (yanlış anlamak mümkündür) daha hızlı değildir.
Gisli

Sanırım kümelenmiş / kümelenmemiş dizin arasındaki farkı anlıyorum, ancak yine de kümelenmiş dizinin parçası olmayan bir sütunu sorgulamanın, diğer sütunlarda kümelenmiş bir dizine sahip olarak nasıl iyileştirilebileceğini anlamıyorum?
Lee Tickett

SQL Server'da kümelenmiş dizin tüm sütunları içerir. Kümelenmiş dizin, verilerin diskte nasıl saklanacağına karar verir. Açıklamak benim için biraz zor ama indeksleri bir ağaç olarak düşünürseniz, kümelenmemiş indeks bir ağaçtır ve alt yapraklar indekste tanımladığınız bilgileri içerir. Kümelenmiş bir dizin için, alttaki yapraklar tablodaki tüm sütunları içerir. Bu, SQL Server'daki tasarım gereğidir.
Gisli

Ben anladım. Ancak dalların kümelenmiş dizindeki sütunlara dayandığını düşündüm. Öyleyse, sorguladığım sütun int değilse, kümelenmiş indeks kesinlikle her dalın / yaprağın taranması gerekir?
Lee Tickett

1
Ben anlamadım. En iyi tahminim, kümelenmemiş bir dizine sahip olduğunuzda, çok sayıda rastgele G / Ç ile sonuçlanan taranmasıdır. Kümelenmiş dizini oluşturduğunuzda bu rastgele G / Ç'lerden kurtuldunuz mu? Ama bu bir tahmin, bu davranış için başka bir sebep bulamıyorum ama uzman değilim.
Gisli

1

1M testinizin daha hızlı çalışmasının nedenlerinden biri, muhtemelen geçici tabloların tamamen bellekte olması ve yalnızca sunucunuz bellek baskısı yaşarsa diske gidecek olmasıdır. Sorgunuzu yeniden oluşturarak siparişi kaldırabilir, daha önce belirtildiği gibi iyi bir kümelenmiş dizin ve kapsayan dizin (ler) ekleyebilir veya donanımla ilgili olup olmadığını görmek için GÇ basıncını kontrol etmek için DMV'yi sorgulayabilirsiniz.

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

umarım tüm veritabanım hafızada olur. Bunu kontrol etmenin veya sql'ye hangi tabloların bellekte saklanacağını söylemenin bir yolu var mı? birkaç gündür uzaktaydım ama geri döndüğümde sorgunuzu deneyeceğim
Lee Tickett

Ort. Görev Sayısı ve Ort. Bekleyen DiskIO Sayısı 4'te zirveye ulaştı. Hala db'yi ram'a zorlamayı denemeyi merak ediyorum.
Lee Tickett

0

Dizin eklemenin bir seçenek olmadığını, ancak elinizdeki tablo taramasını ortadan kaldırmak için tek seçenek olacağını söylediğinizi biliyorum. Tarama yaptığınızda SQL Server, sorgunuzu yerine getirmek için tablodaki 2 milyon satırı okur.

bu makale daha fazla bilgi sağlar ancak şunu unutmayın: Ara = iyi, Tarama = kötü.

İkinci olarak, * seçimini kaldırıp yalnızca ihtiyacınız olan sütunları seçemez misiniz? Üçüncü olarak, "nerede" cümlesi yok mu? Bir dizininiz olsa bile, her şeyi en iyi okuduğunuz için alacağınız bir dizin taramasıdır (bu bir tablo taramasından daha iyidir, ancak bir arama değildir, ki bunu hedeflemeniz gerekir)


Seek'in her zaman Taramadan daha iyi olduğu doğru değildir. Bazen Tarama aslında daha etkilidir. Durum bu olmasaydı, M $ SQL Server 2008 R2'den başlayarak bir FORCESCAN sorgu ipucu içermezdi. Daha fazla ayrıntı için buraya bakın: msdn.microsoft.com/en-us/library/ms181714(v=sql.105).aspx ve hatta burada bir Taramaya zorlamak isteyen biri için (Adam Haines'in 3. yanıtı iyi bilgiye sahiptir): sosyal .msdn.microsoft.com / Forums / en-US / transactsql / thread /…
Solomon Rutzky

1
Her şeyden önce, aramalar nokta benzeri sorgular için iyidir. İkinci olarak, taramalar, çok miktarda verinin alınması gereken aralık sorguları için iyidir. OLAP sistemleri, taramalar olmadan iyi performans göstermez. OLTP sistemleri aramalar olmadan iyi performans göstermez. Her şeyin büyük bir düzen içinde yeri vardır ...
darlove

0

Biliyorum başından beri epey zaman geçti ... Tüm bu cevaplarda çok fazla bilgelik var. Bir sorguyu iyileştirmeye çalışırken ilk şey iyi indekslemedir. Eh, neredeyse ilk. En ilki (tabiri caizse), verimli olması için kodda değişiklik yapmaktır. Yani, her şey söylendikten ve yapıldıktan sonra, WHERE içermeyen bir sorgu varsa veya WHERE koşulu yeterince seçici değilse, verileri almanın tek bir yolu vardır: TABLE SCAN (INDEX SCAN). Bir tablodaki tüm sütunlara ihtiyaç duyulursa, TABLE SCAN kullanılacaktır - bu konuda şüphe yok. Bu, veri organizasyonunun türüne bağlı olarak bir yığın taraması veya kümelenmiş dizin taraması olabilir. İşleri hızlandırmanın tek son yolu (mümkünse), taramayı yapmak için mümkün olduğunca çok çekirdek kullanıldığından emin olmaktır: OPTION (MAXDOP 0). Elbette depolama konusunu görmezden geliyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.