优化超过 25 万行的查询

Ati*_*ieh 11 performance sql-server sql-server-2012 performance-tuning

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

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

需要的列:TABLE, FIELD, AFTER, DATE, 并且每个DATE和上都有一个索引TABLE

在创建一个只有我需要的字段的临时表后,它下降到 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'
Run Code Online (Sandbox Code Playgroud)

运行此 ->(216598 行受影响)

由于并非所有查询都依赖于日期范围,因此我没有将其包含在查询中。问题是只插入. 上面的插入花了1:19 分钟

我想为几个查询运行这样的事情:

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

插入的问题多于选择的问题,但临时表的行数比原始表少,这可能比多次遍历表要好。

我怎样才能优化这个?

编辑

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

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

我正在使用 SQL Server 2012。

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

name    rows        reserved    data        index_size  unused
mytbl   24869658    9204568 KB  3017952 KB  5816232 KB  370384 KB
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 12

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

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

删除冗余投影并添加假定dbo模式:

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

如果没有像([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';
Run Code Online (Sandbox Code Playgroud)

还可以考虑索引压缩(如果可用且有益)。如果没有某种新索引,您几乎无法提高给定查询的性能。


wBo*_*Bob 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
Run Code Online (Sandbox Code Playgroud)

我的结果,6 秒 v 0.08 秒:

在此处输入图片说明

总之,尝试与您的老板建立一个案例来更改索引,或者至少创建某种夜间流程,将这些记录分割到只读报告表/数据库中,您可以在其中完成工作,并添加索引适合该工作量。