2012-03-12 5 views
3

Я создал два варианта запроса, используемого в представлении, которое возвращает требуемые результаты. Мне нужно переписать любой вариант, чтобы его можно было использовать в с индексированным представлением. Оба не работают при создании уникального кластерного индекса на вид. Первый из них выходит из строя из-за LEFT OUTER JOIN, а второй не получается из-за подзапроса. Я считаю, что оба будут терпеть неудачу из-за .Невозможно создать CLUSTERED INDEX в представлении из-за LEFT JOIN или подзапроса

После нахождения Creating Indexed Views существует большой список синтаксических элементов TSQL, которые нельзя использовать. Среди них: производной таблицы, UNION, КРОМЕ, ПЕРЕСЕЧЕНИЕ, подзапросы, Outer или самостоятельно включается, TOP, ORDER BY, DISTINCT, MAX ...

запрос должен получить самый крупный CompanyID для каждого уникального Company. Также необходимо отобразить таблицу StatusName в таблице Statuses, и я добавляю только это, если это влияет на решение . В настоящее время это INNER JOIN, поэтому это не создает проблемы с созданием индекса.

Пример для Companies таблицы, со всеми 3 колонки быть INT:

CompanyID Company Revision 
1   1  1 
2   1  2 
3   2  1 
4   2  2 

Запрос должен возвращать:

CompanyID Company Revision 
2   1  2 
4   2  2 

Вот два варианта я создал:

SELECT t1.CompanyID, t1.Company, t1.Revision, Statuses.StatusName 
FROM dbo.Companies AS t1 

LEFT OUTER JOIN dbo.Companies AS t2 
ON t1.Company = t2.Company AND t1.CompanyID < t2.CompanyID 

INNER JOIN dbo.Statuses 
ON dbo.Statuses.StatusID = t1.StatusID 

WHERE t2.Company IS NULL 

И прочие:

SELECT t1.CompanyID, t1.Company, t1.Revision, Statuses.StatusName 
FROM dbo.Companies AS t1 

INNER JOIN dbo.Statuses 
ON dbo.Statuses.StatusID = t1.StatusID 

WHERE t1.Company NOT IN (SELECT t2.Company from dbo.Companies AS t2 WHERE t1.CompanyID < t2.CompanyID) 

Итак, на мой вопрос, можно ли переписывать запрос, чтобы он использовался в индексированном виде?

Я использую MS SQL Server 2008 R2 и 2005.

+3

Почему вы считаете, что единственное решение любой проблемы, с которой вы столкнулись (но вы не указали), - это индексированный вид? Не могли бы вы указать свою фактическую проблему, а не сообщать нам, почему вы не можете использовать индексированное представление для ее решения? –

+0

Извините, позвольте мне уточнить. У нас ограниченная аппаратная производительность и мы стремимся повысить производительность запросов с минимальными изменениями в схеме базы данных. Неиндексированные представления уже существуют. В базовых таблицах будет 100 000 записей (в этом примере - компании), но обычно менее 100 будут возвращены из любого представления. Похоже, что индексированные представления были бы хорошим кандидатом для решения, если бы довольно простые запросы могли быть переписаны. – njb

+3

Если вы нашли один способ построения набора результатов, и он находится в списке «запрещенных», вполне вероятно, что любой другой способ построения одного и того же набора результатов также потерпит неудачу. Список ограничений для индексированных представлений не был выбран случайным образом - они связаны с тем, как индексирование выполняется за кулисами. –

ответ

2

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

CREATE VIEW dbo.HighestCompany 
AS 
    SELECT t1.CompanyID, t1.Company, t1.Revision, s.StatusName 
    FROM dbo.Companies AS t1 
    INNER JOIN (
     SELECT Company, HighestCompany = MAX(CompanyID) 
     FROM dbo.Companies GROUP BY Company 
    ) AS t2 
    ON t1.Company = t2.Company 
    AND t1.CompanyID = t2.HighestCompany -- not sure if CompanyID is unique 
    INNER JOIN dbo.Statuses AS s 
    ON s.StatusID = t1.StatusID; 

Вы все еще не будете в состоянии создать индексированный вид на этом, но он может работать немного лучше, чем версии, которые у вас есть (в зависимости от нескольких факторов, конечно, включая индекс компании и избирательность).

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

Вы можете сделать это следующим образом (имейте в виду, я предполагаю, что на типы данных и оптимальных показателей здесь):

CREATE SCHEMA hold AUTHORIZATION dbo; 
GO 
CREATE SCHEMA cache AUTHORIZATION dbo; 
GO 
CREATE TABLE dbo.HighestCompany 
(
    CompanyID INT, 
    Company NVARCHAR(255) PRIMARY KEY, 
    Revision INT, 
    StatusName NVARCHAR(64) 
); 
GO 
CREATE TABLE cache.HighestCompany 
(
    CompanyID INT, 
    Company NVARCHAR(255) PRIMARY KEY, 
    Revision INT, 
    StatusName NVARCHAR(64) 
); 
GO 

Однако теперь часто вы думаете, эти данные должны быть обновлены, вы можете запустить работу что делает следующее:

TRUNCATE TABLE cache.HighestCompany; 

INSERT cache.HighestCompany(CompanyID, Company, Revision, StatusName) 
SELECT t1.CompanyID, t1.Company, t1.Revision, s.StatusName 
     FROM dbo.Companies AS t1 
     INNER JOIN (
      SELECT Company, HighestCompany = MAX(CompanyID) 
      FROM dbo.Companies GROUP BY Company 
     ) AS t2 
     ON t1.Company = t2.Company 
     AND t1.CompanyID = t2.HighestCompany 
     INNER JOIN dbo.Statuses AS s 
     ON s.StatusID = t1.StatusID; 

-- this is a fast, metadata operation that should result 
-- in minimal blocking and disruption to end users: 
BEGIN TRANSACTION; 
    ALTER SCHEMA hold TRANSFER dbo.HighestCompany; 
    ALTER SCHEMA dbo TRANSFER cache.HighestCompany; 
    ALTER SCHEME cache TRANSFER hold.HighestCompany; 
COMMIT TRANSACTION; 

Если вы нашли компании меняются так часто, или данные действительно должны быть вверх-к-вторых, что это не практично, вы могли бы сделать что-то подобное с триггер, как предложил @Dems.

+0

@ При чтении запроса говорится: «Из всех компаний, которые соответствуют компании (что я предполагаю, это имя), исключают все те, у которых нет наивысшего идентификатора компании» ... теперь я точно не знаю, что данные находятся в компании против companyID, так как они довольно неоднозначные имена, но я считаю, что мое представление вернет те же строки, что и представление OP. Оптимизатор может не относиться к нему иначе, но это может стоить попробовать. (Это также то, что вы можете запускать периодически и кэшировать результаты где-то.) –

+0

«CompanyID» - это только столбец Identity. «Компания» - это уникальный номер для каждой компании, который никогда не изменится, даже если изменится другая информация о компании (в колонках изначально не указано). У нас есть требование, чтобы в базовых таблицах не было привилегий DELETE или UPDATE. Именно здесь вступает в игру столбец «Редакция». Для каждого изменения «ревизия» увеличивается. Есть несколько дополнительных столбцов в компаниях (CompanyName, Address и т. Д.). Другие базовые таблицы имеют больше столбцов и обновляются чаще, это просто простейшая таблица/представление для примера. – njb

+0

'CompanyID',' Company' и 'Revision' - все' INT'. 'CompanyName' и т. Д.' NVARCHAR (255) '. – njb

1

К сожалению, вы не можете.

Ваш запрос не только требует LEFT JOIN, вы LEFT JOINING той же таблицы для себя. И цитировать BooksOnline и ваш вопрос ...

The SELECT statement in the view cannot contain the following Transact-SQL syntax elements: 
- Outer or self joins. 

Альтернативным вариантом может быть, чтобы создать реальную таблицу сопоставления, что вы будете в курсе с помощью триггера. Записи создаются/удаляются как изменения Companies, а записи обновляются как Statuses.


В равной степени виды расширяются в строке в запросах, в которых они используются (если только вы не указали иное с подсказкой NOEXPAND). Вы проверили планы выполнения своих запросов, чтобы узнать, можете ли вы создать более подходящие индексы в базовых таблицах?

EDIT

Альтернативный макет запроса, так же, как вариант ...

;WITH 
    sequenced_companies 
AS 
(
    SELECT 
    ROW_NUMBER() OVER (PARTITION BY Company ORDER BY CompanyID DESC) AS sequence_id, 
    * 
    FROM 
    dbo.companies 
) 
SELECT 
    * 
FROM 
    sequenced_companies 
INNER JOIN 
    dbo.statuses 
    ON statuses.StatusID = sequenced_companies.StatusID 
WHERE 
    sequenced_companies.sequence_id = 1 

С индексом на (Company, CompanyID DESC), это должно быть довольно быстро. (. Хотя до сих пор не подходит для сменного зрения)

+0

Не думал, что это возможно, но стоит попросить узнать что-то новое. Интересный подход. Похоже, мне придется иметь либо дополнительные таблицы, либо дополнительные представления, поэтому я попробую оба предложенных ответа до сих пор. Еще не проверял планы выполнения, но я считаю, что индексы в порядке. Для 'компаний',' CompanyID' является Identity, PK, индексируется. Посмотрите на это, чтобы увидеть, если что-то выделяется. – njb

+0

Можете ли вы объяснить, почему у вас одна и та же компания несколько раз с разными значениями CompanyID? Мне кажется, что компания должна быть ключом, а не суррогатной идентификационной ценностью. –

+0

@njb - альтернативный запрос и предлагаемый индекс для 'компании', добавленный в случае, если это помогает. – MatBailie