提高大型表上的SQL Server查询性能

时间:2012-04-05 08:56:59

标签: sql sql-server sql-server-2008

我有一个相对较大的表(目前有200万条记录),并且想知道是否有可能提高即席查询的性能。 ad-hoc 这个词在这里是关键。添加索引不是一个选项(最常见的列上已有索引)。

运行简单查询以返回最近更新的100条记录:

select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc

需要几分钟时间。请参阅下面的执行计划:

enter image description here

表扫描的其他详细信息:

enter image description here

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

服务器非常强大(从内存48GB ram,24核心处理器)运行sql server 2008 r2 x64。

更新

我发现这段代码可以创建一个包含1,000,000条记录的表。我想我可以在几台不同的服务器上运行SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESC,看看我的服务器上的磁盘访问速度是否很差。

WITH t1(N) AS (SELECT 1 UNION ALL SELECT 1),
t2(N) AS (SELECT 1 FROM t1 x, t1 y),
t3(N) AS (SELECT 1 FROM t2 x, t2 y),
Tally(N) AS (SELECT TOP 98 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM t3 x, t3 y),
Tally2(N) AS (SELECT TOP 5 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM t3 x, t3 y),
Combinations(N) AS (SELECT DISTINCT LTRIM(RTRIM(RTRIM(SUBSTRING(poss,a.N,2)) + SUBSTRING(vowels,b.N,1)))
                    FROM Tally a
                    CROSS JOIN Tally2 b
                    CROSS APPLY (SELECT 'B C D F G H J K L M N P R S T V W Z SCSKKNSNSPSTBLCLFLGLPLSLBRCRDRFRGRPRTRVRSHSMGHCHPHRHWHBWCWSWTW') d(poss)
                    CROSS APPLY (SELECT 'AEIOU') e(vowels))
SELECT IDENTITY(INT,1,1) AS ID, a.N + b.N AS N
INTO #testNames
FROM Combinations a 
CROSS JOIN Combinations b;

SELECT IDENTITY(INT,1,1) AS ID, firstName, secondName
INTO #testNames2
FROM (SELECT firstName, secondName
      FROM (SELECT TOP 1000 --1000 * 1000 = 1,000,000 rows
            N AS firstName
            FROM #testNames
            ORDER BY NEWID()) a
      CROSS JOIN (SELECT TOP 1000 --1000 * 1000 = 1,000,000 rows
                  N AS secondName
                  FROM #testNames
                  ORDER BY NEWID()) b) innerQ;

SELECT firstName, secondName,
firstName + '.' + secondName + '@fake.com' AS eMail,
CAST((ABS(CHECKSUM(NEWID())) % 250) + 1 AS VARCHAR(3)) + ' ' AS mailAddress,
(ABS(CHECKSUM(NEWID())) % 152100) + 1 AS jID,
IDENTITY(INT,1,1) AS ID
INTO #testNames3
FROM #testNames2

SELECT IDENTITY(INT,1,1) AS ID, firstName, secondName, eMail, 
mailAddress + b.N + b.N AS mailAddress
INTO testEnvironment
FROM #testNames3 a
INNER JOIN #testNames b ON a.jID = b.ID;

--CLEAN UP USELESS TABLES
DROP TABLE #testNames;
DROP TABLE #testNames2;
DROP TABLE #testNames3;

但是在三台测试服务器上,查询几乎是瞬间完成的。谁能解释一下呢?

enter image description here

更新2

感谢您的评论 - 请让他们继续...他们让我尝试将主键索引从非群集更改为群集,并使用相当有趣(和意外?)的结果。

非聚集:

enter image description here

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

集群:

enter image description here

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

这怎么可能?如果没有er101_upd_date_iso列的索引,如何使用聚簇索引扫描?

更新3

根据要求 - 这是创建表脚本:

CREATE TABLE [dbo].[ER101_ACCT_ORDER_DTL](
    [ER101_ORG_CODE] [varchar](2) NOT NULL,
    [ER101_ORD_NBR] [int] NOT NULL,
    [ER101_ORD_LINE] [int] NOT NULL,
    [ER101_EVT_ID] [int] NULL,
    [ER101_FUNC_ID] [int] NULL,
    [ER101_STATUS_CDE] [varchar](2) NULL,
    [ER101_SETUP_ID] [varchar](8) NULL,
    [ER101_DEPT] [varchar](6) NULL,
    [ER101_ORD_TYPE] [varchar](2) NULL,
    [ER101_STATUS] [char](1) NULL,
    [ER101_PRT_STS] [char](1) NULL,
    [ER101_STS_AT_PRT] [char](1) NULL,
    [ER101_CHG_COMMENT] [varchar](255) NULL,
    [ER101_ENT_DATE_ISO] [datetime] NULL,
    [ER101_ENT_USER_ID] [varchar](10) NULL,
    [ER101_UPD_DATE_ISO] [datetime] NULL,
    [ER101_UPD_USER_ID] [varchar](10) NULL,
    [ER101_LIN_NBR] [int] NULL,
    [ER101_PHASE] [char](1) NULL,
    [ER101_RES_CLASS] [char](1) NULL,
    [ER101_NEW_RES_TYPE] [varchar](6) NULL,
    [ER101_RES_CODE] [varchar](12) NULL,
    [ER101_RES_QTY] [numeric](11, 2) NULL,
    [ER101_UNIT_CHRG] [numeric](13, 4) NULL,
    [ER101_UNIT_COST] [numeric](13, 4) NULL,
    [ER101_EXT_COST] [numeric](11, 2) NULL,
    [ER101_EXT_CHRG] [numeric](11, 2) NULL,
    [ER101_UOM] [varchar](3) NULL,
    [ER101_MIN_CHRG] [numeric](11, 2) NULL,
    [ER101_PER_UOM] [varchar](3) NULL,
    [ER101_MAX_CHRG] [numeric](11, 2) NULL,
    [ER101_BILLABLE] [char](1) NULL,
    [ER101_OVERRIDE_FLAG] [char](1) NULL,
    [ER101_RES_TEXT_YN] [char](1) NULL,
    [ER101_DB_CR_FLAG] [char](1) NULL,
    [ER101_INTERNAL] [char](1) NULL,
    [ER101_REF_FIELD] [varchar](255) NULL,
    [ER101_SERIAL_NBR] [varchar](50) NULL,
    [ER101_RES_PER_UNITS] [int] NULL,
    [ER101_SETUP_BILLABLE] [char](1) NULL,
    [ER101_START_DATE_ISO] [datetime] NULL,
    [ER101_END_DATE_ISO] [datetime] NULL,
    [ER101_START_TIME_ISO] [datetime] NULL,
    [ER101_END_TIME_ISO] [datetime] NULL,
    [ER101_COMPL_STS] [char](1) NULL,
    [ER101_CANCEL_DATE_ISO] [datetime] NULL,
    [ER101_BLOCK_CODE] [varchar](6) NULL,
    [ER101_PROP_CODE] [varchar](8) NULL,
    [ER101_RM_TYPE] [varchar](12) NULL,
    [ER101_WO_COMPL_DATE] [datetime] NULL,
    [ER101_WO_BATCH_ID] [varchar](10) NULL,
    [ER101_WO_SCHED_DATE_ISO] [datetime] NULL,
    [ER101_GL_REF_TRANS] [char](1) NULL,
    [ER101_GL_COS_TRANS] [char](1) NULL,
    [ER101_INVOICE_NBR] [int] NULL,
    [ER101_RES_CLOSED] [char](1) NULL,
    [ER101_LEAD_DAYS] [int] NULL,
    [ER101_LEAD_HHMM] [int] NULL,
    [ER101_STRIKE_DAYS] [int] NULL,
    [ER101_STRIKE_HHMM] [int] NULL,
    [ER101_LEAD_FLAG] [char](1) NULL,
    [ER101_STRIKE_FLAG] [char](1) NULL,
    [ER101_RANGE_FLAG] [char](1) NULL,
    [ER101_REQ_LEAD_STDATE] [datetime] NULL,
    [ER101_REQ_LEAD_ENDATE] [datetime] NULL,
    [ER101_REQ_STRK_STDATE] [datetime] NULL,
    [ER101_REQ_STRK_ENDATE] [datetime] NULL,
    [ER101_LEAD_STDATE] [datetime] NULL,
    [ER101_LEAD_ENDATE] [datetime] NULL,
    [ER101_STRK_STDATE] [datetime] NULL,
    [ER101_STRK_ENDATE] [datetime] NULL,
    [ER101_DEL_MARK] [char](1) NULL,
    [ER101_USER_FLD1_02X] [varchar](2) NULL,
    [ER101_USER_FLD1_04X] [varchar](4) NULL,
    [ER101_USER_FLD1_06X] [varchar](6) NULL,
    [ER101_USER_NBR_060P] [int] NULL,
    [ER101_USER_NBR_092P] [numeric](9, 2) NULL,
    [ER101_PR_LIST_DTL] [numeric](11, 2) NULL,
    [ER101_EXT_ACCT_CODE] [varchar](8) NULL,
    [ER101_AO_STS_1] [char](1) NULL,
    [ER101_PLAN_PHASE] [char](1) NULL,
    [ER101_PLAN_SEQ] [int] NULL,
    [ER101_ACT_PHASE] [char](1) NULL,
    [ER101_ACT_SEQ] [int] NULL,
    [ER101_REV_PHASE] [char](1) NULL,
    [ER101_REV_SEQ] [int] NULL,
    [ER101_FORE_PHASE] [char](1) NULL,
    [ER101_FORE_SEQ] [int] NULL,
    [ER101_EXTRA1_PHASE] [char](1) NULL,
    [ER101_EXTRA1_SEQ] [int] NULL,
    [ER101_EXTRA2_PHASE] [char](1) NULL,
    [ER101_EXTRA2_SEQ] [int] NULL,
    [ER101_SETUP_MSTR_SEQ] [int] NULL,
    [ER101_SETUP_ALTERED] [char](1) NULL,
    [ER101_RES_LOCKED] [char](1) NULL,
    [ER101_PRICE_LIST] [varchar](10) NULL,
    [ER101_SO_SEARCH] [varchar](9) NULL,
    [ER101_SSB_NBR] [int] NULL,
    [ER101_MIN_QTY] [numeric](11, 2) NULL,
    [ER101_MAX_QTY] [numeric](11, 2) NULL,
    [ER101_START_SIGN] [char](1) NULL,
    [ER101_END_SIGN] [char](1) NULL,
    [ER101_START_DAYS] [int] NULL,
    [ER101_END_DAYS] [int] NULL,
    [ER101_TEMPLATE] [char](1) NULL,
    [ER101_TIME_OFFSET] [char](1) NULL,
    [ER101_ASSIGN_CODE] [varchar](10) NULL,
    [ER101_FC_UNIT_CHRG] [numeric](13, 4) NULL,
    [ER101_FC_EXT_CHRG] [numeric](11, 2) NULL,
    [ER101_CURRENCY] [varchar](3) NULL,
    [ER101_FC_RATE] [numeric](12, 5) NULL,
    [ER101_FC_DATE] [datetime] NULL,
    [ER101_FC_MIN_CHRG] [numeric](11, 2) NULL,
    [ER101_FC_MAX_CHRG] [numeric](11, 2) NULL,
    [ER101_FC_FOREIGN] [numeric](12, 5) NULL,
    [ER101_STAT_ORD_NBR] [int] NULL,
    [ER101_STAT_ORD_LINE] [int] NULL,
    [ER101_DESC] [varchar](255) NULL
) ON [PRIMARY]
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PRT_SEQ_1] [varchar](12) NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PRT_SEQ_2] [varchar](120) NULL
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAX_BASIS] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_RES_CATEGORY] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DECIMALS] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAX_SEQ] [varchar](7) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MANUAL] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_LC_RATE] [numeric](12, 5) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_FC_RATE] [numeric](12, 5) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_PL_RATE] [numeric](12, 5) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_DIFF] [char](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_UNIT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_EXT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_MIN_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TR_MAX_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_UNIT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_EXT_CHRG] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_MIN_CHRG] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_MAX_CHRG] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAX_RATE_TYPE] [char](1) NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ORDER_FORM] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_FACTOR] [int] NULL
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MGMT_RPT_CODE] [varchar](6) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ROUND_CHRG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_WHOLE_QTY] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SET_QTY] [numeric](15, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SET_UNITS] [numeric](15, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SET_ROUNDING] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SET_SUB] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TIME_QTY] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_GL_DISTR_PCT] [numeric](7, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REG_SEQ] [int] NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC] [varchar](255) NULL
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REG_ACCT] [varchar](8) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DAILY] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_AVG_UNIT_CHRG] [varchar](1) NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC2] [varchar](255) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_CONTRACT_SEQ] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ORIG_RATE] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DISC_PCT] [decimal](17, 10) NULL
SET ANSI_PADDING OFF
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DTL_EXIST] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ORDERED_ONLY] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_STDATE] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_STTIME] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_ENDATE] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_ENTIME] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_RATE] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_UNITS] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_BASE_RATE] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_COMMIT_QTY] [numeric](11, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MM_QTY_USED] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MM_CHRG_USED] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_TEXT_1] [varchar](50) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_1] [numeric](13, 3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_2] [numeric](13, 3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_3] [numeric](13, 3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_BASE_RATE] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REV_DIST] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_COVER] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_RATE_TYPE] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_USE_SEASONAL] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAX_EI] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TAXES] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_FC_TAXES] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PL_TAXES] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_FC_QTY] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_LEAD_HRS] [numeric](6, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_STRIKE_HRS] [numeric](6, 2) NULL
SET ANSI_PADDING ON
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_CANCEL_USER_ID] [varchar](10) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ST_OFFSET_HRS] [numeric](7, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_EN_OFFSET_HRS] [numeric](7, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_FLAG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_EXT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_EXT_CHRG_PL] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_EXT_CHRG_TR] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MEMO_EXT_CHRG_FC] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TIME_QTY_EDIT] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SURCHARGE_PCT] [decimal](17, 10) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_INCL_EXT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_INCL_EXT_CHRG_FC] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_CARRIER] [varchar](6) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_ID2] [varchar](8) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHIPPABLE] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_CHARGEABLE] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_ALLOW] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_START] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_NBR_END] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_SUPPLIER] [varchar](8) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_TRACK_ID] [varchar](40) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REF_INV_NBR] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_NEW_ITEM_STS] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MSTR_REG_ACCT_CODE] [varchar](8) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC3] [varchar](255) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC4] [varchar](255) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ALT_DESC5] [varchar](255) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_ROLLUP] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MM_COST_USED] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_AUTO_SHIP_RCD] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_FIXED] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ITEM_EST_TBD] [varchar](3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ROLLUP_PL_UNIT_CHRG] [numeric](13, 4) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ROLLUP_PL_EXT_CHRG] [numeric](13, 2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_GL_ORD_REV_TRANS] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_DISCOUNT_FLAG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_RES_TYPE] [varchar](6) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_RES_CODE] [varchar](12) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PERS_SCHED_FLAG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PRINT_STAMP] [datetime] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SHOW_EXT_CHRG] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PRINT_SEQ_NBR] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_PAY_LOCATION] [varchar](3) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_MAX_RM_NIGHTS] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_USE_TIER_COST] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_UNITS_SCHEME_CODE] [varchar](6) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_ROUND_TIME] [varchar](2) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_LEVEL] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_SETUP_PARENT_ORD_LINE] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_BADGE_PRT_STS] [varchar](1) NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_EVT_PROMO_SEQ] [int] NULL
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD [ER101_REG_TYPE] [varchar](12) NULL
/****** Object:  Index [PK__ER101_ACCT_ORDER]    Script Date: 04/15/2012 20:24:37 ******/
ALTER TABLE [dbo].[ER101_ACCT_ORDER_DTL] ADD  CONSTRAINT [PK__ER101_ACCT_ORDER] PRIMARY KEY CLUSTERED 
(
    [ER101_ORD_NBR] ASC,
    [ER101_ORD_LINE] ASC,
    [ER101_ORG_CODE] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 50) ON [PRIMARY]

该表的大小为2.8 GB,索引大小为3.9 GB。

9 个答案:

答案 0 :(得分:52)

简单回答:没有。您无法在238列表上对群集索引上的50%填充因子进行即席查询。

详细答案:

正如我在关于这个主题的其他答案中所说,指数设计既是艺术又是科学,有很多因素需要考虑,因为很少有(如果有的话)硬性规则。您需要考虑:DML操作与SELECT的数量,磁盘子系统,表上的其他索引/触发器,表中数据的分布,是使用SARGable WHERE条件的查询,还有其他一些我甚至无法做到的事情记得现在。

我可以说在没有理解表本身,索引,触发器等的情况下,没有关于这个主题的问题的帮助。现在你已经发布了表定义(仍在等待索引但是表定义单独指出问题的99%)我可以提供一些建议。

首先,如果表格定义准确(238列,50%填充因子),那么你几乎可以忽略其余的答案/建议;-)。对不起在这里不太政治,但严肃地说,这是一个疯狂的追逐,不知道具体细节。现在,我们看到表定义,即使测试查询(Update#1)运行得如此之快,为什么简单查询需要这么长时间也会变得更加清晰。

这里的主要问题(以及许多性能不佳的情况)是糟糕的数据建模。不禁止238列,就像不禁止999个索引一样,但通常也不是很明智。

建议:

  1. 首先,这个表确实需要重新构建。如果这是一个数据仓库表,那么可能,但如果没有,那么这些字段确实需要分成几个表,这些表都可以具有相同的PK。您将拥有一个主记录表,子表只是基于常用关联属性的依赖信息,这些表的PK与主表的PK相同,因此也是主表的FK。主数据库和所有子表之间将存在一对一的关系。
  2. ANSI_PADDING OFF的使用令人不安,更不用说由于随着时间的推移添加了各种列而在表中不一致。不确定您现在是否可以解决此问题,但理想情况下,您始终会ANSI_PADDING ON,或者至少在所有ALTER TABLE语句中设置相同的设置。
  3. 考虑创建另外两个文件组:表和索引。最好不要将您的东西放在PRIMARY中,因为SQL SERVER存储了有关对象的所有数据和元数据。您在[Tables]上创建表和聚簇索引(因为这是表的数据)以及[Indexes]上的所有非聚簇索引
  4. 将填充因子从50%增加。这个低数字可能是您的索引空间大于数据空间的原因。执行索引重建将重新创建数据页面,最多使用4k(在总页数大小为8k),因此您的表格可以在很大范围内展开。
  5. 如果大多数或所有查询都有" ER101_ORG_CODE"在WHERE条件中,然后考虑将其移动到聚集索引的前导列。假设它的使用频率高于" ER101_ORD_NBR"。如果" ER101_ORD_NBR"经常使用然后保留它。看来,假设字段名称意味着" OrganizationCode"和" OrderNumber"," OrgCode"是一个更好的分组,可能有多个" OrderNumbers"在其中。
  6. 次要,但如果" ER101_ORG_CODE"总是2个字符,然后使用CHAR(2)而不是VARCHAR(2),因为它会在行标题中保存一个字节,跟踪可变宽度大小并累计超过数百万行。
  7. 正如其他人提到的那样,使用SELECT *会影响性能。不仅因为它要求SQL Server返回所有列,因此无论您的其他索引如何都更有可能执行聚簇索引扫描,但它也需要SQL Server时间转到表定义并将*转换为所有列名称。如果<{1}}列表中的所有238列名称都指定了,那么稍微会更快 ,但这不会有助于扫描问题。但是你真的同时需要所有238列吗?
  8. 祝你好运!

    <强>更新
    为了完整地解决问题&#34;如何提高大型表的性能以进行即席查询&#34;,应该注意的是,虽然它对这种特定情况没有帮助,但如果有人使用SQL Server 2012年(当时间到来时更新)并且如果表没有更新,那么使用Columnstore Indexes是一个选项。有关该新功能的更多详细信息,请查看此处: http://msdn.microsoft.com/en-us/library/gg492088.aspx(我相信这些可以从SQL Server 2014开始更新)。

    更新2
    其他考虑因素包括:

    • 在群集索引上启用压缩。此选项在SQL Server 2008中可用,但仅作为Enterprise Edition功能。但是,从SQL Server 2016 SP1 开始,数据压缩可用in all editions!有关行和页面压缩的详细信息,请参阅Data Compression的MSDN页面。
    • 如果您无法使用数据压缩,或者它不能为特定表提供多少好处,那么如果您有一个固定长度类型的列(SELECTINTBIGINTTINYINTSMALLINTCHARNCHARBINARYDATETIMESMALLDATETIME等并且超过50%的行是MONEY,然后考虑启用SQL Server 2008中可用的NULL选项。有关详细信息,请参阅Use Sparse Columns的MSDN页面。

答案 1 :(得分:51)

此查询存在一些问题(这适用于每个查询)。

缺少索引

er101_upd_date_iso列上缺少索引是最重要的,因为Oded已经提到过了。

没有匹配的索引(缺少可能导致表扫描),就没有机会在大表上运行快速查询。

如果你不能添加索引(由于各种原因,包括只为一个即席查询创建索引没有意义),我建议一些解决方法(可以用于ad-hoc)查询):

1。使用临时表

在您感兴趣的数据的子集(行和列)上创建临时表。 临时表应该比原始源表小得多,可以轻松索引(如果需要),并且可以缓存您感兴趣的数据子集。

要创建临时表,您可以使用代码(未经测试),如:

-- copy records from last month to temporary table
INSERT INTO
   #my_temporary_table
SELECT
    *
FROM
    er101_acct_order_dtl WITH (NOLOCK)
WHERE 
    er101_upd_date_iso > DATEADD(month, -1, GETDATE())

-- you can add any index you need on temp table
CREATE INDEX idx_er101_upd_date_iso ON #my_temporary_table(er101_upd_date_iso)

-- run other queries on temporary table (which can be indexed)
SELECT TOP 100
    * 
FROM 
    #my_temporary_table 
ORDER BY 
    er101_upd_date_iso DESC

优点:

  • 易于处理任何数据子集。
  • 易于管理 - 它是临时,它是
  • 不会影响view等整体系统性能。
  • 可以为临时表编制索引。
  • 你不必关心它 - 它是暂时的:)。

缺点:

  • 这是数据的快照 - 但对于大多数临时查询而言,这可能已经足够了。

2。公用表表达式 - CTE

就个人而言,我经常使用CTE进行临时查询 - 这对于逐个构建(和测试)查询有很大帮助。

请参阅下面的示例(以WITH开头的查询)。

优点:

  • 大视图开始轻松构建,然后选择并过滤您真正需要的内容。
  • 易于测试。

缺点:

  • 有些人不喜欢CDE - CDE查询似乎很长很难理解。

3。创建视图

与上面类似,但创建视图而不是临时表(如果您经常使用相同的查询,并且您拥有支持索引视图的MS SQL版本。

您可以对您感兴趣的数据子集创建视图或indexed views 并在视图上运行查询 - 它应该只包含比整个表小得多的有趣数据子集。

优点:

  • 容易做到。
  • 这是源数据的最新版本。

缺点:

  • 仅可用于已定义的数据子集。
  • 对于具有高更新率的大型表格可能效率低下。
  • 不太容易管理。
  • 会影响整体系统性能。
  • 我不确定每个版本的MS SQL都提供索引视图。

选择所有列

在大桌上运行明星查询SELECT * FROM)并不是件好事......

如果您有大列(如长字符串),则需要花费大量时间从磁盘读取它们 并通过网络传播。

我会尝试用您真正需要的列名替换*

或者,如果您需要所有列,请尝试将查询重写为(使用公共数据表达式):

;WITH recs AS (
    SELECT TOP 100 
        id as rec_id -- select primary key only
    FROM 
        er101_acct_order_dtl 
    ORDER BY 
        er101_upd_date_iso DESC
)
SELECT
    er101_acct_order_dtl.*
FROM
    recs
    JOIN
      er101_acct_order_dtl
    ON
      er101_acct_order_dtl.id = recs.rec_id
ORDER BY 
    er101_upd_date_iso DESC 

脏读

可以加速即席查询的最后一件事是允许使用table hint WITH (NOLOCK)进行脏读

而不是暗示你可以set transaction isolation level阅读uncommited:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

或设置正确的SQL Management Studio设置。

我认为对于临时查询脏读已经足够了。

答案 2 :(得分:12)

您正在进行表扫描,这意味着没有在er101_upd_date_iso上定义索引,或者该列是现有列的一部分index,索引不能使用(可能它不是主索引器列)。

添加缺失的索引将有助于性能不再结束。

  

最常查询的列上已有索引

这并不意味着它们在此查询中使用(并且它们可能不是)。

我建议通过Gail Shaw,part 1part 2阅读查找SQL Server性能不佳的原因。

答案 3 :(得分:7)

该问题明确指出 ad-hoc 查询需要改进的性能,并且无法添加索引。因此,从表面上看,可以采取哪些措施来提高任何桌面的表现?

由于我们正在考虑临时查询,因此WHERE子句和ORDER BY子句可以包含任何列组合。这意味着几乎不管表上放置了什么索引,都会有一些需要进行表扫描的查询,如上面查询效果不佳的查询计划所示。

考虑到这一点,我们假设除了主键上的聚簇索引之外,表上根本没有索引。现在让我们考虑一下我们有哪些选择来最大限度地提高性能。

  • 对表进行碎片整理

    只要我们有聚簇索引,我们就可以使用DBCC INDEXDEFRAG(不建议使用)或最好ALTER INDEX对表进行碎片整理。 这将最大限度地减少扫描表所需的磁盘读取次数,并提高速度。

  • 尽可能使用最快的磁盘。您没有说明您正在使用的磁盘,但是您是否可以使用SSD。

  • 优化tempdb。将tempdb放在最快的磁盘上,再次使用SSD。请参阅此SO Article和此RedGate article

  • 如其他答案中所述,使用更具选择性的查询将返回更少的数据,因此应该更快。

现在让我们考虑如果允许我们添加索引,我们可以做些什么。

如果我们没有谈论即席查询,那么我们将专门针对针对该表运行的有限查询集添加索引。 由于我们正在讨论 ad-hoc 查询,可以采取哪些措施来提高大多数时间的速度?

  • 为每列添加单个列索引。这应该为SQL Server提供至少可以使用的东西来提高大多数查询的速度,但不是最佳的。
  • 为最常见的查询添加特定索引,以便对其进行优化。
  • 根据监控执行效果不佳的查询添加其他特定索引。

修改

我在一个2200万行的'大'表上运行了一些测试。我的表只有六列,但确实包含4GB的数据。我的机器是一款值得尊敬的台式机,配备8Gb RAM和四核CPU,并配有一个Agility 3 SSD。

我删除了Id列上主键以外的所有索引。

如果首先重新启动SQL服务器并且随后重启3秒,则对问题中给出的问题进行类似的查询需要5秒。数据库调优顾问显然建议添加索引以改进此查询,估计改进了&gt; 99%。添加索引会导致查询时间实际为零。

有趣的是,我的查询计划与您的查询计划相同(使用聚簇索引扫描),但索引扫描占查询成本的9%,而剩余的91%则排序。我只能假设您的表包含大量数据和/或您的磁盘速度很慢或位于非常慢的网络连接上。

答案 4 :(得分:2)

即使您在某些查询中使用某些列的索引,“ad-hoc”查询导致表扫描的事实也表明您没有足够的索引来允许此查询有效地完成。 / p>

特别是对于日期范围,很难添加好的索引。

只需查看您的查询,数据库必须按所选列对所有记录进行排序,以便能够返回前n个记录。

db是否也在没有order by子句的情况下进行全表扫描?该表是否具有主键 - 如果没有PK,db将不得不更加努力地执行排序?

答案 5 :(得分:2)

  
    

这怎么可能?如果没有er101_upd_date_iso列的索引,如何使用聚簇索引扫描?

  

索引是一个B树,其中每个叶节点指向一堆“行”(在SQL内部术语中称为“页面”),即索引是非聚集索引时。

聚簇索引是一种特殊情况,其中叶节点具有“一堆行”(而不是指向它们)。这就是为什么......

1)表上只能有一个聚簇索引。

这也意味着整个表存储为聚簇索引,这就是您开始看到索引扫描而不是表扫描的原因。

2)利用聚簇索引的操作通常比非聚簇索引

更快

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

了解详情

对于您遇到的问题,您应该考虑将此列添加到索引中,因为您说添加新索引(或列到现有索引)会增加INSERT / UPDATE成本。但是有可能删除一些未充分利用的索引(或现有索引中的列)以替换为“er101_upd_date_iso”。

如果无法进行索引更改,我建议在列上添加统计信息,当列与索引列有一定关联时,它可以解决问题

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

BTW,如果您可以发布ER101_ACCT_ORDER_DTL的表格架构,您将获得更多帮助。 和现有的索引......,可能会重新编写查询以使用其中的一些。

答案 6 :(得分:1)

您的1M测试运行得更快的原因之一可能是因为临时表完全在内存中,并且只有在您的服务器遇到内存压力时才会进入磁盘。您可以重新编写查询以删除顺序,添加良好的聚簇索引并覆盖索引(如前所述),或查询DMV以检查IO压力以查看硬件是否相关。

-- From Glen Barry
-- Clear Wait Stats (consider clearing and running wait stats query again after a few minutes)
-- DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR);

-- Check Task Counts to get an initial idea what the problem might be

-- Avg Current Tasks Count, Avg Runnable Tasks Count, Avg Pending Disk IO Count across all schedulers
-- Run several times in quick succession
SELECT AVG(current_tasks_count) AS [Avg Task Count], 
       AVG(runnable_tasks_count) AS [Avg Runnable Task Count],
       AVG(pending_disk_io_count) AS [Avg Pending DiskIO Count]
FROM sys.dm_os_schedulers WITH (NOLOCK)
WHERE scheduler_id < 255 OPTION (RECOMPILE);

-- Sustained values above 10 suggest further investigation in that area
-- High current_tasks_count is often an indication of locking/blocking problems
-- High runnable_tasks_count is a good indication of CPU pressure
-- High pending_disk_io_count is an indication of I/O pressure

答案 7 :(得分:0)

我知道您说添加索引不是一种选择,但这是消除您的表扫描的唯一选择。执行扫描时,SQL Server会读取表中的所有200万行以完成查询。

this文章提供了更多信息,但请记住:Seek = good,Scan = bad。

其次,你不能消除select *并只选择你需要的列吗? 三,没有“哪里”条款?即使你有一个索引,因为你正在阅读所有最好的东西,你会得到一个索引扫描(这比一个表扫描更好,但它不是一个寻求,这是你应该瞄准的目标)

答案 8 :(得分:0)

我知道从一开始就已经很久了......所有这些答案都有很多智慧。在尝试改进查询时,首先要做好索引。好吧,几乎是第一个。最重要的(可以这么说)是对代码进行更改以使其有效。因此,在完成所有操作之后,如果一个查询没有WHERE,或者WHERE条件没有足够的选择性,那么只有一种获取数据的方法:TABLE SCAN(INDEX SCAN)。如果需要表中的所有列,则将使用TABLE SCAN - 毫无疑问。这可能是堆扫描或聚簇索引扫描,具体取决于数据组织的类型。加速(如果可能的话)的唯一最后一种方法是确保尽可能多地使用核心进行扫描:OPTION(MAXDOP 0)。当然,我忽略了存储的主题,但是应该确保一个人有无限的RAM,这不用说:)