2011-12-13 73 views
1

我正在编写一款软件,旨在识别已放入Web服务器(CMS)但不再需要且应该/可以删除的文件。如何优化此SQL查询

要开始我试图手动重现所有必需的步骤。

我正在使用在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 /即时性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表:

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 
+0

你可以在问题中包含你的page_data和PageXMLArchive表的结构吗? –

+2

对于初学者,您应该将'SELECT COUNT(*)'移到WHILE之上,并将结果放入一个变量中。避免为每一行执行该查询。 – Johan

+0

@MarkBannister添加了表格结构。谢谢 – LukeP

回答

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将会大大加快速度。我毫无疑问。但是编写或查找代码将是一件苦差事。

+0

+1为解释为什么这仍然不到“理想” –

+0

谢谢。出于测试目的,我运行了查询(仅部分查询page_data表,将PageXMLArchive留出),我的查询在5:04分钟后结束,而你的时间为5分09秒,这只在page_data表上(1600行),PageXMLArchive是16000行,将COUNT查询移动到循环外部并立即运行,将在一秒钟内回复 – LukeP

0

一个存储过程尤其是有一个循环与selectinsert混合会反对减慢查询。

理想情况下,如果你可以insert into @table select a, b from table它将比单独插入每行快上百万倍。

对于你的榜样,可以这样做:

INSERT INTO @t(_filename)选择...从p_content加入... ..对其中某事像某物%

让我知道它是否不适用。

+0

我不认为它是一个导致延迟的INSERT,它是SELECT * FROM X WHERE Y LIKE'%Z%'在两个表中的ntext列,共17000行,每行的ntext字段将包含整个XML/HTML代码页。 – LukeP