Cải thiện hiệu suất truy vấn SQL Server trên các bảng lớn


85

Tôi có một bảng tương đối lớn (hiện có 2 triệu bản ghi) và muốn biết liệu có thể cải thiện hiệu suất cho các truy vấn đặc biệt hay không. Từ đặc biệt là chìa khóa ở đây. Thêm chỉ mục không phải là một tùy chọn (đã có chỉ mục trên các cột được truy vấn phổ biến nhất).

Chạy một truy vấn đơn giản để trả về 100 bản ghi được cập nhật gần đây nhất:

select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc

Mất vài phút. Xem kế hoạch thực hiện bên dưới:

nhập mô tả hình ảnh ở đây

Chi tiết bổ sung từ quá trình quét bảng:

nhập mô tả hình ảnh ở đây

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

Máy chủ khá mạnh (từ bộ nhớ ram 48GB, bộ vi xử lý 24 lõi) chạy sql server 2008 r2 x64.

Cập nhật

Tôi tìm thấy mã này để tạo một bảng với 1.000.000 bản ghi. Tôi nghĩ sau đó tôi có thể chạy SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESCtrên một vài máy chủ khác nhau để tìm hiểu xem tốc độ truy cập đĩa của tôi có kém trên máy chủ hay không.

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;

Nhưng trên ba máy chủ thử nghiệm, truy vấn chạy gần như ngay lập tức. Bất cứ ai có thể giải thích điều này?

nhập mô tả hình ảnh ở đây

Cập nhật 2

Cảm ơn bạn đã đóng góp ý kiến- hãy tiếp tục tham gia ... họ đã khiến tôi thử thay đổi chỉ mục khóa chính từ không phân cụm thành có cụm với kết quả khá thú vị (và bất ngờ?).

Không phân cụm:

nhập mô tả hình ảnh ở đây

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

Nhóm:

nhập mô tả hình ảnh ở đây

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

Sao có thể như thế được? Nếu không có chỉ mục trên cột er101_upd_date_iso thì làm thế nào có thể sử dụng quét chỉ mục theo cụm?

Cập nhật 3

Theo yêu cầu - đây là kịch bản tạo bảng:

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]

Bảng có kích thước 2,8 GB với kích thước chỉ mục là 3,9 GB.


1
Có một số gợi ý nhỏ khi bạn di con trỏ chuột qua mục kế hoạch. Chúng hiển thị I / O ước tính và chi phí CPU. Tôi sẽ quan tâm đến chi phí I / O lúc đầu.
Grzegorz Gierlik

4
Table Scanchỉ ra một đống (không có chỉ mục được phân cụm) - vì vậy bước đầu tiên sẽ là thêm một chỉ mục được phân nhóm tốt, nhanh chóng vào bảng của bạn. Bước thứ hai có thể là để điều tra nếu một chỉ số nonclustered trên er101_upd_date_isosẽ giúp (và không gây ra nhược điểm biểu diễn khác)
marc_s

1
@marc_s cảm ơn vì điều đó- tôi đã thay đổi chỉ mục pk thành nhóm và nó đã tạo ra sự khác biệt về cơ bản- bạn có thể giải thích thêm về điều này không? (xem bản cập nhật 2)
Lee Tickett

2
Chà, chỉ mục được phân cụm chỉ thay đổi bố cục lưu trữ của bảng. Chỉ mục được phân cụm chứa dữ liệu bảng thực tế trong các nút cấp độ lá của nó - nghĩa là: để đọc toàn bộ bảng, SQL Server hiện đang thực hiện quét chỉ mục theo cụm (về cơ bản là "quét bảng" trên bảng có chỉ mục được phân cụm). Điều đó hầu như luôn nhanh hơn một chút so với việc quét bảng trên một đống (không có chỉ mục theo cụm). Nếu bây giờ bạn đã thêm một chỉ mục không hợp nhất vào er101_upd_date_isocột, bạn có thể cũng có thể loại bỏ thao tác "Sắp xếp" trong kế hoạch thực thi của mình và tăng tốc mọi thứ hơn nữa
marc_s

2
@LeeTickett, vui lòng hiển thị định nghĩa Bảng & Chỉ mục của bạn. Có nhiều yếu tố cần xem xét và dường như không ai yêu cầu chúng (điều này làm tôi ngạc nhiên nhưng có lẽ không nên). Tôi có thể nói với bạn rằng 2 triệu hàng KHÔNG phải là bảng lớn và được lập chỉ mục đúng cách với 200 triệu + hàng trả về nhanh hơn mức này. Rất có thể chỉ mục được phân cụm (bây giờ bạn có một chỉ số nhờ vào marc_s) là một lựa chọn tồi nhưng khó nói nếu không xem chi tiết cụ thể. KHÔNG sử dụng phân vùng nhưng KHÔNG sử dụng ĐẶT THỐNG KÊ IO ON và kiểm tra Số đọc logic trong tab thông báo. Nếu một thay đổi làm giảm Số đọc logic, bạn đang tiến gần hơn.
Solomon Rutzky

Câu trả lời:


59

Câu trả lời đơn giản: KHÔNG. Bạn không thể trợ giúp các truy vấn đặc biệt trên bảng 238 cột với Hệ số lấp đầy 50% trên Chỉ mục được phân nhóm.

Câu trả lời chi tiết:

Như tôi đã nêu trong các câu trả lời khác về chủ đề này, Thiết kế chỉ mục vừa là Nghệ thuật vừa là Khoa học và có rất nhiều yếu tố cần xem xét nên có rất ít quy tắc cứng và nhanh, nếu có. Bạn cần xem xét: khối lượng hoạt động DML so với SELECT, hệ thống con đĩa, các chỉ mục / trình kích hoạt khác trên bảng, phân phối dữ liệu trong bảng, là các truy vấn sử dụng điều kiện SARGable WHERE và một số điều khác mà tôi thậm chí không thể nhớ được. hiện nay.

Tôi có thể nói rằng không thể trợ giúp cho các câu hỏi về chủ đề này nếu không hiểu về Bảng, các chỉ mục, trình kích hoạt của nó, v.v. Bây giờ bạn đã đăng định nghĩa bảng (vẫn đang chờ Chỉ mục nhưng riêng định nghĩa Bảng chỉ đến 99% vấn đề) Tôi có thể đưa ra một số gợi ý.

Đầu tiên, nếu định nghĩa bảng là chính xác (238 cột, 50% Hệ số lấp đầy) thì bạn có thể bỏ qua phần còn lại của câu trả lời / lời khuyên ở đây ;-). Xin lỗi vì không mang tính chính trị ở đây, nhưng nghiêm túc mà nói, đó là một cuộc rượt đuổi ngỗng hoang mà không biết chi tiết cụ thể. Và bây giờ chúng ta thấy định nghĩa bảng, nó trở nên rõ ràng hơn một chút về lý do tại sao một truy vấn đơn giản lại mất nhiều thời gian như vậy, ngay cả khi các truy vấn thử nghiệm (Bản cập nhật # 1) chạy quá nhanh.

Vấn đề chính ở đây (và trong nhiều tình huống hoạt động kém) là mô hình dữ liệu không tốt. 238 cột không bị cấm cũng giống như việc có 999 chỉ mục không bị cấm, nhưng nhìn chung nó cũng không được khôn ngoan lắm.

Khuyến nghị:

  1. Đầu tiên, chiếc bàn này thực sự cần được tu sửa lại. Nếu đây là một bảng kho dữ liệu thì có thể, nhưng nếu không thì các trường này thực sự cần được chia thành nhiều bảng mà tất cả có thể có cùng PK. Bạn sẽ có một bảng bản ghi chính và các bảng con chỉ là thông tin phụ thuộc dựa trên các thuộc tính thường được kết hợp và PK của các bảng đó giống với PK của bảng chính và do đó cũng FK với bảng chính. Sẽ có mối quan hệ 1-1 giữa bảng cái và tất cả các bảng con.
  2. Việc sử dụng của ANSI_PADDING OFFlà đáng lo ngại, chưa kể đến sự không nhất quán trong bảng do các cột bổ sung khác nhau theo thời gian. Không chắc liệu bạn có thể khắc phục điều đó ngay bây giờ hay không, nhưng lý tưởng nhất là bạn sẽ luôn có ANSI_PADDING ONhoặc ít nhất là có cùng một cài đặt trên tất cả các ALTER TABLEcâu lệnh.
  3. Cân nhắc tạo 2 Nhóm tệp bổ sung: Bảng và Chỉ mục. Tốt nhất là không nên đưa nội dung của bạn vào PRIMARYvì đó là nơi SQL SERVER lưu trữ tất cả dữ liệu của nó và siêu dữ liệu về các đối tượng của bạn. Bạn tạo Bảng và Chỉ mục theo cụm (vì đó là dữ liệu cho bảng) trên [Tables]và tất cả các chỉ mục Không được phân cụm trên[Indexes]
  4. Tăng Hệ số lấp đầy từ 50%. Con số thấp này có thể là lý do tại sao không gian chỉ mục của bạn lớn hơn không gian dữ liệu của bạn. Thực hiện Xây dựng lại chỉ mục sẽ tạo lại các trang dữ liệu với tối đa 4k (trong tổng số 8 nghìn trang) được sử dụng cho dữ liệu của bạn để bảng của bạn được trải rộng trên một khu vực rộng.
  5. Nếu hầu hết hoặc tất cả các truy vấn có "ER101_ORG_CODE" trong WHEREđiều kiện, thì hãy cân nhắc chuyển điều kiện đó sang cột đầu của chỉ mục được nhóm. Giả sử rằng nó được sử dụng thường xuyên hơn "ER101_ORD_NBR". Nếu "ER101_ORD_NBR" được sử dụng thường xuyên hơn thì hãy giữ nó. Có vẻ như, giả sử rằng tên trường có nghĩa là "Mã tổ chức" và "Số thứ tự", thì "Mã tổ chức" là một nhóm tốt hơn có thể có nhiều "Số thứ tự" bên trong nó.
  6. Điểm nhỏ, nhưng nếu "ER101_ORG_CODE" luôn là 2 ký tự, thì hãy sử dụng CHAR(2)thay vì VARCHAR(2)nó sẽ lưu một byte trong tiêu đề hàng để theo dõi kích thước chiều rộng thay đổi và thêm hàng triệu hàng.
  7. Như những người khác ở đây đã đề cập, việc sử dụng SELECT *sẽ ảnh hưởng đến hiệu suất. Không chỉ do nó yêu cầu SQL Server trả về tất cả các cột và do đó có nhiều khả năng thực hiện Quét chỉ mục theo cụm bất kể các chỉ mục khác của bạn, mà SQL Server còn mất thời gian để đi đến định nghĩa bảng và dịch *sang tất cả các tên cột . Sẽ nhanh hơn một chút để chỉ định tất cả 238 tên cột trong SELECTdanh sách mặc dù điều đó sẽ không giúp được vấn đề Quét. Nhưng bạn có bao giờ thực sự cần tất cả 238 cột cùng một lúc không?

Chúc may mắn!

CẬP NHẬT
Để hoàn thiện câu hỏi "cách cải thiện hiệu suất trên một bảng lớn cho các truy vấn đặc biệt", cần lưu ý rằng mặc dù nó sẽ không hữu ích cho trường hợp cụ thể này, NẾU ai đó đang sử dụng SQL Server 2012 (hoặc mới hơn khi thời điểm đó đến) và NẾU bảng không được cập nhật, thì việc sử dụng Columnstore Indexes là một tùy chọn. Để biết thêm chi tiết về tính năng mới đó, hãy xem tại đây: http://msdn.microsoft.com/en-us/library/gg492088.aspx (Tôi tin rằng những tính năng này đã được thực hiện để có thể cập nhật bắt đầu trong SQL Server 2014).

CẬP NHẬT 2
Các cân nhắc bổ sung là:

  • Bật tính năng nén trên Chỉ mục được phân cụm. Tùy chọn này có sẵn trong SQL Server 2008, nhưng là một tính năng chỉ dành cho Phiên bản Doanh nghiệp. Tuy nhiên, kể từ SQL Server 2016 SP1 , Nén dữ liệu đã được cung cấp trong tất cả các phiên bản ! Vui lòng xem trang MSDN về Nén Dữ liệu để biết chi tiết về Nén Hàng và Trang.
  • Nếu bạn không thể sử dụng nén dữ liệu, hoặc nếu nó sẽ không cung cấp nhiều lợi ích cho một bảng cụ thể, sau đó nếu bạn có một cột của một loại cố định chiều dài ( INT, BIGINT, TINYINT, SMALLINT, CHAR, NCHAR, BINARY, DATETIME, SMALLDATETIME, MONEY, vv) và hơn 50 % số hàng là NULL, sau đó xem xét bật SPARSEtùy chọn đã có sẵn trong SQL Server 2008. Vui lòng xem trang MSDN cho Sử dụng Cột thưa để biết chi tiết.

Về điểm 7 Cá nhân tôi tưởng tượng rằng nó cần được nhanh hơn để thêm 238 tên cột từ siêu dữ liệu hơn phân tích cú pháp họ ra khỏi nội dung truy vấn và sau đó phải kiểm tra các siêu dữ liệu nào để đảm bảo tất cả họ đều exist.There là lý lẽ đủ mạnh chống lại *mà không có một mơ hồ
Martin Smith

53

Có một số vấn đề với truy vấn này (và điều này áp dụng cho mọi truy vấn).

Thiếu chỉ số

Thiếu chỉ mục trên er101_upd_date_isocột là điều quan trọng nhất như Oded đã đề cập.

Nếu không có chỉ mục phù hợp (thiếu chỉ mục có thể gây ra quét bảng) thì không có cơ hội để chạy các truy vấn nhanh trên các bảng lớn.

Nếu bạn không thể thêm chỉ mục (vì nhiều lý do khác nhau, bao gồm cả việc tạo chỉ mục chỉ cho một truy vấn đặc biệt không có ích lợi gì ), tôi sẽ đề xuất một số cách giải quyết (có thể được sử dụng cho các truy vấn đặc biệt):

1. Sử dụng bảng tạm thời

Tạo bảng tạm thời trên tập hợp con (hàng và cột) dữ liệu bạn quan tâm. Bảng tạm thời phải nhỏ hơn nhiều so với bảng nguồn ban đầu, có thể được lập chỉ mục dễ dàng (nếu cần) và có thể lưu vào bộ nhớ đệm tập hợp con dữ liệu mà bạn quan tâm.

Để tạo bảng tạm thời, bạn có thể sử dụng mã (không được kiểm tra) như:

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

Ưu điểm:

  • Dễ dàng thực hiện đối với bất kỳ tập hợp con dữ liệu nào.
  • Dễ quản lý - nó tạm thời và nó là bảng .
  • Không ảnh hưởng đến hiệu suất hệ thống tổng thể như view.
  • Bảng tạm thời có thể được lập chỉ mục.
  • Bạn không cần phải quan tâm đến nó - đó là tạm thời :).

Nhược điểm:

  • Đó là ảnh chụp nhanh dữ liệu - nhưng có lẽ điều này đủ tốt cho hầu hết các truy vấn đặc biệt.

2. Biểu thức bảng chung - CTE

Cá nhân tôi sử dụng CTE rất nhiều với các truy vấn đặc biệt - nó giúp ích rất nhiều cho việc xây dựng (và thử nghiệm) từng phần một truy vấn.

Xem ví dụ bên dưới (truy vấn bắt đầu bằng WITH).

Ưu điểm:

  • Dễ dàng xây dựng bắt đầu từ chế độ xem lớn , sau đó chọn và lọc những gì bạn thực sự cần.
  • Dễ dàng kiểm tra.

Nhược điểm:

  • Một số người không thích CDE - Các truy vấn CDE có vẻ dài và khó hiểu.

3. Tạo chế độ xem

Tương tự như trên, nhưng tạo chế độ xem thay vì bảng tạm thời (nếu bạn chơi thường xuyên với cùng một truy vấn và bạn có phiên bản MS SQL hỗ trợ chế độ xem được lập chỉ mục.

Bạn có thể tạo dạng xem hoặc dạng xem được lập chỉ mục trên tập hợp con dữ liệu mà bạn quan tâm và chạy các truy vấn trên dạng xem - chỉ nên chứa tập hợp con dữ liệu thú vị nhỏ hơn nhiều so với toàn bộ bảng.

Ưu điểm:

  • Dễ làm.
  • Nó được cập nhật với dữ liệu nguồn.

Nhược điểm:

  • Chỉ có thể cho tập dữ liệu con đã xác định.
  • Có thể không hiệu quả đối với các bảng lớn với tỷ lệ cập nhật cao.
  • Không dễ dàng để quản lý.
  • Có thể ảnh hưởng đến hiệu suất tổng thể của hệ thống.
  • Tôi không chắc các dạng xem được lập chỉ mục có sẵn trong mọi phiên bản MS SQL.

Chọn tất cả các cột

Chạy truy vấn dấu sao ( SELECT * FROM) trên bảng lớn không phải là điều tốt ...

Nếu bạn có các cột lớn (như chuỗi dài), bạn sẽ mất rất nhiều thời gian để đọc chúng từ đĩa và chuyển qua mạng.

Tôi sẽ cố gắng thay thế *bằng tên cột mà bạn thực sự cần.

Hoặc, nếu bạn cần tất cả các cột, hãy thử viết lại truy vấn thành một thứ như (sử dụng biểu thức dữ liệu chung ):

;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 

Đọc bẩn

Điều cuối cùng có thể tăng tốc truy vấn đặc biệt là cho phép đọc bẩn với gợi ý bảngWITH (NOLOCK) .

Thay vì gợi ý, bạn có thể đặt mức cô lập giao dịch để đọc không giới hạn:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

hoặc đặt cài đặt SQL Management Studio thích hợp.

Tôi cho rằng đối với các truy vấn đặc biệt, các lần đọc bẩn là đủ tốt.


2
+1 cho SELECT *- nó buộc SQL Server sử dụng chỉ mục được phân cụm. Ít nhất, nó nên. Tôi không thấy bất kỳ lý do thực sự nào cho một chỉ mục bao phủ không phân cụm ... bao gồm toàn bộ bảng :)
ta.speot.is

4
Câu trả lời này chỉ giải quyết việc cải thiện tốc độ của truy vấn mẫu, KHÔNG phải câu hỏi, đó là "có thể cải thiện hiệu suất cho các truy vấn đặc biệt"
Phil

CDE tại là CTE (Common Table Expression)
sqluser

12

Bạn đang quét bảng ở đó, nghĩa là bạn không có chỉ mục được xác định trêner101_upd_date_iso hoặc nếu cột đó là một phần của chỉ mục hiện có, thì chỉ mục đó không thể được sử dụng (có thể nó không phải là cột của trình lập chỉ mục chính).

Thêm các chỉ mục còn thiếu sẽ giúp hiệu suất không có hồi kết.

đã có các chỉ mục trên các cột được truy vấn phổ biến nhất

Điều đó không có nghĩa là chúng được sử dụng trong truy vấn này (và chúng có thể không).

Tôi khuyên bạn nên đọc Tìm nguyên nhân của hiệu suất kém trong SQL Server của Gail Shaw, phần 1phần 2 .


quan điểm của tôi là đây không phải là một trong những cột được truy vấn phổ biến nhất :)
Lee Tickett

1
@LeeTickett - Chưa hết, đây là cột duy nhất bạn có thể thêm chỉ mục để cải thiện hiệu suất của truy vấn này .
Hoàn thành

2
Không có cái gọi là tìm kiếm không được lập chỉ mục được tối ưu hóa. Bạn đang sử dụng chỉ mục hoặc quét toàn bộ bảng. Nếu bạn không muốn quét toàn bộ bảng, bạn cần chỉ mục. Tùy thuộc vào hồ sơ sử dụng của bạn, nó có thể đủ rẻ để thêm các chỉ mục. Ồ, và kiểu dữ liệu của cột cũng quan trọng. Nếu er101_upd_date_isolà một varchar lớn hoặc một int, sẽ thay đổi hiệu suất một cách đáng kể.
Cylindric

cảm ơn. tôi đã thêm một bình luận cho câu hỏi. tôi đánh giá cao không chắc tôi có thể tối ưu hóa các query- nhưng tôi tưởng tượng có thể có những cách tôi có thể cải thiện hiệu suất của ad-hoc truy vấn
Lee Tickett

Tôi là một người học, làm thế nào để chúng tôi biết rằng cột nào cần lập chỉ mục?
Virus

7

Câu hỏi đặc biệt cho biết hiệu suất cần được cải thiện cho các truy vấn đặc biệt và không thể thêm chỉ mục. Vậy lấy mệnh giá đó, có thể làm gì để cải thiện hiệu suất trên bất kỳ bảng nào?

Vì chúng tôi đang xem xét các truy vấn đặc biệt, mệnh đề WHERE và mệnh đề ORDER BY có thể chứa bất kỳ tổ hợp cột nào. Điều này có nghĩa là hầu như bất kể chỉ mục nào được đặt trên bảng, sẽ có một số truy vấn yêu cầu quét bảng, như đã thấy ở trên trong kế hoạch truy vấn của một truy vấn hoạt động kém.

Có tính đến điều này, giả sử không có chỉ mục nào trên bảng ngoài một chỉ mục được nhóm trên khóa chính. Bây giờ chúng ta hãy xem xét những tùy chọn chúng ta có để tối đa hóa hiệu suất.

  • Chống phân mảnh bảng

    Miễn là chúng ta có một chỉ mục được phân nhóm thì chúng ta có thể chống phân mảnh bảng bằng cách sử dụng DBCC INDEXDEFRAG (không được dùng nữa) hoặc tốt hơn là ALTER INDEX . Điều này sẽ giảm thiểu số lần đọc đĩa cần thiết để quét bảng và sẽ cải thiện tốc độ.

  • Sử dụng các đĩa nhanh nhất có thể. Bạn không nói bạn đang sử dụng đĩa nào nhưng bạn có thể sử dụng SSD hay không.

  • Tối ưu hóa tempdb. Đặt tempdb trên các đĩa nhanh nhất có thể, lại là SSD. Xem bài viết SO này và bài báo RedGate này .

  • Như đã nêu trong các câu trả lời khác, sử dụng truy vấn chọn lọc hơn sẽ trả về ít dữ liệu hơn và do đó sẽ nhanh hơn.

Bây giờ chúng ta hãy xem xét những gì chúng ta có thể làm nếu chúng ta được phép thêm các chỉ mục.

Nếu chúng ta không nói về các truy vấn đặc biệt, thì chúng ta sẽ thêm các chỉ mục dành riêng cho nhóm truy vấn giới hạn đang được chạy trên bảng. Vì chúng ta đang thảo luận về các truy vấn đặc biệt , có thể làm gì để cải thiện tốc độ hầu hết thời gian?

  • Thêm một chỉ mục cột vào mỗi cột. Điều này sẽ cung cấp cho SQL Server ít nhất một cái gì đó để làm việc để cải thiện tốc độ cho phần lớn các truy vấn, nhưng sẽ không tối ưu.
  • Thêm các chỉ mục cụ thể cho các truy vấn phổ biến nhất để chúng được tối ưu hóa.
  • Thêm các chỉ mục cụ thể bổ sung theo yêu cầu bằng cách giám sát các truy vấn hoạt động kém.

Biên tập

Tôi đã chạy một số thử nghiệm trên bảng 'lớn' gồm 22 triệu hàng. Bảng của tôi chỉ có sáu cột nhưng chứa 4GB dữ liệu. Máy của tôi là một máy tính để bàn đáng nể với RAM 8Gb và CPU lõi tứ và có một SSD Agility 3 duy nhất.

Tôi đã xóa tất cả các chỉ mục ngoài khóa chính trên cột Id.

Một truy vấn tương tự cho vấn đề được đưa ra trong câu hỏi mất 5 giây nếu máy chủ SQL được khởi động lại trước và 3 giây sau đó. Cố vấn điều chỉnh cơ sở dữ liệu rõ ràng là khuyên bạn nên thêm chỉ mục để cải thiện truy vấn này, với mức cải thiện ước tính là> 99%. Thêm chỉ mục dẫn đến thời gian truy vấn thực sự bằng 0.

Điều thú vị nữa là kế hoạch truy vấn của tôi giống với kế hoạch của bạn (với quét chỉ mục theo cụm), nhưng quét chỉ mục chiếm 9% chi phí truy vấn và 91% còn lại sắp xếp. Tôi chỉ có thể cho rằng bảng của bạn chứa một lượng lớn dữ liệu và / hoặc đĩa của bạn rất chậm hoặc nằm trên kết nối mạng rất chậm.


2

Ngay cả khi bạn có chỉ mục trên một số cột được sử dụng trong một số truy vấn, thực tế là truy vấn 'đặc biệt' của bạn gây ra việc quét bảng cho thấy rằng bạn không có đủ chỉ mục để cho phép truy vấn này hoàn thành một cách hiệu quả.

Đối với các phạm vi ngày nói riêng, rất khó để thêm các chỉ mục tốt.

Chỉ cần nhìn vào truy vấn của bạn, db phải sắp xếp tất cả các bản ghi theo cột đã chọn để có thể trả về n bản ghi đầu tiên.

Liệu db cũng thực hiện quét toàn bộ bảng mà không có mệnh đề theo thứ tự? Bảng có khóa chính - không có PK, db sẽ phải làm việc nhiều hơn để thực hiện việc sắp xếp?


Có một khóa chính trên bàn. Một bảng quét cũng xuất hiện trong kế hoạch thực hiện khi chỉ đơn giản là thực hiệnselect top 100 * from ER101_ACCT_ORDER_DTL
Lee Tickett

2

Sao có thể như thế được? Nếu không có chỉ mục trên cột er101_upd_date_iso thì làm thế nào có thể sử dụng quét chỉ mục theo cụm?

Chỉ mục là B-Tree nơi mỗi nút lá trỏ đến một 'nhóm hàng' (được gọi là 'Trang' trong thuật ngữ nội bộ SQL), Đó là khi chỉ mục là một chỉ mục không phân cụm.

Chỉ mục cụm là một trường hợp đặc biệt, trong đó các nút lá có 'nhóm hàng' (thay vì trỏ đến chúng). đó là lý do tại sao...

1) Chỉ có thể có một chỉ mục nhóm trên bảng.

điều này cũng có nghĩa là toàn bộ bảng được lưu trữ dưới dạng chỉ mục theo cụm, đó là lý do tại sao bạn bắt đầu thấy quét chỉ mục hơn là quét bảng.

2) Hoạt động sử dụng chỉ mục được phân cụm thường nhanh hơn so với chỉ mục không được phân cụm

Đọc thêm tại http://msdn.microsoft.com/en-us/library/ms177443.aspx

Đối với vấn đề bạn gặp phải, bạn thực sự nên xem xét việc thêm cột này vào một chỉ mục, như bạn đã nói thêm một chỉ mục mới (hoặc một cột vào chỉ mục hiện có) làm tăng chi phí CHÈN / CẬP NHẬT. Nhưng có thể loại bỏ một số chỉ mục sử dụng chưa đủ (hoặc một cột từ chỉ mục hiện có) để thay thế bằng 'er101_upd_date_iso'.

Nếu không thể thay đổi chỉ mục, tôi khuyên bạn nên thêm số liệu thống kê trên cột, nó có thể làm nhanh mọi thứ khi các cột có một số tương quan với các cột được lập chỉ mục

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

BTW, Bạn sẽ nhận được nhiều trợ giúp hơn nếu bạn có thể đăng giản đồ bảng của ER101_ACCT_ORDER_DTL. và các chỉ số hiện có nữa ..., có thể truy vấn có thể được viết lại để sử dụng một số trong số chúng.


+1 cho câu trả lời. Tuy nhiên, có một nhận xét, các chỉ mục được phân nhóm không phải lúc nào cũng nhanh hơn như người ta có thể đọc (có thể hiểu nhầm) từ câu trả lời của bạn.
Gisli

Tôi nghĩ rằng tôi hiểu sự khác biệt giữa chỉ mục được phân nhóm / không được phân cụm nhưng vẫn không thấy cách truy vấn cột không phải là một phần của chỉ mục được phân nhóm có thể được cải thiện như thế nào bằng cách có chỉ mục được phân nhóm trên các cột khác?
Lee Tickett

Trong SQL Server, chỉ mục nhóm chứa tất cả các cột. Chỉ mục phân cụm là thứ quyết định cách dữ liệu được lưu trữ trên đĩa. Tôi hơi khó giải thích một chút nhưng nếu bạn coi các chỉ mục là một cái cây thì chỉ mục không hợp nhất là một cái cây và các lá phía dưới chứa thông tin mà bạn đã xác định là chứa chỉ mục. Đối với chỉ mục được phân nhóm, các lá dưới cùng chứa tất cả các cột trong bảng. Đây là do thiết kế trong SQL Server.
Gisli

Tôi hiểu điều đó. Nhưng tôi nghĩ rằng các nhánh dựa trên các cột trong chỉ mục nhóm. Vì vậy, nếu cột tôi đang truy vấn không phải là chỉ mục cụm từ anh ta chắc chắn mọi nhánh / lá đều cần được quét?
Lee Tickett

1
Tôi không hiểu điều này. Dự đoán tốt nhất của tôi là khi bạn có một chỉ mục không hợp nhất, nó đã được quét dẫn đến rất nhiều I / O ngẫu nhiên. Khi bạn tạo chỉ mục nhóm, bạn đã loại bỏ các I / O ngẫu nhiên đó chưa? Nhưng đây là một phỏng đoán, tôi không thể tìm thấy bất kỳ lý do nào khác cho hành vi này nhưng tôi không phải là chuyên gia.
Gisli

1

Một trong những lý do khiến thử nghiệm 1M của bạn chạy nhanh hơn có thể là do các bảng tạm thời hoàn toàn nằm trong bộ nhớ và sẽ chỉ chuyển sang đĩa nếu máy chủ của bạn gặp áp lực bộ nhớ. Bạn có thể tạo lại truy vấn của mình để xóa thứ tự bằng cách thêm chỉ mục được phân cụm tốt và bao gồm (các) chỉ mục như đã đề cập trước đây hoặc truy vấn DMV để kiểm tra áp lực IO để xem liệu có liên quan đến phần cứng hay không.

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

tôi hy vọng toàn bộ cơ sở dữ liệu của tôi sẽ ở trong bộ nhớ. có cách nào để kiểm tra điều đó hoặc để cho sql biết bảng nào cần lưu trong bộ nhớ không? tôi đã đi cho một vài ngày nhưng khi tôi quay trở lại tôi sẽ cố gắng query- của bạn nhờ
Lee Tickett

Số lượng tác vụ trung bình và số lượng DiskIO đang chờ xử lý trung bình đạt đỉnh là 4. Tôi vẫn tò mò về việc cố gắng ép db vào ram.
Lee Tickett

0

Tôi biết rằng bạn đã nói rằng thêm chỉ mục không phải là một tùy chọn nhưng đó sẽ là tùy chọn duy nhất để loại bỏ việc quét bảng mà bạn có. Khi bạn quét, SQL Server đọc tất cả 2 triệu hàng trên bảng để thực hiện truy vấn của bạn.

này bài viết cung cấp thông tin hơn, nhưng hãy nhớ rằng: Seek = tốt, Scan = xấu.

Thứ hai, bạn không thể loại bỏ select * và chỉ chọn những cột bạn cần? Thứ ba, không có mệnh đề "ở đâu"? Ngay cả khi bạn có chỉ mục, vì bạn đang đọc mọi thứ tốt nhất bạn sẽ nhận được là quét chỉ mục (tốt hơn quét bảng, nhưng nó không phải là tìm kiếm, đó là điều bạn nên nhắm tới)


Không hẳn là Seek luôn tốt hơn Scan. Đôi khi Quét thực sự hiệu quả hơn. Nếu không phải như vậy thì M $ sẽ không bao gồm gợi ý truy vấn FORCESCAN bắt đầu trong SQL Server 2008 R2. Xem tại đây để biết thêm chi tiết: msdn.microsoft.com/en-us/library/ms181714(v=sql.105).aspx và thậm chí ở đây cho ai đó đang tìm cách buộc Quét (câu trả lời thứ 3 của Adam Haines có thông tin tốt): social .msdn.microsoft.com / đàn / en-US / transactsql / thread / ...
Solomon Rutzky

1
Trước hết, tìm kiếm tốt cho các truy vấn giống như điểm. Thứ hai, quét tốt cho các truy vấn phạm vi nơi phải truy xuất nhiều dữ liệu. Hệ thống OLAP sẽ không hoạt động tốt nếu không quét. Hệ thống OLTP sẽ không hoạt động tốt nếu không tìm kiếm. Mọi thứ đều có vị trí của nó trong chương trình lớn của sự vật ...
darlove

0

Tôi biết đã khá lâu kể từ khi bắt đầu ... Có rất nhiều sự khôn ngoan trong tất cả những câu trả lời này. Lập chỉ mục tốt là điều đầu tiên khi cố gắng cải thiện một truy vấn. Chà, gần như là người đầu tiên. Điều đầu tiên nhất (có thể nói) là thực hiện các thay đổi đối với mã để nó hiệu quả. Vì vậy, sau khi tất cả đã được nói và thực hiện, nếu có một truy vấn không có WHERE hoặc khi điều kiện WHERE không đủ chọn lọc, chỉ có một cách để lấy dữ liệu: QUÉT BẢNG (INDEX SCAN). Nếu một người cần tất cả các cột từ một bảng, thì TABLE SCAN sẽ được sử dụng - không có câu hỏi về nó. Đây có thể là quét đống hoặc quét chỉ mục theo cụm, tùy thuộc vào kiểu tổ chức dữ liệu. Cách cuối cùng duy nhất để tăng tốc mọi thứ (nếu có thể), là đảm bảo rằng càng nhiều lõi càng tốt để thực hiện quá trình quét: OPTION (MAXDOP 0). Tất nhiên, tôi đang bỏ qua chủ đề lưu trữ,

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.