优化25+百万行的查询


11

我正在使用MS SQL,并且必须在同一表上以不同条件运行多个查询。最初,我在原始表上运行了每个查询,尽管它们都共享一些过滤条件(即日期,状态)。这花费了很多时间(大约2分钟)。

数据行中有重复项,并且所有索引都是非聚集的。我只对4列的标准感兴趣,并且结果应仅输出所有查询的计数。

列需要:TABLEFIELDAFTERDATE,并且对每一个的索引DATETABLE

在仅创建了我需要的字段的临时表之后,它降到了1:40分钟,这仍然非常糟糕。

CREATE TABLE #TEMP
(
    TABLE VARCHAR(30) NULL,
    FIELD VARCHAR(30) NULL,
    AFTER VARCHAR(1000) NULL,
    DATE DATETIME,
    SORT_ID INT IDENTITY(1,1)
)
CREATE CLUSTERED INDEX IX_ADT ON #TEMP(SORT_ID)

INSERT INTO #TEMP (TABLE, FIELD, AFTER, DATE)
    SELECT TABLE, FIELD, AFTER, DATE 
    FROM mytbl WITH (NOLOCK)
    WHERE TABLE = 'OTB' AND
    FIELD = 'STATUS'

Runnig this->(受影响的216598行)

由于并非所有查询都依赖日期范围,因此我没有将其包括在查询中。问题在于,仅插入需要1分钟以上的时间。上面的插入内容花费了1:19分钟

我想对几个查询运行这样的内容:

SELECT COUNT(*) AS COUNT
FROM #TEMP
WHERE AFTER = 'R' AND
DATE >= '2014-01-01' AND
DATE <= '2015-01-01'

插入问题多于选择问题,但是temp的行数远少于原始表,这可能比遍历该表要好得多。

我该如何优化呢?

编辑

我删除了排序ID,我认为问题主要出在选择而不是插入。这是一个猜测。

由于没有唯一的字段或行,因此无法在任何索引上创建唯一的。

我正在使用SQL Server 2012。

表信息:它是一个堆,具有以下空间使用量:

name    rows        reserved    data        index_size  unused
mytbl   24869658    9204568 KB  3017952 KB  5816232 KB  370384 KB

@MikaelEriksson我不能修改生产表..
阿提埃赫

如果您要优化的查询形式为SELECT COUNT(*) AS COUNT FROM original_table WHERE AFTER = 'R' AND DATE >= '2014-01-01' AND DATE < '2015-01-01',为什么不尝试分别优化每个(查询)?您是否不允许向表添加索引?
ypercubeᵀᴹ

2
您需要确定速度慢的原因。被阻止了吗?它在等待tempdb增长吗?执行计划糟糕透顶吗?没有更多的人,没有人能解决“我的查询很慢” ...
亚伦·贝特朗

3
好吧,这似乎对我来说是一个失败的原因(“不允许我进行任何优化,因此,每次需要运行一些查询时,只需在临时表中推送200K行即可”)。但是您可以从表中删除TABLEFIELD#temp(毕竟所有行都具有TABLE = 'OTB' AND FIELD = 'STATUS'特定的临时表。)
ypercubeᵀᴹ2015年

2
我确实要求通过添加详细(和礼貌)的评论来进行修改和改进。这就是评论的目的。您还应该使用正在使用的SQL Server版本(例如SQL Server 2014)标记问题。该表的DDL可能也有帮助(CREATE TABLE语句)。否决票是因为问题不清楚。
保罗·怀特9

Answers:


12

问题主要是关于如何优化选择语句:

SELECT [TABLE], [FIELD], [AFTER], [DATE]
FROM mytbl WITH (NOLOCK)
WHERE [TABLE] = 'OTB' AND
[FIELD] = 'STATUS'

删除多余的投影并添加假定的dbo架构:

SELECT [AFTER], [DATE] 
FROM dbo.mytbl WITH (NOLOCK)
WHERE [TABLE] = 'OTB'
AND FIELD = 'STATUS';

没有索引之类的([TABLE],[FIELD]) INCLUDE ([AFTER],[DATE])SQL Server有两个主要选项:

  1. 完全扫描堆(超过3GB);要么
  2. 找到与[TABLE] = 'OTB'和匹配的行[FIELD] = 'STATUS'(使用IDX6),然后对每行执行堆(RID)查找以检索[AFTER][DATE]列。

优化器是选择堆扫描还是通过RID查找进行索引查找取决于对[TABLE] = 'OTB'[FIELD] = 'STATUS'谓词的估计选择性。检查以了解搜索中的估计行数是否与实际匹配。如果不是,请更新您的统计信息。如果该条件具有一定的选择性使用表提示测试查询以强制使用索引。如果优化器当前正在选择索引查找,请使用INDEX(0)FORCESCAN提示来测试性能以扫描堆。

除此之外,您还可以通过删除一些未使用的空间(370MB)来改善对堆的扫描。在SQL Server 2008中,可以通过重建堆来完成。堆中未使用的空间通常是由于没有执行表锁而执行的删除操作导致的(没有表锁,不会从堆中释放空页)。因此,经常删除的表通常最好存储为群集表。

堆扫描的性能取决于表中有多少表存储在内存中,必须从磁盘读取多少表,页面有多满,持久性存储的速度,扫描是受I / O还是CPU约束(并行性可以提供帮助)。

在研究完上述所有内容后,如果性能仍然不可接受,请尝试为新索引辩护。如果在您的SQL Server版本上可用,则给定查询的可能筛选索引为:

CREATE INDEX index_name
ON dbo.mytbl ([DATE],[AFTER])
WHERE [TABLE] = 'OTB'
AND [FIELD] = 'STATUS';

如果可行并且还考虑索引压缩。如果没有某种新的索引,那么您只能做很少的事情来改善给定查询的性能。


对不起保罗,有:IDX6 nonclustered located on PRIMARY TABLE, FIELD。也许这会改变您提到的事情?
Atieh 2015年

6

我认为这里有一种更改索引的情况,因为:

  • 您有任务要做(这些多个查询)
  • 数据仓库量(25+百万行)和
  • 性能问题。

这对于SQL Server 2012中引入的非聚集列存储索引也将是一个很好的用例,例如,在具有许多列的大表上汇总/聚集一些列。

尽管这些索引具有使表变为只读(分区切换除外)的副作用,但它们可以在适当的条件下改变聚合查询的性能。可以通过将索引或简单的分区切换数据拖放并重新创建到表中来管理只读方面。

我设置了一个简单的测试平台来模拟您的设置,并看到了性能上的良好改进:

USE tempdb
GO

SET NOCOUNT ON
GO

-- Create a large table
IF OBJECT_ID('dbo.largeTable') IS NOT NULL
DROP TABLE dbo.largeTable
GO
CREATE TABLE dbo.largeTable ( 

    [TABLE] VARCHAR(30) NULL,
    FIELD VARCHAR(30) NULL,
    [AFTER] VARCHAR(1000) NULL,
    [DATE] DATETIME,
    SORT_ID INT IDENTITY(1,1),

    pad VARCHAR(100) DEFAULT REPLICATE( '$', 100 )
)
GO

-- Populate table
;WITH cte AS (
SELECT TOP 100000 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.largeTable ( [TABLE], FIELD, [AFTER], [DATE] )
SELECT 
    x.tableName, 
    y.field,
    z.[after],
    DATEADD( day, rn % 1111, '1 Jan 2012' )
FROM cte c
    CROSS JOIN ( VALUES ( 'OTB' ), ( 'AAA' ), ( 'BBB' ), ( 'CCCC' ) ) x ( tableName )
    CROSS JOIN ( VALUES ( 'STATUS' ), ( 'TIME' ), ( 'POWER' ) ) y ( field )
    CROSS JOIN ( VALUES ( 'R' ), ( 'X' ), ( 'Z' ), ( 'A' ) ) z ( [after] )

CHECKPOINT

GO 5

EXEC sp_spaceused 'dbo.largeTable'
GO

SELECT MIN([DATE]) xmin, MAX([DATE]) xmax, FORMAT( COUNT(*), '#,#' ) records
FROM dbo.largeTable
GO

-- Optionally clear cache for more comparable results; DO NOT RUN ON PRODUCTION SYSTEM!!
--DBCC DROPCLEANBUFFERS
--DBCC FREEPROCCACHE
--GO

DECLARE @startDate DATETIME2 = SYSUTCDATETIME()

SELECT COUNT(*) AS COUNT
FROM dbo.largeTable
WHERE [AFTER] = 'R' 
  AND [DATE] >= '2014-01-01' 
  AND [DATE] <= '2015-01-01'

SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff1
GO

-- Add the non-clustered columnstore
CREATE NONCLUSTERED COLUMNSTORE INDEX _cs ON dbo.largeTable ( [TABLE], FIELD, [AFTER], [DATE] )
GO

-- Optionally clear cache for more comparable results; DO NOT RUN ON PRODUCTION SYSTEM!!
--DBCC DROPCLEANBUFFERS
--DBCC FREEPROCCACHE
--GO

-- Check query again
DECLARE @startDate DATETIME2 = SYSUTCDATETIME()

SELECT COUNT(*) AS COUNT
FROM dbo.largeTable
WHERE [AFTER] = 'R' 
  AND [DATE] >= '2014-01-01' 
  AND [DATE] <= '2015-01-01'

SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff2
GO

我的结果是6秒v 0.08秒:

在此处输入图片说明

总而言之,尝试与老板一起建立案例以更改索引,或者至少创建某种通宵的过程,在这些过程中,将这些记录划分到一个只读的报告表/数据库中,您可以在其中进行工作并添加索引适合该工作量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.