Как я могу оптимизировать этот запрос SQL - PullRequest
1 голос
/ 13 декабря 2011

Я пишу программный продукт, предназначенный для идентификации файлов, которые были помещены на веб-сервер (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.

Я хотел бы отсканировать базу данных, чтобы определить, есть ли ссылки на файлы из первой таблицы где-либо в содержимом сайта - столбец p_content из таблицы page_data и столбец PageXML из таблицы PageXMLArchive.

Итак, у меня есть цикл, который получает каждое имя файла и проверяет, есть ли на него ссылка в какой-либо из этих таблиц, если он пропускает его, если нет, добавляет его во временную таблицу.

В конце запроса отображается временная таблица.

Запрос ниже:

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 часов и время ожидания истекло.

Как я могу улучшить этот запрос или изменить его для достижения аналогичной цели, не выполняя тысячи операторов WHERE x LIKE в полях ntext? Я не могу вносить какие-либо изменения в базу данных, она должна оставаться нетронутой (или она не будет поддерживаться - большое дело для наших клиентов).

Спасибо

EDIT: В настоящее время я работаю над этой проблемой, собирая результаты по несколько сотен за раз. Это работает, но длится вечно.

EDIT:

Можно ли использовать полнотекстовый поиск для достижения этой цели? Я готов сделать снимок базы данных и поработать над копией, если есть способ изменить схему для достижения желаемых результатов.

Таблица данных_строки:

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

PageXMLАрхив таблицы:

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 ]

3 голосов
/ 13 декабря 2011

Вы можете избежать цикла разными способами, и вот пример ...

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_data и PageXMLArchive и создать список файлов, на которые имеются ссылки.Так как HTML и XML очень структурированы, это можно сделать, но я бы искал библиотеку или что-то для вас.

Затем вы можете создать еще одну таблицу со всеми файлами, бездублирование и с соответствующим индексом.Запросы против этого вместо использования LIKE будут значительно быстрее.У меня нет никаких сомнений.Но написание или поиск кода будет рутиной.

0 голосов
/ 13 декабря 2011

Хранимая процедура, в частности, имеет цикл со смешанными select и insert, который демонстративно замедляет запрос.

в идеале, если вы могли бы insert into @table select a, b from table, он будет в миллионы раз быстрее, чем вставлять каждую строку отдельно.

Для вашего примера можно сделать что-то вроде:

вставить в @t (_fileName) select ... from p_content join ... on .. где sth вроде% sth

дайте мне знать, если это не применимо.

...