【问题标题】:Sometime SQL Server Select Query is too slow有时 SQL Server 选择查询太慢
【发布时间】:2021-06-19 04:17:15
【问题描述】:

我有一个这样的表,它有超过 700 万条记录:

CREATE TABLE [dbo].[Test]
(
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [UUID] [nvarchar](100) NOT NULL,
    [FirstName] [nvarchar](50) NULL,
    [LastName] [nvarchar](50) NULL,
    [AddrLine1] [nvarchar](100) NULL,
    [AddrLine2] [nvarchar](100) NULL,
    [City] [nvarchar](50) NULL,
    [Prov] [nvarchar](10) NULL,
    [Postal] [nvarchar](10) NULL,
    [DateAdded] [datetime] NULL,

    CONSTRAINT [PK_Test] 
        PRIMARY KEY CLUSTERED ([Id] ASC)
                    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
                          ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

现在,系统每天下午都会运行以下选择查询。有趣的是,有时同一个查询非常慢,大约需要 4 分钟。第二次或其他时间,相同的查询非常快,不到一秒。

查询:

WITH testquery AS 
(
    SELECT TOP 1 
        'Matched' as location,Firstname, LastName, 
        AddrLine1, AddrLine2, City,  Prov, Postal 
    FROM 
        [Test] 
    WHERE 
        UUID = 'BLABLABLABLABLABLABLABLABLA' 
    ORDER BY 
        DateAdded DESC
),
defaults AS 
(
    SELECT
        'Rejected' AS location, NULL AS Firstname, NULL AS LastName, 
        NULL AS AddrLine1, NULL AS AddrLine2, NULL AS City, NULL AS Prov, 
        NULL AS Postal 
)
SELECT * 
FROM testquery

UNION ALL

SELECT * 
FROM defaults
WHERE NOT EXISTS (SELECT * FROM testquery);

有人可以帮忙吗?

注意事项:

  • 我有一项服务,每天早上向表中添加大约 1000 条新记录。
  • [avg_fragmentation_in_percent] 为 0.01
  • 如果我有同一个人但地址不同,UUID 可能会重复。
  • 该表没有同时在其他地方使用。
  • 数据库没有同时忙于其他查询。我使用“sys.dm_exec_requests”进行了检查

【问题讨论】:

  • 查看实际的查询计划。它是否暗示任何缺失的索引?也许在 (UUID,DateAdded) 上,因此不必每次都扫描表。还可以考虑使用testquery 的结果加载一个临时表,这样您就不会运行它两次。
  • 您没有涵盖任一个搜索词的索引; UUID, DateAdded... 这意味着查询必须扫描所有 700 万条记录以查找匹配的 UUID,然后按 DateAdded 对结果进行排序,以便它可以返回您正在查找的单个记录。这就像我要求您告诉我单词either 出现在一本书的所有页面上的行号,然后选择该行号最高的页面。在test(UUID, DateAdded DESC) 上添加索引将使插入稍微变慢,并使查询大大更快。
  • 谢谢。但是,它没有帮助,因为我厌倦了这个测试(UUID,DateAdded desc)。它终止了它,因为找到了重复的密钥。请问还有什么建议吗?

标签: sql sql-server


【解决方案1】:

您需要一个好的索引来有效地为这个查询提供服务。

您说由于重复键错误而无法创建索引:索引不需要唯一。

因此,您要查找的查询取决于您正在运行的其他查询,但以下内容足以满足此查询:

CREATE NONCLUSTERED INDEX IX_Test_UuidDate ON
    Test (UUID ASC, DateAdded DESC)
    INCLUDE (Firstname, LastName, AddrLine1, AddrLine2, City, Prov, Postal)

GO

而且,不需要查询表两次。

从一个虚拟的VALUES 表构造函数开始,这样你总是有一行,然后LEFT JOIN 表并使用CASE 来处理没有一行的问题。

WITH testquery AS 
(
    SELECT TOP 1 
        *
    FROM 
        [Test] 
    WHERE 
        UUID = 'BLABLABLABLABLABLABLABLABLA' 
    ORDER BY 
        DateAdded DESC
)
SELECT
    CASE WHEN UUID IS NULL 'Rejected' ELSE 'Matched' END as location,
    t.Firstname,
    t.LastName,
    t.AddrLine1,
    t.AddrLine2,
    t.City,
    t.Prov,
    t.Postal 
FROM (VALUES(0)) AS v(dummy)
LEFT JOIN testquery AS t ON 1=1;

【讨论】:

    【解决方案2】:

    对此的通常解释是冷缓存。在您的情况下,我认为问题在于第一个 CTE 中的 ORDER BY

    要解决此问题,您需要在test(UUID, DateAdded desc) 上建立索引。

    我不确定为什么在第一次执行后会加快速度。也许服务器的缓存工作得特别好。

    【讨论】:

    • 谢谢。但是,它没有帮助,因为我厌倦了这个测试(UUID,DateAdded desc)。它终止了它,因为找到了重复的密钥。请问还有什么建议吗?
    • @Ribaz 。 . .索引允许重复。您正在定义唯一索引/约束或外键约束。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    • 1970-01-01
    相关资源
    最近更新 更多