如何优化此SQL查询

时间:2011-12-13 13:06:26

标签: sql sql-server-2005 tsql query-optimization

我正在编写一个软件,用于识别已放入Web服务器(CMS)但不再需要且应该/可以删除的文件。

首先,我试图手动重现所有必需的步骤。

我正在使用webroot中执行的批处理脚本来识别服务器上的所有(相关)文件。然后,我将列表导入SQL Server,表格如下所示:

id   filename
1    filename1.docx
2    files/file.pdf
3    files/filename2.docx
4    files/filename3.docx
5    files/file1.pdf
6    file2.pdf
7    file4.pdf

我还有一个CMS数据库(Alterian / Immediacy CMC 6.X),它有2个表存储页面内容:page_data和PageXMLArchive。

我想扫描数据库以查看第一个表中的文件是否在站点内容中的任何位置引用 - 来自page_data表的p_content列和来自PageXMLArchive表的PageXML列。

所以我有一个循环,它获取每个文件名并检查它是否在任何这些表中被引用,如果它是跳过它,如果它不是它将它添加到临时表。

在查询结束时,将显示临时表。

以下查询:

DECLARE @t as table (_fileName nvarchar(255))
DECLARE @row as int
DECLARE @result as nvarchar(255)

SET @row = 1


WHILE(@row <= (SELECT COUNT(*) FROM ListFileReport))
BEGIN
    SET @result = (SELECT [FileName] FROM ListFileReport WHERE id = @row)

    IF ((SELECT TOP(1) p_content FROM page_data WHERE p_content LIKE '%' + LTRIM(RTRIM(@result)) + '%') IS NULL) OR ((SELECT TOP(1) PageXML FROM PageXMLArchive WHERE PageXML LIKE '%' + LTRIM(RTRIM(@result)) + '%') IS NULL)
    BEGIN
        INSERT INTO @t (_fileName) VALUES(@result)
    END

    SET @row = @row + 1

END

select * from @t

不幸的是,由于我的SQL技能不佳,查询需要2个多小时才能执行并超时。

如何在不需要在ntext字段上运行1000个WHERE x LIKE语句的情况下改进该查询,或者更改它以实现类似的操作?我不能对数据库进行任何更改,它必须保持不变(或者它不会被支持 - 对我们的客户来说很重要。)

由于

修改 目前我正在解决这个问题,一次批量处理几百个结果。它可以工作,但需要永远。

修改

我可以使用全文搜索来实现这一目标吗?如果有一种方法可以改变模式以达到预期的效果,我愿意拍摄数据库的快照并处理副本。

page_data table:

USE [TD-VMB-01-STG]
GO

/****** Object:  Table [dbo].[page_data]    Script Date: 12/13/2011 13:19:15 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[page_data](
    [p_page_id] [int] NOT NULL,
    [p_title] [nvarchar](120) NULL,
    [p_link] [nvarchar](250) NULL,
    [p_content] [ntext] NULL,
    [p_parent_id] [int] NULL,
    [p_top_id] [int] NULL,
    [p_stylesheet] [nvarchar](50) NULL,
    [p_author] [nvarchar](50) NULL,
    [p_last_update] [datetime] NULL,
    [p_order] [smallint] NULL,
    [p_window] [nvarchar](10) NULL,
    [p_meta_keywords] [nvarchar](1000) NULL,
    [p_meta_desc] [nvarchar](2000) NULL,
    [p_type] [nvarchar](1) NULL,
    [p_confirmed] [int] NOT NULL,
    [p_changed] [int] NOT NULL,
    [p_access] [int] NULL,
    [p_errorlink] [nvarchar](255) NULL,
    [p_noshow] [int] NOT NULL,
    [p_edit_parent] [int] NULL,
    [p_hidemenu] [int] NOT NULL,
    [p_subscribe] [int] NOT NULL,
    [p_StartDate] [datetime] NULL,
    [p_EndDate] [datetime] NULL,
    [p_pageEnSDate] [int] NOT NULL,
    [p_pageEnEDate] [int] NOT NULL,
    [p_hideexpiredPage] [int] NOT NULL,
    [p_version] [float] NULL,
    [p_edit_order] [float] NULL,
    [p_order_change] [datetime] NOT NULL,
    [p_created_date] [datetime] NOT NULL,
    [p_short_title] [nvarchar](30) NULL,
    [p_authentication] [tinyint] NOT NULL,
 CONSTRAINT [aaaaapage_data_PK] PRIMARY KEY NONCLUSTERED 
(
    [p_page_id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_order]  DEFAULT (0) FOR [p_order]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF__Temporary__p_con__1CF15040]  DEFAULT (0) FOR [p_confirmed]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF__Temporary__p_cha__1DE57479]  DEFAULT (0) FOR [p_changed]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF__Temporary__p_acc__1ED998B2]  DEFAULT (1) FOR [p_access]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF__Temporary__p_nos__1FCDBCEB]  DEFAULT (0) FOR [p_noshow]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF__Temporary__p_edi__20C1E124]  DEFAULT (0) FOR [p_edit_parent]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF__Temporary__p_hid__21B6055D]  DEFAULT (0) FOR [p_hidemenu]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_subscribe]  DEFAULT (0) FOR [p_subscribe]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_pageEnSDate]  DEFAULT (0) FOR [p_pageEnSDate]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_pageEnEDate]  DEFAULT (0) FOR [p_pageEnEDate]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_hideexpiredPage]  DEFAULT (1) FOR [p_hideexpiredPage]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_version]  DEFAULT (0) FOR [p_version]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_edit_order]  DEFAULT (0) FOR [p_edit_order]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_order_change]  DEFAULT (getdate()) FOR [p_order_change]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_created_date]  DEFAULT (getdate()) FOR [p_created_date]
GO

ALTER TABLE [dbo].[page_data] ADD  CONSTRAINT [DF_page_data_p_authentication]  DEFAULT ((0)) FOR [p_authentication]
GO

PageXMLArchive表:

USE [TD-VMB-01-STG]
GO

/****** Object:  Table [dbo].[PageXMLArchive]    Script Date: 12/13/2011 13:20:00 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[PageXMLArchive](
    [ArchiveID] [bigint] IDENTITY(1,1) NOT NULL,
    [P_Page_ID] [int] NOT NULL,
    [p_author] [nvarchar](100) NULL,
    [p_title] [nvarchar](400) NULL,
    [Version] [int] NOT NULL,
    [PageXML] [ntext] NULL,
    [ArchiveDate] [datetime] NOT NULL,
 CONSTRAINT [PK_PageXMLArchive] PRIMARY KEY CLUSTERED 
(
    [ArchiveID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [dbo].[PageXMLArchive] ADD  CONSTRAINT [DF_PageXMLArchive_ArchiveDate]  DEFAULT (getdate()) FOR [ArchiveDate]
GO

2 个答案:

答案 0 :(得分:3)

你可以通过多种方式避免循环,这是一个例子......

SELECT
  *
FROM
  ListFileReport
WHERE
  NOT EXISTS (
     SELECT *
     FROM   page_data
     WHERE  p_content LIKE '%' + LTRIM(RTRIM(ListFileReport.FileName)) + '%'
    )
  AND
  NOT EXISTS (
     SELECT *
     FROM   PageXMLArchive 
     WHERE  PageXML LIKE '%' + LTRIM(RTRIM(ListFileReport.FileName)) + '%'
    )

注意:这会删除循环,并因此而产生巨大的改进。但是它仍然必须为ListFileReport中的每个条目解析整个两个查找表,而没有任何聪明的算法,它们可能没有用的索引。所以它作为一只狗仍然会很慢,它只会有一条断腿而不是两条腿。


避免使用LIKE的唯一方法是解析page_dataPageXMLArchive表中的所有字段,并创建引用文件列表。由于HTML和XML非常结构化,这可以完成,但我会寻找一个库或其他东西为你做。

然后,您可以使用所有文件创建另一个表,不带重复,并使用适当的索引。查询而不是使用LIKE将大大加快。我毫不怀疑。但是编写或找到代码将是一件苦差事。

答案 1 :(得分:0)

存储过程特别是有一个selectinsert混合的循环会严重降低查询速度。

理想情况下,如果你能insert into @table select a, b from table,它将比分别插入每一行快上百万倍。

对于您的示例,可以执行以下操作:

  

插入@t(_fileName)select ... from p_content join ... on .. where sth like%sth

如果不适用,请告诉我。