Pergunta

Em nosso produto temos um motor de busca genérico, e tentando optimze o desempenho da pesquisa. Muitas das tabelas usadas nas consultas permitir valores nulos. Devemos redesenhar nossa mesa para valores nulos rejeitar para otimização ou não?

nosso produto é executado em ambos Oracle e MS SQL Server.

Foi útil?

Solução

Em Oracle, valores NULL não são indexados, i. e. esta consulta:

SELECT  *
FROM    table
WHERE   column IS NULL

sempre usará varredura completa da tabela como o índice não cobre os valores que você precisa.

Mais do que isso, esta consulta:

SELECT  column
FROM    table
ORDER BY
        column

também vai usar varredura completa da tabela e classificar para a mesma razão.

Se os seus valores não permitem intrinsecamente NULL de, em seguida, marcar a coluna como NOT NULL.

Outras dicas

Uma resposta extra para desenhar alguma atenção extra ao comentário de David Aldridge em resposta aceita de Quassnoi.

A declaração:

esta consulta:

SELECT * FROM tabela WHERE column IS NULL

sempre usará mesa cheia de digitalização

Não é verdade. Aqui está o exemplo contador usando um índice com um valor literal:

SQL> create table mytable (mycolumn)
  2  as
  3   select nullif(level,10000)
  4     from dual
  5  connect by level <= 10000
  6  /

Table created.

SQL> create index i1 on mytable(mycolumn,1)
  2  /

Index created.

SQL> exec dbms_stats.gather_table_stats(user,'mytable',cascade=>true)

PL/SQL procedure successfully completed.

SQL> set serveroutput off
SQL> select /*+ gather_plan_statistics */ *
  2    from mytable
  3   where mycolumn is null
  4  /

  MYCOLUMN
----------


1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'))
  2  /

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------
SQL_ID  daxdqjwaww1gr, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ *   from mytable  where mycolumn
is null

Plan hash value: 1816312439

-----------------------------------------------------------------------------------
| Id  | Operation        | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |      |      1 |        |      1 |00:00:00.01 |       2 |
|*  1 |  INDEX RANGE SCAN| I1   |      1 |      1 |      1 |00:00:00.01 |       2 |
-----------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("MYCOLUMN" IS NULL)


19 rows selected.

Como você pode ver, o índice está sendo usado.

Saudações, Rob.

A resposta curta: sim, condicionalmente

O principal problema com valores nulos e desempenho tem a ver com as pesquisas diretas.

Se você inserir uma linha em uma tabela, com valores nulos, ele é colocado na página natural que ele pertence. Qualquer consulta olhando para esse registro vai encontrá-lo no local apropriado. Fácil até agora ....

... Mas vamos dizer que a página enche, e agora essa linha é abraçada por entre as outras linhas. Ainda indo bem ...

... até que a linha é atualizada, eo valor nulo agora contém alguma coisa. O tamanho da fila tem aumentado além do espaço disponível para isso, então o motor DB tem que fazer algo sobre isso.

A coisa mais rápida para o servidor a fazer é mover a linha off essa página para outra, e para substituir a entrada da linha com um ponteiro para a frente. Infelizmente, isso requer uma pesquisa extra quando uma consulta é realizada:. A encontrar a localização natural da linha, e um para encontrar a sua localização actual

Assim, a curta resposta à sua pergunta é sim, fazer aqueles campos não-anulável vai ajudar o desempenho da pesquisa. Isto é especialmente verdadeiro se muitas vezes acontece que os campos que você pesquisar na nulos em registros são atualizados para não nulo.

É claro, existem outras penalidades (nomeadamente I / O, embora a uma profundidade pequena índice de extensão) associado com conjuntos de dados maiores, e então você tem problemas de aplicativos com disallowing nulos em campos que conceitualmente exigem-los, mas hey, isso é outra problema:)

Se a sua coluna não contém NULLs é melhor declarar este NOT NULL coluna, o otimizador pode ser capaz de tomar o caminho mais eficiente.

No entanto, se você tem nulos em sua coluna de você não tem muita escolha (um valor padrão não nulo pode criar mais problemas do que resolve).

Como Quassnoi mencionada, nulos não são indexados no Oracle, ou para ser mais preciso, uma linha não será indexado se todas as colunas indexadas são NULL, isto significa:

  • que nulos podem potencialmente acelerar a sua investigação porque o índice terá menos linhas
  • você ainda pode indexar os registros NULL se você adicionar outra coluna NOT NULL ao índice ou mesmo uma constante.

O script a seguir demonstra uma maneira de valores NULL de índice:

CREATE TABLE TEST AS 
SELECT CASE
          WHEN MOD(ROWNUM, 100) != 0 THEN
           object_id
          ELSE
           NULL
       END object_id
  FROM all_objects;

CREATE INDEX idx_null ON test(object_id, 1);

SET AUTOTRACE ON EXPLAIN

SELECT COUNT(*) FROM TEST WHERE object_id IS NULL;

Eu diria que o teste é necessário, mas é bom saber que outras experiências dos povos. Na minha experiência no servidor MS SQL, nulos podem e causam problemas enormes de desempenho (diferenças). Em um teste muito simples agora vi um retorno de consulta em 45 segundos quando não nula foi definido nos campos relacionados na tabela a instrução CREATE e mais de 25 minutos, onde ele não foi definido (eu desisti de espera e só teve um pico em o plano de consulta estimado).

Os dados de teste é de 1 milhão de linhas x 20 colunas que são construídos a partir de 62 caracteres em minúsculas alfa aleatórios em um i5-3320 HD normal e 8GB de RAM (SQL Server usando 2GB) / SQL Server 2012 Enterprise Edition no Windows 8.1. É importante usar / dados irregulares dados aleatórios para fazer o teste de um caso realista "pior". Em ambos os casos mesa foi recriado e recarregado com dados aleatórios que levou cerca de 30 segundos em arquivos de banco de dados que já tinha uma quantidade adequada de espaço livre.

select count(field0) from myTable where field0 
                     not in (select field1 from myTable) 1000000

CREATE TABLE [dbo].[myTable]([Field0] [nvarchar](64) , ...

 vs

CREATE TABLE [dbo].[myTable]([Field0] [nvarchar](64) not null,

por razões de desempenho ambos tinham opção de tabela DATA_COMPRESSION = conjunto de páginas e tudo o mais foi cumprido. Nenhum índice.

alter table myTable rebuild partition = all with (data_compression = page);

Não ter nulos é um requisito para em tabelas de memória otimizado para que eu não estou usando especificamente servidor no entanto sql irá, obviamente, fazer o que é mais rápido que neste específico caso parece ser maciçamente em favor de não ter nulos nos dados e não usar nulo sobre a mesa criar.

Todas as consultas subsequentes da mesma forma neste retorno mesa em dois segundos, então eu assumiria estatísticas padrão padrão e, possivelmente, com o (1.3GB) Tabela de ajuste na memória estão funcionando bem. i.

select count(field19) from myTable where field19 
                       not in (select field18 from myTable) 1000000

Em um aparte que não têm valores nulos e não ter que lidar com casos nulos também faz consultas muito mais simples, mais curto, menos propenso a erros e muito normalmente mais rápido. Se possível, melhor evitar nulos geralmente no servidor MS SQL, pelo menos, a menos que sejam explicitamente necessário e não pode razoavelmente ser trabalhado para fora da solução.

Começando com uma nova tabela e dimensionamento este até linhas 10m / 13GB mesma consulta leva 12 minutos, que é muito respeitável considerando o hardware e nenhum índice em uso. Para informações consulta foi completamente IO preso com IO pairando entre 20MB / s para 60MB / s. A repetição da mesma consulta levou 9 minutos.

campos anuláveis ??pode ter um grande impacto no desempenho ao fazer "NOT IN" consultas. Porque linhas com todos os campos indexados definido como nulo não são indexados em um índice B-Tree, a Oracle deve fazer uma varredura completa da tabela para verificar se há entires nulos, mesmo quando existe um índice.

Por exemplo:

create table t1 as select rownum rn from all_objects;

create table t2 as select rownum rn from all_objects;

create unique index t1_idx on t1(rn);

create unique index t2_idx on t2(rn);

delete from t2 where rn = 3;

explain plan for
select *
  from t1
 where rn not in ( select rn
                     from t2 );

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 50173 |   636K|  3162   (1)| 00:00:38 |
|*  1 |  FILTER            |      |       |       |            |          |
|   2 |   TABLE ACCESS FULL| T1   | 50205 |   637K|    24   (5)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| T2   | 45404 |   576K|     2   (0)| 00:00:01 |
---------------------------------------------------------------------------

A consulta tem de verificar os valores nulos por isso tem que fazer uma varredura completa da tabela de t2 para cada linha de T1.

Agora, se fizermos os campos não anulável, ele pode usar o índice.

alter table t1 modify rn not null;

alter table t2 modify rn not null;

explain plan for
select *
  from t1
 where rn not in ( select rn
                     from t2 );

-----------------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |        |  2412 | 62712 |    24   (9)| 00:00:01 |
|   1 |  NESTED LOOPS ANTI |        |  2412 | 62712 |    24   (9)| 00:00:01 |
|   2 |   INDEX FULL SCAN  | T1_IDX | 50205 |   637K|    21   (0)| 00:00:01 |
|*  3 |   INDEX UNIQUE SCAN| T2_IDX | 45498 |   577K|     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------

A questão de se usar nulos porque eles afetam o desempenho é um daqueles equilíbrio age de design de banco de dados. Você tem que equilibrar as necessidades de negócios contra o desempenho.

nulos deve ser usado se eles são necessários. Por exemplo, você pode ter uma data e uma data final em uma tabela começar. Muitas vezes você não saberia a data final no momento do registro é criado. Portanto, você deve permitir nulos se eles afetam o desempenho ou não que os dados simplesmente não está lá para ser colocado em. No entanto, se o mosto de dados, pelas regras de negócio, estar lá no momento do registro é criado, então você não deve permitir nulos. Este desempenho iria melhorar, tornar a codificação um pouco mais simples e certifique-se a integridade dos dados é preservada.

Se você tiver dados existentes que você gostaria de mudar para não permitir nulos, então você tem que considerar o impacto dessa mudança. Primeiro, você sabe o valor que você precisa colocar nos registros que são actualmente nulo? Em segundo lugar, você tem um monte de código que está usando isnull ou coalesce que você precisa de atualização (essas coisas desempenho lento, por isso, se você já não necessidade de verificá-los, você deve alterar o código)? Você precisa de um valor padrão? você pode realmente atribuir um? Se não vai algumas das pausa de inserção ou atualização de código se ele não considera que o campo não pode ser nulo. Às vezes as pessoas vão colocar em má informação que lhes permita se livrar de nulos. Então agora as necessidades de campo preço para conter valores decimais e coisas como 'desconhecido' e, portanto, não pode ser adequadamente um tipo de dados decimal e, em seguida, você tem que ir a todos os tipos de comprimentos, a fim de fazer cálculos. Isso muitas vezes cria problemas de desempenho tão ruim ou pior do que o nulo criado. Além disso, você precisa passar por todo o seu código e onde quer que você usou um refernce ao ser arquivado nulo ou não sendo nula, é necessário reescrever para excluir ou incluir baseado na possível que alguém valores ruim vai colocar em becasue os dados não é permitido para ser nulo.

Eu faço um monte de importações de dados a partir de dados do cliente e cada vez que obtemos um arquivo onde algum campo que deve permitir nulos não, obtemos dados de lixo que precisa ser limpos antes de importar para o nosso sistema. E-mail é uma delas. Muitas vezes, os dados são de entrada não saber este valor e é geralmente algum tipo de dados de cadeia, de modo que o usuário pode digitar nada aqui. Vamos para importar e-mails e encontrar coisas "Eu não sei". Difícil de tentar realmente enviar um e-mail com "Eu não sei". Se o sistema requres um endereço de email válido e verifica se há algo como a existência de um sinal @, obteríamos 'I@dont.know" Como é dados de lixo como este útil para os usuários dos dados?

Alguns dos problemas de desempenho com valores nulos são o resultado de escrever consultas nonsargable. Às vezes, apenas rearranjando a cláusula onde ao invés de eliminar um nulo necessário pode melhorar o desempenho.

Em minha experiência NULL é um valor válido e, geralmente, significa "não sei". Se você não sabe, então ele realmente é inútil para compensar algum valor padrão para a coluna ou para tentar impor alguma restrição NOT NULL. NULL só acontece de ser um caso específico.

O verdadeiro desafio para NULLs é complicar um pouco a recuperação. Por exemplo, você não pode dizer de onde column_name IN (NULL, 'value1', 'valor2').

Pessoalmente, se você encontrar muitas de suas colunas, ou determinadas colunas contêm uma grande quantidade de nulos eu acho que você pode querer rever seu modelo de dados. Talvez essas colunas nulos podem ser colocados em uma tabela filho? Por exemplo:. Uma tabela com números de telefone onde seu nome, homephone, celular, faxno, worknumber, emergencynumber etc ... Você pode apenas um preenchimento ou dois deles e seria melhor normalizar-lo

O que você precisa fazer é passo para trás e ver como os dados serão acessados. É este uma coluna que deve ter um valor? É este uma coluna que tem apenas um valor para certos casos? É este uma coluna que será consultado muito?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top