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:
Zusätzliches Detail aus dem Tabellenscan:
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?
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:
SQL Server Execution Times:
CPU time = 3634 ms, elapsed time = 154179 ms.
Gruppiert:
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.
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:
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.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]
.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.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.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:
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 .Bei dieser Abfrage gibt es einige Probleme (und dies gilt für jede Abfrage).
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):
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:
view
.Nachteile:
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:
Nachteile:
Ä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:
Nachteile:
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
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.
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 .
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?
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.
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.
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?
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
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 :)
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).