89

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

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

select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc

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

在此处输入图像描述

表扫描的其他详细信息:

在此处输入图像描述

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

该服务器非常强大(内存 48GB 内存,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;

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

在此处输入图像描述

更新 2

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

非集群:

在此处输入图像描述

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

集群:

在此处输入图像描述

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。

4

9 回答 9

65

简单的回答:不。您无法帮助对 238 列的表进行临时查询,并且聚集索引上的填充因子为 50%。

详细答案:

正如我在有关该主题的其他答案中所述,索引设计既是艺术又是科学,需要考虑的因素太多,因此几乎没有硬性规定(如果有的话)。您需要考虑:DML 操作与 SELECT 的数量、磁盘子系统、表上的其他索引/触发器、表内的数据分布、使用 SARGable WHERE 条件的查询以及其他一些我什至记不清的事情现在。

我可以说,如果不了解表本身、它的索引、触发器等,就无法为关于这个主题的问题提供任何帮助。现在您已经发布了表定义(仍在等待索引,但表定义单独指向99% 的问题)我可以提供一些建议。

首先,如果表格定义准确(238 列,50% 填充因子),那么您几乎可以忽略此处的其余答案/建议;-)。很抱歉在这里没有政治色彩,但说真的,这是一场不知道具体情况的疯狂追逐。现在我们看到了表定义,对于为什么一个简单的查询会花费这么长时间,即使测试查询(更新#1)运行得如此之快,也变得相当清楚了。

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

建议:

  1. 首先,这张桌子确实需要改造。如果这是一个数据仓库表,那么可能,但如果不是,那么这些字段确实需要分成几个表,它们都可以具有相同的 PK。您将拥有一个主记录表,子表只是基于共同关联属性的依赖信息,并且这些表的 PK 与主表的 PK 相同,因此也与主表的 FK 相同。主表和所有子表之间将存在一对一的关系。
  2. 的使用ANSI_PADDING OFF令人不安,更不用说由于随着时间的推移添加的各种列而导致表内不一致。不知道你现在是否可以解决这个问题,但理想情况下你总是有ANSI_PADDING ON,或者至少在所有ALTER TABLE语句中都有相同的设置。
  3. 考虑创建 2 个额外的文件组:表和索引。最好不要把你的东西放进去,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 还需要时间来访问表定义并转换*为所有列名. 在列表中指定所有 238 个列名应该会稍微SELECT快一些,但这对扫描问题没有帮助。但是,您是否真的同时需要所有 238 列?

祝你好运!

更新
为了完整起见“如何提高临时查询的大型表的性能”这个问题,应该注意,虽然它对这种特定情况没有帮助,但如果有人使用 SQL Server 2012(或更新版本)当那个时候)并且如果表没有被更新,那么使用列存储索引是一个选项。有关该新功能的更多详细信息,请查看此处:http: //msdn.microsoft.com/en-us/library/gg492088.aspx(我相信这些是从 SQL Server 2014 开始可更新的)。

更新 2
其他注意事项是:

  • 在聚集索引上启用压缩。此选项在 SQL Server 2008 中可用,但仅作为企业版功能。但是,从 SQL Server 2016 SP1开始,所有版本都提供了数据压缩功能!有关行和页面压缩的详细信息,请参阅数据压缩的 MSDN 页面。
  • 如果您不能使用数据压缩,或者如果它不会为特定表提供太多好处,那么如果您有一个固定长度类型的列(INT, BIGINT, TINYINT, SMALLINT, CHAR, NCHAR, BINARY, DATETIME, SMALLDATETIME, MONEY, 等)并且超过 50 % 的行是NULL,然后考虑启用SPARSE在 SQL Server 2008 中可用的选项。有关详细信息,请参阅使用稀疏列的 MSDN 页面。
于 2012-04-16T03:15:23.747 回答
55

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

缺乏索引

正如Oded已经提到的,缺少er101_upd_date_iso列索引是最重要的。

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

如果您无法添加索引(由于各种原因,包括为一个临时查询创建索引没有意义),我会建议一些解决方法(可用于临时查询):

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

优点:

  • 轻松处理任何数据子集。
  • 易于管理 - 它是临时的,它是table
  • 不影响整体系统性能,例如view.
  • 临时表可以被索引。
  • 你不必关心它——它是暂时的:)。

缺点:

  • 它是数据的快照——但这对于大多数临时查询来说可能已经足够了。

2.公用表表达式——CTE

就我个人而言,我经常将CTE用于临时查询——它对逐个构建(和测试)查询有很大帮助。

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

优点:

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

缺点:

  • 有些人不喜欢 CDE——CDE 查询似乎很长而且难以理解。

3. 创建视图

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

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

优点:

  • 很容易做到。
  • 它是最新的源数据。

缺点:

  • 仅适用于已定义的数据子集。
  • 对于具有高更新率的大型表可能效率低下。
  • 没那么容易管理。
  • 会影响整体系统性能。
  • 我不确定每个版本的 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 

脏读

可以加快临时查询的最后一件事是允许带有表提示的脏读WITH (NOLOCK)

您可以将事务隔离级别设置为未提交,而不是提示:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

或设置适当的 SQL Management Studio 设置。

我假设对于临时查询,脏读已经足够好了。

于 2012-04-05T09:32:47.077 回答
13

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

添加缺少的索引将无止境地帮助性能。

最常查询的列上已经有索引

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

我建议阅读 Gail Shaw 的《寻找 SQL Server 性能不佳的原因》,第 1部分和第 2 部分

于 2012-04-05T08:59:07.993 回答
7

该问题特别指出需要改进临时查询的性能,并且不能添加索引。那么从表面上看,可以做些什么来提高任何桌子的性能呢?

由于我们正在考虑即席查询,因此 WHERE 子句和 ORDER BY 子句可以包含列的任意组合。这意味着几乎不管表上放置了哪些索引,都会有一些查询需要表扫描,如上面在性能不佳查询的查询计划中所见。

考虑到这一点,我们假设除了主键上的聚集索引之外,表上根本没有索引。现在让我们考虑一下我们必须使用哪些选项来最大化性能。

  • 对表进行碎片整理

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

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

  • 优化 tempdb。将 tempdb 放在尽可能快的磁盘上,同样是 SSD。请参阅此SO 文章和此RedGate 文章

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

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

如果我们不是在谈论临时查询,那么我们将专门为针对表运行的有限查询集添加索引。既然我们正在讨论临时查询,那么在大多数情况下可以做些什么来提高速度呢?

  • 为每一列添加一个列索引。这应该至少为 SQL Server 提供一些可用于提高大多数查询速度的东西,但不会是最佳的。
  • 为最常见的查询添加特定索引,以便优化它们。
  • 通过监控性能不佳的查询,根据需要添加额外的特定索引。

编辑

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

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

如果首先重新启动 SQL Server,则与问题中给出的问题类似的查询需要 5 秒,随后需要 3 秒。数据库调优顾问显然建议添加一个索引来改进这个查询,估计改进 > 99%。添加索引会导致查询时间实际上为零。

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

于 2012-04-12T09:21:37.230 回答
2

即使您在某些查询中使用的某些列上有索引,您的“临时”查询会导致表扫描这一事实表明您没有足够的索引来允许此查询有效完成。

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

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

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

于 2012-04-09T06:24:16.597 回答
2

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

索引是一个 B-Tree,其中每个叶节点都指向“一堆行”(在 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

顺便说一句,如果您可以发布 ER101_ACCT_ORDER_DTL 的表模式,您将获得更多帮助。以及现有的索引......,可能可以重写查询以使用其中的一些。

于 2012-04-11T07:09:19.347 回答
1

One of the reasons your 1M test ran quicker is likely because the temp tables are entirely in memory and would only go to disk if your server experiences memory pressure. You can either re-craft your query to remove the order by, add a good clustered index and covering index(es) as previously mentioned, or query the DMV to check for IO pressure to see if hardware related.

-- 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
于 2012-04-10T23:26:56.067 回答
0

我知道您说过添加索引不是一种选择,但那将是消除您拥有的表扫描的唯一选择。当您进行扫描时,SQL Server 会读取表上的所有 200 万行来完成您的查询。

本文提供了更多信息,但请记住:Seek = 好,Scan = 坏。

其次,你不能消除select *并只选择你需要的列吗?第三,没有“where”子句?即使您有索引,由于您正在阅读所有内容,因此您会得到最好的索引扫描(这比表扫描更好,但它不是查找,这是您应该瞄准的目标)

于 2012-04-12T16:05:26.473 回答
0

我知道从一开始已经有很长一段时间了......所有这些答案都有很多智慧。尝试改进查询时,良好的索引是第一件事。嗯,几乎是第一个。最先(可以这么说)是对代码进行更改以使其高效。所以,说到底,如果一个查询没有 WHERE,或者当 WHERE 条件不够选择性时,只有一种方法可以获取数据:TABLE SCAN (INDEX SCAN)。如果需要表中的所有列,则将使用 TABLE SCAN - 毫无疑问。这可能是堆扫描或聚集索引扫描,具体取决于数据组织的类型。加快速度的唯一最后一种方法(如果可能的话)是确保使用尽可能多的内核来进行扫描:OPTION (MAXDOP 0)。当然,我忽略了存储的主题,

于 2017-08-16T20:57:54.280 回答