it-swarm.com.de

Verbessern Sie die SQL Server-Abfrageleistung für große Tabellen

Ich habe eine relativ große Tabelle (derzeit 2 Millionen Datensätze) und würde gerne wissen, ob es möglich ist, die Leistung für Ad-hoc-Abfragen zu verbessern. Das Wort ad-hoc hier der Schlüssel. Das Hinzufügen von Indizes ist keine Option (es gibt bereits Indizes für die Spalten, die am häufigsten abgefragt werden).

Ausführen einer einfachen Abfrage, um die 100 zuletzt aktualisierten Datensätze zurückzugeben:

select top 100 * from ER101_ACCT_ORDER_DTL order by er101_upd_date_iso desc

Dauert einige Minuten Siehe Ausführungsplan unten:

enter image description here

Zusätzliches Detail aus dem Tabellenscan:

enter image description here

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

Der Server ist ziemlich leistungsfähig (von 48 GB RAM, 24-Core-Prozessor), auf dem SQL Server 2008 R2 x64 ausgeführt wird.

Update

Ich habe diesen Code gefunden, um eine Tabelle mit 1.000.000 Datensätzen zu erstellen. Ich dachte, ich könnte dann SELECT TOP 100 * FROM testEnvironment ORDER BY mailAddress DESC auf ein paar verschiedenen Servern ausführen, um herauszufinden, ob meine Festplattenzugriffsgeschwindigkeiten auf dem Server schlecht sind.

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;

Auf den drei Testservern lief die Abfrage jedoch fast sofort ab. Kann das jemand erklären?

enter image description here

Update 2

Vielen Dank für die Kommentare. Bitte warten Sie, bis sie kommen. Sie veranlaßten mich, den Primärschlüsselindex von nicht gruppierten zu gruppierten mit ziemlich interessanten (und unerwarteten?) Ergebnissen zu ändern.

Nicht gruppiert:

enter image description here

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

Gruppiert:

enter image description here

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

Wie ist das möglich? Wie kann ein Clustered-Index-Scan ohne Index in der Spalte er101_upd_date_iso verwendet werden?

Update 3

Wie gewünscht - dies ist das Create Table-Skript:

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]

Die Tabelle ist 2,8 GB groß und die Indexgröße liegt bei 3,9 GB.

80
Lee Tickett

Einfache Antwort: NEIN. Sie können Ad-hoc-Abfragen für eine Tabelle mit 238 Spalten mit einem Füllfaktor von 50% für den Clustered-Index nicht unterstützen.

Ausführliche Antwort:

Wie ich in anderen Antworten zu diesem Thema festgestellt habe, ist Indexdesign sowohl Kunst als auch Wissenschaft, und es gibt so viele Faktoren, die berücksichtigt werden müssen, dass es nur wenige, wenn überhaupt, feste und schnelle Regeln gibt. Sie müssen Folgendes berücksichtigen: Das Volumen der DML-Operationen im Vergleich zu SELECTs, das Festplattensubsystem, andere Indizes/Trigger in der Tabelle, die Verteilung der Daten in der Tabelle, Abfragen mit SARGable WHERE-Bedingungen und einige andere Dinge, an die ich mich nicht einmal richtig erinnern kann jetzt.

Ich kann sagen, dass bei Fragen zu diesem Thema keine Hilfe geleistet werden kann, ohne die Tabelle selbst, ihre Indizes, Trigger usw. zu verstehen. Nun, da Sie die Tabellendefinition gepostet haben (noch auf die Indizes warten, aber nur auf die Tabellendefinition) 99% des Problems) Ich kann einige Vorschläge unterbreiten.

Erstens, wenn die Tabellendefinition korrekt ist (238 Spalten, 50% Füllfaktor), können Sie den Rest der Antworten/Ratschläge hier so ziemlich ignorieren ;-). Es tut mir leid, dass ich hier weniger als politisch bin, aber im Ernst, es ist eine wilde Gänsejagd, ohne die Einzelheiten zu kennen. Und jetzt, da wir die Tabellendefinition sehen, wird deutlich, warum eine einfache Abfrage so lange dauern würde, selbst wenn die Testabfragen (Update Nr. 1) so schnell ausgeführt wurden.

Das Hauptproblem hier (und in vielen Situationen mit schlechter Leistung) ist die schlechte Datenmodellierung. 238 Spalten sind nicht verboten, genauso wie 999 Indizes nicht verboten sind, aber es ist auch im Allgemeinen nicht sehr klug.

Empfehlungen:

  1. Erstens muss dieser Tisch wirklich umgestaltet werden. Wenn dies eine Data Warehouse-Tabelle ist, müssen diese Felder in mehrere Tabellen aufgeteilt werden, die alle dieselbe PK haben können. Sie hätten eine Stammdatensatztabelle und die untergeordneten Tabellen sind nur abhängige Informationen, die auf häufig zugeordneten Attributen basieren, und die PK dieser Tabellen ist dieselbe wie die PK der Mastertabelle und daher auch die FK der Mastertabelle. Zwischen dem Master und allen untergeordneten Tabellen besteht eine 1: 1-Beziehung.
  2. Die Verwendung von ANSI_PADDING OFF ist störend, ganz zu schweigen von der Inkonsistenz innerhalb der Tabelle aufgrund der verschiedenen Spaltenzusätze im Laufe der Zeit. Wir sind uns nicht sicher, ob Sie das jetzt beheben können, aber im Idealfall hätten Sie immer ANSI_PADDING ON oder zumindest die gleiche Einstellung für alle ALTER TABLE -Anweisungen.
  3. Ziehen Sie in Betracht, zwei zusätzliche Dateigruppen zu erstellen: Tabellen und Indizes. Es ist am besten, Ihre Daten nicht in PRIMARY abzulegen, da SQL SERVER dort alle Daten und Metadaten zu Ihren Objekten speichert. Sie erstellen Ihre Tabelle und Ihren gruppierten Index (da dies die Daten für die Tabelle sind) in [Tables] und alle nicht gruppierten Indizes in [Indexes].
  4. Erhöhen Sie den Füllfaktor von 50%. Diese niedrige Zahl ist wahrscheinlich der Grund dafür, dass Ihr Indexbereich größer ist als Ihr Datenbereich. Bei einer Index-Neuerstellung werden die Datenseiten mit maximal 4 KB (von der gesamten Seitengröße von 8 KB) für Ihre Daten neu erstellt, sodass Ihre Tabelle über einen großen Bereich verteilt ist.
  5. Wenn die meisten oder alle Abfragen "ER101_ORG_CODE" in der Bedingung WHERE enthalten, sollten Sie dies in die führende Spalte des Clustered-Index verschieben. Angenommen, es wird häufiger als "ER101_ORD_NBR" verwendet. Wenn "ER101_ORD_NBR" häufiger verwendet wird, behalten Sie es bei. Unter der Annahme, dass die Feldnamen "OrganizationCode" und "OrderNumber" bedeuten, scheint "OrgCode" eine bessere Gruppierung zu sein, in der möglicherweise mehrere "OrderNumbers" enthalten sind.
  6. Kleiner Punkt, aber wenn "ER101_ORG_CODE" immer 2 Zeichen enthält, verwenden Sie CHAR(2) anstelle von VARCHAR(2), da ein Byte im Zeilenkopf gespeichert wird, das die Größe der variablen Breite verfolgt und sich über Millionen von Zeilen summiert.
  7. Wie andere hier bereits erwähnt haben, beeinträchtigt die Verwendung von SELECT * die Leistung. Dies liegt nicht nur daran, dass SQL Server alle Spalten zurückgeben muss und daher mit größerer Wahrscheinlichkeit unabhängig von Ihren anderen Indizes einen Clustered-Index-Scan durchführt, sondern auch daran, dass SQL Server Zeit benötigt, um zur Tabellendefinition zu wechseln und * in zu übersetzen alle Spaltennamen. Es sollte etwas schneller sein, alle 238 Spaltennamen in der Liste SELECT anzugeben, aber das hilft dem Scan-Problem nicht. Aber brauchen Sie überhaupt alle 238 Spalten gleichzeitig?

Viel Glück!

UPDATE
Der Vollständigkeit halber zu der Frage "Wie kann die Leistung einer großen Tabelle für Ad-hoc-Abfragen verbessert werden?" Sollte angemerkt werden, dass dies in diesem speziellen Fall zwar nicht hilfreich ist, WENN jemand SQL Server verwendet 2012 (oder später) und WENN die Tabelle nicht aktualisiert wird, ist die Verwendung von Columnstore-Indizes eine Option. Weitere Informationen zu dieser neuen Funktion finden Sie hier: http://msdn.Microsoft.com/en-us/library/gg492088.aspx (Ich glaube, diese wurden so erstellt, dass sie ab SQL Server aktualisierbar sind 2014).

UPDATE 2
Weitere Überlegungen sind:

  • Aktivieren Sie die Komprimierung für den Clustered Index. Diese Option wurde in SQL Server 2008 verfügbar, jedoch nur als Enterprise Edition-Funktion. Ab SQL Server 2016 SP1 wurde die Datenkomprimierung jedoch verfügbar gemacht in allen Editionen ! Weitere Informationen zur Zeilen- und Seitenkomprimierung finden Sie auf der MSDN-Seite für Datenkomprimierung .
  • Wenn Sie die Datenkomprimierung nicht verwenden können oder wenn sie für eine bestimmte Tabelle keinen großen Nutzen bringt, haben Sie eine Spalte mit fester Länge (INT, BIGINT, TINYINT, SMALLINT, CHAR, NCHAR, BINARY, DATETIME, SMALLDATETIME, MONEY usw.) und weit über 50 % der Zeilen sind NULL. Aktivieren Sie die Option SPARSE, die in SQL Server 2008 verfügbar wurde. Weitere Informationen finden Sie auf der MSDN-Seite unter se Sparse Columns .
53
Solomon Rutzky

Bei dieser Abfrage gibt es einige Probleme (und dies gilt für jede Abfrage).

Mangel an Index

Das Fehlen eines Index für die Spalte er101_upd_date_iso ist am wichtigsten, da Oded bereits erwähnt hat.

Ohne übereinstimmenden Index (der fehlende Tabellenscan könnte dazu führen), besteht keine Möglichkeit, schnelle Abfragen für große Tabellen auszuführen.

Wenn Sie keine Indizes hinzufügen können (aus verschiedenen Gründen, einschließlich , ist es nicht sinnvoll, nur für eine Ad-hoc-Abfrage einen Index zu erstellen). Ich würde einige Problemumgehungen vorschlagen (die für Ad-hoc-Abfragen verwendet werden können):

1. Verwenden Sie temporäre Tabellen

Erstellen Sie eine temporäre Tabelle für die Teilmenge (Zeilen und Spalten) der Daten, an denen Sie interessiert sind . Die temporäre Tabelle sollte viel kleiner als die ursprüngliche Quelltabelle sein, kann leicht indiziert werden (falls erforderlich) und kann zwischengespeicherte Teilmenge der Daten du bist interessiert in.

Um eine temporäre Tabelle zu erstellen, können Sie Code verwenden (nicht getestet):

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

Pros:

  • Einfach für jeden Teil der Daten.
  • Einfach zu verwalten - es ist temporär und es ist table.
  • Beeinflusst die allgemeine Systemleistung nicht wie view.
  • Temporäre Tabelle kann indiziert werden.
  • Sie müssen sich nicht darum kümmern - es ist vorübergehend :).

Nachteile:

  • Es ist eine Momentaufnahme der Daten - aber wahrscheinlich ist dies für die meisten Ad-hoc-Abfragen ausreichend.

2. Allgemeiner Tabellenausdruck - CTE

Ich persönlich benutze CTE viel mit Ad-hoc-Abfragen - es hilft viel beim Erstellen (und Testen) einer Abfrage Stück für Stück.

Siehe Beispiel unten (die Abfrage beginnt mit WITH).

Pros:

  • Einfach zu bauen, beginnend mit big view und dann auswählen und filtern, was Sie wirklich brauchen.
  • Einfach zu testen.

Nachteile:

  • Einige Leute mögen CDE nicht - CDE-Abfragen scheinen lang und schwer zu verstehen.

3. Ansichten erstellen

Ähnlich wie oben, aber erstellen Sie Ansichten anstelle von temporären Tabellen (wenn Sie häufig mit den gleichen Abfragen spielen und Sie eine MS SQL-Version haben, die indizierte Ansichten unterstützt.

Sie können Ansichten oder indizierte Ansichten auf eine Teilmenge der Daten, für die Sie sich interessieren Erstellen, und Abfragen auf View ausführen - die nur interessante Teilmengen von Daten enthalten sollte, die viel kleiner als die gesamte Tabelle sind.

Pros:

  • Leicht zu schaffen.
  • Es ist auf dem neuesten Stand mit den Quelldaten.

Nachteile:

  • Nur für definierte Datenuntermenge möglich.
  • Könnte bei großen Tabellen mit hoher Aktualisierungsrate ineffizient sein.
  • Nicht so einfach zu handhaben.
  • Kann die allgemeine Systemleistung beeinträchtigen.
  • Ich bin nicht sicher, ob indizierte Ansichten in jeder Version von MS SQL verfügbar sind.

Alle Spalten auswählen

Star-Abfrage (SELECT * FROM) für große Tabellen auszuführen ist keine gute Sache ...

Wenn Sie große Spalten (wie lange Zeichenfolgen) haben, dauert es lange, sie von der Festplatte zu lesen und über das Netzwerk zu übergeben.

Ich würde versuchen, * durch Spaltennamen zu ersetzen, die Sie wirklich benötigen.

Oder, wenn Sie alle Spalten benötigen, versuchen Sie, die Abfrage mit etwas wie (mit common data expression) neu zu schreiben:

;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 

Dirty liest

Das Letzte, was die Ad-hoc-Abfrage beschleunigen könnte, ist dirty reads mit table-Hinweis WITH (NOLOCK) .

Anstelle eines Hinweises können Sie Transaktionsisolationsstufe festlegen nicht festgeschrieben lesen:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

oder legen Sie die richtigen Einstellungen für SQL Management Studio fest.

Ich gehe davon aus, dass für Ad-hoc-Abfragen dirty reads gut genug ist.

50

Sie erhalten dort einen table-Scan, was bedeutet, dass kein für er101_upd_date_iso definierter Index definiert ist. Wenn diese Spalte Teil eines vorhandenen Index ist, kann der Index nicht verwendet werden (möglicherweise ist er vorhanden) nicht die primäre Indexer-Spalte).

Durch das Hinzufügen fehlender Indizes wird die Leistung ohne Ende verbessert.

es gibt bereits Indizes für die Spalten, die am häufigsten abgefragt werden

Das bedeutet nicht, dass sie in dieser Abfrage verwendet werden (und wahrscheinlich auch nicht).

Ich schlage vor, die Ursachen für schlechte Leistung in SQL Server durch Gail Shaw zu finden, Teil 1 und Teil 2 .

12
Oded

In der Frage wird speziell angegeben, dass die Leistung für Ad-hoc-Abfragen verbessert werden muss und dass keine Indizes hinzugefügt werden können. Was kann man also tun, um die Leistung auf jedem Tisch zu verbessern?

Da Ad-hoc-Abfragen berücksichtigt werden, können die WHERE-Klausel und die ORDER BY-Klausel eine beliebige Kombination von Spalten enthalten. Dies bedeutet, dass unabhängig davon, welche Indizes in der Tabelle platziert sind, einige Abfragen einen Tabellenscan erfordern, wie oben im Abfrageplan einer Abfrage mit schlechter Leistung dargestellt.

Nehmen wir unter Berücksichtigung dessen an, dass die Tabelle bis auf einen Clustered-Index für den Primärschlüssel überhaupt keine Indizes enthält. Überlegen wir uns nun, welche Optionen wir haben, um die Leistung zu maximieren.

  • Defragmentieren Sie die Tabelle

    Solange wir einen Clustered-Index haben, können wir die Tabelle mit DBCC INDEXDEFRAG (veraltet) oder vorzugsweise ALTER INDEX defragmentieren. Dies minimiert die Anzahl der zum Durchsuchen der Tabelle erforderlichen Festplattenlesevorgänge und verbessert die Geschwindigkeit.

  • Verwenden Sie die schnellstmöglichen Datenträger. Sie sagen nicht, welche Festplatten Sie verwenden, aber wenn Sie SSDs verwenden können.

  • Optimieren Sie Tempdb. Legen Sie tempdb auf die schnellstmöglichen Festplatten, wieder auf SSDs. Siehe diesen SO-Artikel und diesen RedGate-Artikel .

  • Wie in anderen Antworten angegeben, gibt die Verwendung einer selektiveren Abfrage weniger Daten zurück und sollte daher schneller sein.

Betrachten wir nun, was wir tun können, wenn wir Indizes hinzufügen dürfen.

Wenn wir nicht über Ad-hoc-Abfragen sprechen, würden wir Indizes speziell für die begrenzte Menge von Abfragen hinzufügen, die für die Tabelle ausgeführt werden. Da wir ad-hoc Abfragen diskutieren, was kann getan werden, um die Geschwindigkeit zu verbessern meistens der Zeit?

  • Fügen Sie jeder Spalte einen einzelnen Spaltenindex hinzu. Dies sollte SQL Server zumindest die Möglichkeit geben, die Geschwindigkeit für die meisten Abfragen zu verbessern, ist jedoch nicht optimal.
  • Fügen Sie spezifische Indizes für die häufigsten Abfragen hinzu, damit diese optimiert werden.
  • Fügen Sie nach Bedarf zusätzliche spezifische Indizes hinzu, indem Sie Abfragen mit schlechter Leistung überwachen.

Bearbeiten

Ich habe einige Tests an einem 'großen' Tisch mit 22 Millionen Zeilen durchgeführt. Meine Tabelle hat nur sechs Spalten, enthält aber 4 GB Daten. Mein Computer ist ein seriöser Desktop mit 8 GByteRAMund einer Quad-Core-CPU und verfügt über eine einzelne Agility 3-SSD.

Ich habe alle Indizes mit Ausnahme des Primärschlüssels in der ID-Spalte entfernt.

Eine ähnliche Abfrage wie die in der Frage angegebene dauert 5 Sekunden, wenn der SQL Server zuerst und anschließend 3 Sekunden neu gestartet wird. Der Datenbankoptimierungsratgeber empfiehlt offensichtlich, einen Index hinzuzufügen, um diese Abfrage zu verbessern, mit einer geschätzten Verbesserung von> 99%. Das Hinzufügen eines Index führt zu einer Abfragezeit von praktisch Null.

Interessant ist auch, dass mein Abfrageplan mit Ihrem identisch ist (mit dem Clustered-Index-Scan), der Index-Scan jedoch 9% der Abfragekosten und der Sortiervorgang die restlichen 91% ausmacht. Ich kann nur davon ausgehen, dass Ihre Tabelle eine enorme Datenmenge enthält und/oder Ihre Festplatten sehr langsam sind oder sich über eine sehr langsame Netzwerkverbindung befinden.

7
Phil

Wie ist das möglich? Wie kann ein Clustered-Index-Scan ohne Index in der Spalte er101_upd_date_iso verwendet werden?

Ein Index ist ein B-Tree, bei dem jeder Blattknoten auf eine Reihe von Zeilen verweist (in der internen SQL-Terminologie als "Seite" bezeichnet). Dies ist der Fall, wenn der Index ein nicht gruppierter Index ist. 

Clustered-Index ist ein Sonderfall, in dem die Blattknoten die 'Reihe von Zeilen' haben (anstatt auf sie zu zeigen). deswegen...

1) Es kann nur einen Clusterindex für die Tabelle geben. 

dies bedeutet auch, dass die gesamte Tabelle als gruppierter Index gespeichert wird. Aus diesem Grund wurde der Index-Scan anstelle eines Tabellenscan angezeigt.

2) Eine Operation, die Clustered-Index verwendet, ist im Allgemeinen schneller als ein Non-Clustered-Index

Lesen Sie mehr unter http://msdn.Microsoft.com/de-de/library/ms177443.aspx

Für das Problem, das Sie haben, sollten Sie wirklich in Betracht ziehen, diese Spalte einem Index hinzuzufügen, da das Hinzufügen eines neuen Index (oder einer Spalte zu einem vorhandenen Index) die INSERT/UPDATE-Kosten erhöht. Es kann jedoch möglich sein, einen nicht ausgelasteten Index (oder eine Spalte aus einem vorhandenen Index) zu entfernen, der durch 'er101_upd_date_iso' ersetzt wird.

Wenn Indexänderungen nicht möglich sind, empfehle ich, Statistiken für die Spalte hinzuzufügen. Dies kann die Dinge beschleunigen, wenn die Spalten eine gewisse Korrelation mit indizierten Spalten aufweisen

http://msdn.Microsoft.com/de-de/library/ms188038.aspx

Übrigens, Sie werden viel mehr Hilfe erhalten, wenn Sie das Tabellenschema von ER101_ACCT_ORDER_DTL . Und die vorhandenen Indizes auch buchen können.

2
shankar_pratap

Selbst wenn Sie Indizes für einige Spalten haben, die in einigen Abfragen verwendet werden, zeigt die Tatsache, dass Ihre Ad-hoc-Abfrage eine Tabellensuche verursacht, dass Sie nicht über ausreichende Indizes verfügen, um diese Abfrage effizient abschließen zu können.

Insbesondere für Datumsbereiche ist es schwierig, gute Indizes hinzuzufügen.

Wenn Sie Ihre Abfrage betrachten, muss die Datenbank alle Datensätze nach der ausgewählten Spalte sortieren, um die ersten n Datensätze zurückgeben zu können. 

Führt die Datenbankbibliothek auch einen vollständigen Tabellenscan ohne die order by-Klausel durch? Hat die Tabelle einen Primärschlüssel - ohne PK muss die Datenbank mehr arbeiten, um die Sortierung durchzuführen?

2
foamdino

Einer der Gründe, warum Ihr 1M-Test schneller lief, ist wahrscheinlich, weil sich die temporären Tabellen vollständig im Speicher befinden und nur dann auf die Festplatte gehen, wenn auf Ihrem Server Speicherdruck auftritt. Sie können Ihre Abfrage entweder erneut erstellen, um die Reihenfolge zu entfernen, einen guten Clustered-Index und einen oder mehrere Covering-Index (e) hinzufügen, oder die DMV abfragen, um zu prüfen, ob der Druck auf IO feststellt, ob Hardware-bezogen ist.

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

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

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

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

Ich weiß, dass es eine ganze Weile seit dem Anfang ist ... In all diesen Antworten steckt viel Weisheit. Eine gute Indizierung ist das Erste, wenn Sie versuchen, eine Abfrage zu verbessern. Nun, fast der erste. Die allererste (sozusagen) macht Änderungen am Code, damit er effizient ist. Nach allem, was gesagt und getan wurde, wenn man eine Abfrage ohne WHERE hat oder wenn die WHERE-Bedingung nicht selektiv genug ist, gibt es nur einen Weg, um die Daten zu erhalten: TABLE SCAN (INDEX SCAN). Benötigt man alle Spalten aus einer Tabelle, wird TABLE SCAN verwendet - keine Frage. Dies kann je nach Art der Datenorganisation ein Heap-Scan oder Cluster-Index-Scan sein. Der letzte Weg, um die Sache zu beschleunigen (wenn überhaupt möglich), ist sicherzustellen, dass so viele Kerne wie möglich für den Scan verwendet werden: OPTION (MAXDOP 0). Ich ignoriere natürlich das Thema Speicher, aber man sollte sicherstellen, dass man über unbegrenzten RAM verfügt, was selbstverständlich ist :)

0
darlove

Ich weiß, dass Sie gesagt haben, dass das Hinzufügen von Indizes keine Option ist, aber dies wäre die einzige Option, um den Tabellenscan zu beseitigen, den Sie haben. Wenn Sie einen Scan durchführen, liest SQL Server alle 2 Millionen Zeilen in der Tabelle, um Ihre Abfrage auszuführen. 

dieser Artikel enthält weitere Informationen, aber denken Sie daran: Suchen = gut, Scannen = schlecht.

Zweitens, können Sie select * nicht entfernen und nur die Spalten auswählen, die Sie benötigen? Drittens keine "wo" -Klausel? Selbst wenn Sie einen Index haben, da Sie alles lesen, ist das Beste, was Sie erhalten, ein Index-Scan (der besser ist als ein Tabellenscan, aber es ist kein Suchvorgang, auf den Sie abzielen sollten).

0
Diego