AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 116347
Accepted
Marcello Miorelli
Marcello Miorelli
Asked: 2015-09-29 07:37:46 +0800 CST2015-09-29 07:37:46 +0800 CST 2015-09-29 07:37:46 +0800 CST

Não é possível criar um índice filtrado em uma coluna computada

  • 772

Em uma pergunta anterior minha, é uma boa ideia desabilitar a escalação de bloqueio ao adicionar novas colunas calculadas a uma tabela? , estou criando uma coluna computada:

ALTER TABLE dbo.tblBGiftVoucherItem
ADD isUsGift AS CAST
(
    ISNULL(
        CASE WHEN sintMarketID = 2 
            AND strType = 'CARD'
            AND strTier1 LIKE 'GG%' 
        THEN 1 
        ELSE 0 
        END
    , 0) 
    AS BIT
) PERSISTED;

A coluna calculada é PERSISTED, e de acordo com computed_column_definition (Transact-SQL) :

PERSISTIDO

Especifica que o Mecanismo de Banco de Dados armazenará fisicamente os valores calculados na tabela e atualizará os valores quando quaisquer outras colunas das quais a coluna calculada depende forem atualizadas. Marcar uma coluna computada como PERSISTED permite que um índice seja criado em uma coluna computada que seja determinística, mas não precisa. Para obter mais informações, consulte índices em colunas computadas. Quaisquer colunas computadas usadas como colunas de particionamento de uma tabela particionada devem ser explicitamente marcadas como PERSISTED. computed_column_expression deve ser determinístico quando PERSISTED é especificado.

Mas quando tento criar um índice na minha coluna, recebo o seguinte erro:

CREATE INDEX FIX_tblBGiftVoucherItem_incl
ON dbo.tblBGiftVoucherItem (strItemNo) 
INCLUDE (strTier3)
WHERE isUsGift = 1;

O índice filtrado 'FIX_tblBGiftVoucherItem_incl' não pode ser criado na tabela 'dbo.tblBGiftVoucherItem' porque a coluna 'isUsGift' na expressão de filtro é uma coluna computada. Reescreva a expressão de filtro para que ela não inclua esta coluna.

Como posso criar um índice filtrado em uma coluna computada?

ou

Existe uma solução alternativa?

sql-server index
  • 5 5 respostas
  • 4941 Views

5 respostas

  • Voted
  1. Best Answer
    Mark Sinkinson
    2015-09-29T07:55:07+08:002015-09-29T07:55:07+08:00

    Infelizmente, a partir do SQL Server 2014, não há capacidade de criar um Filtered Indexonde o filtro esteja em uma coluna computada (independentemente de ser ou não persistente).

    Há um Connect Item aberto desde 2009, então vá em frente e vote nele. Talvez a Microsoft conserte isso um dia.

    Aaron Bertrand tem um artigo que aborda vários outros problemas com índices filtrados .

    • 23
  2. Hannah Vernon
    2015-09-29T08:19:32+08:002015-09-29T08:19:32+08:00

    Embora você não possa criar um índice filtrado em uma coluna persistente, há uma solução bastante simples que você pode usar.

    Como teste, criei uma tabela simples com uma IDENTITYcoluna e uma coluna computada persistente com base na coluna de identidade:

    USE tempdb;
    
    CREATE TABLE dbo.PersistedViewTest
    (
        PersistedViewTest_ID INT NOT NULL
            CONSTRAINT PK_PersistedViewTest
            PRIMARY KEY CLUSTERED
            IDENTITY(1,1)
        , SomeData VARCHAR(2000) NOT NULL
        , TestComputedColumn AS (PersistedViewTest_ID - 1) PERSISTED
    );
    GO
    

    Em seguida, criei uma visualização vinculada ao esquema com base na tabela com um filtro na coluna computada:

    CREATE VIEW dbo.PersistedViewTest_View
    WITH SCHEMABINDING
    AS
    SELECT PersistedViewTest_ID
        , SomeData 
        , TestComputedColumn
    FROM dbo.PersistedViewTest
    WHERE TestComputedColumn < CONVERT(INT, 27);
    

    Em seguida, criei um índice clusterizado na exibição vinculada ao esquema, que tem o efeito de persistir os valores armazenados na exibição, incluindo o valor da coluna computada:

    CREATE UNIQUE CLUSTERED INDEX IX_PersistedViewTest
    ON dbo.PersistedViewTest_View(PersistedViewTest_ID);
    GO
    

    Insira alguns dados de teste na tabela:

    INSERT INTO dbo.PersistedViewTest (SomeData)
    SELECT o.name + o1.name + o2.name
    FROM sys.objects o
        CROSS JOIN sys.objects o1
        CROSS JOIN sys.objects o2;
    

    Crie um item de estatísticas e um índice na exibição:

    CREATE STATISTICS ST_PersistedViewTest_View
    ON dbo.PersistedViewTest_View(TestComputedColumn)
    WITH FULLSCAN;
    
    CREATE INDEX IX_PersistedViewTest_View_TestComputedColumn
    ON dbo.PersistedViewTest_View(TestComputedColumn);
    

    A execução SELECTde instruções na tabela com a coluna persistente agora pode usar automaticamente a exibição persistente, se o otimizador de consulta determinar que faz sentido fazer isso:

    SELECT pv.PersistedViewTest_ID
        , pv.TestComputedColumn
    FROM dbo.PersistedViewTest pv
    WHERE pv.TestComputedColumn = CONVERT(INT, 26)
    

    O plano de execução real para a consulta acima mostra que o otimizador de consulta escolheu usar a exibição persistente para retornar os resultados:

    insira a descrição da imagem aqui

    Você deve ter notado a conversão explícita na WHEREcláusula acima. Esse explícito CONVERT(INT, 26)permite que o otimizador de consulta use corretamente o objeto de estatísticas para estimar o número de linhas que serão retornadas pela consulta. Se escrevermos a consulta com WHERE pv.TestComputedColumn = 26, o otimizador de consulta pode não estimar adequadamente o número de linhas, pois 26 é realmente considerado um TINY INT; isso pode fazer com que o SQL Server não use a exibição persistente. As conversões implícitas podem ser muito dolorosas e vale a pena usar consistentemente os tipos de dados corretos para comparações e uniões.

    É claro que todas as "pegadinhas" padrão resultantes do uso de schema binding se aplicam ao cenário acima; isso pode impedir o uso dessa solução alternativa em todos os cenários. Por exemplo, não será mais possível modificar a tabela base sem primeiro remover a vinculação do esquema da exibição. Para fazer isso, você precisará remover o índice clusterizado da exibição.

    Se você não tiver o SQL Server Enterprise Edition, o otimizador de consulta não usará automaticamente a exibição persistente para consultas que não fazem referência direta à exibição usando a WITH (NOEXPAND)dica. Para obter o benefício de usar a exibição persistente em versões não Enterprise Edition, você precisará reescrever a consulta acima para algo como:

    SELECT pv.PersistedViewTest_ID
        , pv.TestComputedColumn
    FROM dbo.PersistedViewTest_View pv WITH (NOEXPAND)
    WHERE pv.TestComputedColumn = CONVERT(INT, 26)
    

    Obrigado a Ian Ringrose por apontar a limitação da Enterprise Edition acima e a Paul White pela (NOEXPAND)dica.

    Esta resposta de Paul tem alguns detalhes interessantes sobre o otimizador de consulta em relação a visualizações persistentes.

    • 22
  3. Julien Vavasseur
    2015-09-29T07:55:41+08:002015-09-29T07:55:41+08:00

    De Create Indexe sua wherecláusula, isso não é possível:

    ONDE

    Cria um índice filtrado especificando quais linhas incluir no índice. O índice filtrado deve ser um índice não clusterizado em uma tabela. Cria estatísticas filtradas para as linhas de dados no índice filtrado.

    O predicado do filtro usa lógica de comparação simples e não pode fazer referência a uma coluna computada, uma coluna UDT, uma coluna de tipo de dados espaciais ou uma coluna de tipo de dados hierarquiaID. Comparações usando literais NULL não são permitidas com os operadores de comparação. Em vez disso, use os operadores IS NULL e IS NOT NULL.

    Fonte: MSDN

    • 5
  4. Ian Ringrose
    2015-09-30T06:19:58+08:002015-09-30T06:19:58+08:00
    • Você precisa de uma coluna que não seja calculada para colocar o índice filtrado.
    • Você precisa calcular o valor para ir nessa coluna.

    Antes de calcularmos as colunas, usávamos gatilhos para calcular o valor das colunas sempre que a linha era alterada ou inserida.

    (Um gatilho também pode ser usado para inserir/remover o PK do item de uma 2ª tabela que foi usada em consultas.)

    • 4
  5. Bob Bryan
    2016-08-27T15:27:08+08:002016-08-27T15:27:08+08:00

    Esta é uma tentativa de melhorar o trabalho de Hannah . Em sua solução, ela sugere o uso de 2 índices na exibição e um objeto de estatísticas.

    O primeiro índice é clusterizado, o que é realmente necessário, pois, ao contrário de um índice não clusterizado em uma tabela, um erro será gerado se a criação de um índice não clusterizado na exibição for tentada sem primeiro ter um índice clusterizado.

    O segundo índice é um índice não clusterizado, usado como o índice por trás da consulta. Na seção de comentários de sua resposta, perguntei o que aconteceria se um índice clusterizado fosse usado em vez de um índice não clusterizado.

    A análise a seguir tenta responder a essa pergunta.

    Estou usando exatamente o mesmo código, exceto que não estou criando um índice não clusterizado na exibição.

    Também não estou criando um objeto de estatísticas. Se você estiver acompanhando e usando o SQL Server Management Studio (SSMS) para inserir o código abaixo, saiba que poderá ver algumas linhas onduladas vermelhas - que parecem erros. Estes (provavelmente) não são erros, mas envolvem um problema com o intellisense.

    Você pode desabilitar o intellisense ou simplesmente ignorar os erros e executar os comandos. Eles devem ser concluídos sem erros.

    -- Create the test table that uses a computed column.
    USE tempdb;
    CREATE TABLE dbo.PersistedViewTest
    (
        PersistedViewTest_ID INT NOT NULL
        CONSTRAINT PK_PersistedViewTest
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
        , SomeData VARCHAR(2000) NOT NULL
        , TestComputedColumn AS (PersistedViewTest_ID - 1) PERSISTED
    );
    GO
    
    -- Insert some test data into the table.
    INSERT INTO dbo.PersistedViewTest (SomeData)
    SELECT o.name + o1.name + o2.name
    FROM sys.objects o
        CROSS JOIN sys.objects o1
        CROSS JOIN sys.objects o2;
    GO
    

    O seguinte plano de execução (sem visualização/visualização de índice) é produzido depois que a seguinte consulta é executada na tabela:

    SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
    FROM dbo.PersistedViewTest pv
    WHERE pv.TestComputedColumn = CONVERT(INT, 26)
    GO
    

    insira a descrição da imagem aqui

    Isso fornece uma linha de base para comparação. Observe que após a conclusão da consulta, um objeto de estatísticas foi criado (_WA_Sys_00000003_1FCDBCEB). O objeto de estatísticas PK_PersistedViewTest foi criado quando o índice de tabela em cluster foi criado.

    Em seguida, a exibição filtrada e o índice clusterizado nessa exibição são criados:

    -- Create filtered view on the computed column.
    CREATE VIEW dbo.PersistedViewTest_View
    WITH SCHEMABINDING
    AS
    SELECT PersistedViewTest_ID, SomeData, TestComputedColumn
    FROM dbo.PersistedViewTest
    WHERE TestComputedColumn < CONVERT(INT, 27);
    GO
    
    -- Create unique clustered index to persist the values, including the computed column.
    CREATE UNIQUE CLUSTERED INDEX IX_PersistedViewTest
    ON dbo.PersistedViewTest_View(PersistedViewTest_ID);
    GO
    

    Agora, vamos tentar executar a consulta novamente, mas desta vez contra a exibição:

    SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
    FROM dbo.PersistedViewTest_View pv
    WHERE pv.TestComputedColumn = CONVERT(INT, 26)
    GO
    

    O novo plano de execução é agora:

    insira a descrição da imagem aqui

    Se o novo plano for confiável, após a adição da exibição e do índice clusterizado nessa exibição, as estatísticas parecem indicar que o tempo necessário para executar a consulta dobrou. Além disso, observe que nenhum novo objeto de estatísticas foi criado para suportar o novo índice após a execução da consulta, o que é diferente da consulta na tabela.

    O plano de consulta ainda sugere que a criação de um índice não clusterizado seria bastante útil para melhorar o desempenho da consulta. Então, isso significa que um índice não clusterizado deve ser adicionado à exibição antes que a melhoria de desempenho desejada possa ser obtida? Há uma última coisa a tentar. Modifique a consulta para usar a opção "WITH NOEXPAND":

    SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
    FROM dbo.PersistedViewTest_View pv WITH (NOEXPAND)
    WHERE pv.TestComputedColumn = CONVERT(INT, 26)
    GO
    

    Isso resulta no seguinte plano de consulta:

    insira a descrição da imagem aqui

    This execution plan looks quite similar to the one that was produced with the nonclustered index given in Hannah's answer. But, this one is done with one less (nonclustered) index and one less statistics object.

    It turns out that the NOEXPAND option has to be used with the express and standard versions of SQL Server to make proper use of an indexed view. Paul White has an excellent article that expounds on the benefits of using the NOEXPAND option. He also recommends this option be used with the enterprise edition to ensure the uniqueness guarantee provided by the view indexes is used by the optimizer.

    A análise acima foi feita com a edição expressa do SQL Server 2014. Também tentei com a edição do desenvolvedor do SQL Server 2016. A opção NOEXPAND não parece ser necessária com a edição de desenvolvimento para obter os ganhos de desempenho, mas ainda é recomendada .

    Há menos de 5 meses, a Microsoft liberou as edições para desenvolvedores . A licença restringe o uso apenas para desenvolvimento, o que significa que o banco de dados não pode ser usado em um ambiente de produção. Portanto, se você está procurando testar tabelas com otimização de memória, criptografia, R etc., não tem mais a desculpa de não ter licença. Eu instalei com sucesso no meu computador há alguns dias junto com o SQL Server 2014 Express sem problemas.

    • 4

relate perguntas

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Quanto "Padding" coloco em meus índices?

  • Como determinar se um Índice é necessário ou necessário

  • O que significa "índice" em RDBMSs? [fechado]

  • Como criar um índice condicional no MySQL?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve