优化更新查询

时间:2012-01-05 00:56:40

标签: sql sql-server tsql

我正在寻找优化此查询的建议,该查询已经运行了一个多小时,表中有大约300,000行。我们正在使用一种报告工具,要求数据在拉动时处于这种形状,因此重新设计表结构不是一种选择。该表如下所示:

CREATE TABLE [datatable](
    [RowID] [int] IDENTITY(1,1) NOT NULL,
    [CampaignID] [int] NOT NULL,
    [CampaignName] [nvarchar](255) NULL,
    [Category] [nvarchar](255) NOT NULL,
    [PostID] [int] NOT NULL,
    [TopicName] [nvarchar](4000) NULL,
    [TopicFrequency] [int] NULL
)

数据不断添加到表中,因此我必须定期更新主题频率。这是我目前的查询:

UPDATE  datatable
SET     TopicFrequency = b.TopicFrequency
FROM    datatable INNER JOIN
  (SELECT CampaignID, Category, TopicName, COUNT(DISTINCT PostID) AS TopicFrequency
    FROM datatable GROUP BY CampaignID, Category, TopicName) AS b 
    ON datatable.CampaignID = b.CampaignID 
    AND datatable.Category = b.Category 
    AND datatable.TopicName = b.TopicName

主题名称为nvarchar 4000我无法在该字段上创建索引。寻找想法。感谢。

4 个答案:

答案 0 :(得分:1)

一般决策 - 将您的表拆分为两个或更多表 - 即 - 规范化数据结构。我认为可以引入另外两个表 - 适用于广告系列和主题

<强> BUT

对于您当前的数据结构,您可以创建uniqueidentifierbigint计算列作为TopicName字段的哈希值,对其进行索引并查找哈希而不是字符串字段。我将以bigint

为您提供示例
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[HashString64SVF](@input NVARCHAR(4000))
RETURNS BIGINT
WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT 
AS 
BEGIN
    RETURN
        CAST(SUBSTRING(HASHBYTES('SHA1', UPPER(@Input)), 1, 8) AS BIGINT) 
    ^   CAST(SUBSTRING(HASHBYTES('SHA1', UPPER(@Input)), 9, 8) AS BIGINT) 
    ^   CAST(SUBSTRING(HASHBYTES('SHA1', UPPER(@Input)), 17, 4) AS BIGINT) 
END
GO
ALTER TABLE datatable ADD TopicNameHash AS dbo.HashString64SVF(TopicName)
GO
CREATE INDEX NewIndexName ON DataTable(TopicNameHash, CampaignID, Category) INCLUDE(PostId)
GO
UPDATE  datatable
SET     TopicFrequency = b.TopicFrequency
FROM    datatable 
JOIN
  (SELECT CampaignID, Category, TopicNameHash, COUNT(DISTINCT PostID) AS TopicFrequency
    FROM datatable GROUP BY CampaignID, Category, TopicNameHash) AS b 
    ON datatable.CampaignID = b.CampaignID 
    AND datatable.Category = b.Category 
    AND datatable.TopicNameHash = b.TopicNameHash

在RowId列上创建主键

以这样的方式重新创建表:

CREATE TABLE [datatable](
    [RowID] [int] IDENTITY(1,1) PRIMARY KEY,
    [CampaignID] [int] NOT NULL,
    [Category] [nvarchar](255) NOT NULL,
    [PostID] [int] NOT NULL,
    --uncomment if needed [TopicNameHash] AS dbo.HashString64SVF(TopicName),
    [TopicFrequency] [int] NULL,
    [CampaignName] [nvarchar](255) NULL,
    [TopicName] [nvarchar](4000) NULL
)

主要原因 - 如果您的可空列变量列在列列表的末尾并且这些列中有许多NULL值 - sql server可以在行中保存一点空间,因此 - 在IO中

答案 1 :(得分:0)

<强>触发

在插入/更新/删除数据时更新频率字段?随着时间的推移传播负载,并且唯一更新的记录是与更改的数据相关的记录。


TopicID

有一个主题表,可能有也可能不只有id, name。然后,您可以使用(并索引)TopicID。

由于您在GROUP BY和JOIN中都有TopicName,因此能够对此进行索引会产生大量性能差异。


LastModified或其他审计跟踪

记录(并包含在索引中)上次修改时间或其他一些审计跟踪。这样,您就可以将更新范围缩小到自上次批处理以来已插入/更新/删除记录的主题。


归一化

将频率值保存在另一个表中,按“广告系列”,“类别”,“主题”键入。

目前,如果你的COUNT(*)产生100,你就会更新100条记录。规范化将意味着每组更新一次。


明显的注意事项?

仅仅因为您对基础数据进行了规范化或重构,您是否(当然?)无法将该表替换为“更好”设计结构的视图?

报告工具将视图视为表格。数据处理直接与重构表结构进行交互,以更有效的方式进行。

分离数据报告注意事项和数据处理注意事项将使您成为更自由的人。

答案 2 :(得分:0)

如果您可以避免使用相关子查询来更新它,我认为它会提高性能。为什么不直接加入表并更新表。请参阅this链接

答案 3 :(得分:0)

前段时间我遇到过类似问题:第三方软件使用的表格对于某些数据操作来说太大了。我解决问题的诀窍是:

  1. 创建优化的表结构并从旧表中复制数据
  2. 删除旧表
  3. 创建与已删除表同名的视图,该视图使用优化结构并具有与旧表相同的结构
  4. 对于第三方软件,表和视图之间没有区别。

    您还可以在视图上添加触发器以使其可更新。