Pregunta

En nuestro producto tenemos un motor de búsqueda genérico e intentamos optimizar el rendimiento de la búsqueda. Muchas de las tablas utilizadas en las consultas permiten valores nulos. ¿Deberíamos rediseñar nuestra tabla para no permitir valores nulos para la optimización o no?

Nuestro producto se ejecuta en Oracle y MS SQL Server .

¿Fue útil?

Solución

En Oracle , los valores de NULL no están indexados, i. mi. esta consulta:

SELECT  *
FROM    table
WHERE   column IS NULL

siempre usará el escaneo completo de la tabla ya que el índice no cubre los valores que necesita.

Más que eso, esta consulta:

SELECT  column
FROM    table
ORDER BY
        column

también usará el escaneo completo de la tabla y ordenará por la misma razón.

Si sus valores no permiten intrínsecamente NULL , marque la columna como NOT NULL .

Otros consejos

Una respuesta adicional para llamar la atención sobre el comentario de David Aldridge sobre la respuesta aceptada de Quassnoi.

La declaración:

  

esta consulta:

     

SELECCIONAR * DE la tabla DONDE columna   ES NULO

     

siempre usará el escaneo completo de la tabla

no es cierto. Aquí está el ejemplo del contador usando un índice con un 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 puede ver, se está utilizando el índice.

Saludos, Rob.

Respuesta corta: sí, ¡condicionalmente!

El problema principal con los valores nulos y el rendimiento tiene que ver con las búsquedas directas.

Si inserta una fila en una tabla, con valores nulos, se coloca en la página natural a la que pertenece. Cualquier consulta que busque ese registro lo encontrará en el lugar apropiado. Fácil hasta ahora ...

... pero digamos que la página se llena, y ahora esa fila está acurrucada entre las otras filas. Sigue yendo bien ...

... hasta que se actualice la fila y el valor nulo ahora contenga algo. El tamaño de la fila ha aumentado más allá del espacio disponible, por lo que el motor DB debe hacer algo al respecto.

Lo más rápido que puede hacer el servidor es mover la fila fuera de esa página a otra y reemplazar la entrada de la fila con un puntero hacia adelante. Desafortunadamente, esto requiere una búsqueda adicional cuando se realiza una consulta: una para encontrar la ubicación natural de la fila y otra para encontrar su ubicación actual.

Entonces, la respuesta corta a su pregunta es sí, hacer que esos campos no sean anulables ayudará al rendimiento de la búsqueda. Esto es especialmente cierto si a menudo sucede que los campos nulos en los registros en los que busca se actualizan como no nulos.

Por supuesto, hay otras penalizaciones (especialmente E / S, aunque en un grado de profundidad de índice muy pequeño) asociadas con conjuntos de datos más grandes, y luego tiene problemas de aplicación con no permitir nulos en campos que los requieren conceptualmente, pero bueno, esa es otra problema :)

Si su columna no contiene NULL, es mejor declarar esta columna NOT NULL , el optimizador puede tomar una ruta más eficiente.

Sin embargo, si tiene NULL en su columna, no tiene muchas opciones (un valor predeterminado no nulo puede crear más problemas de los que resuelve).

Como mencionó Quassnoi, los NULL no están indexados en Oracle, o para ser más precisos, una fila no se indexará si todas las columnas indexadas son NULL, esto significa:

  • que los NULL pueden acelerar su investigación porque el índice tendrá menos filas
  • aún puede indexar las filas NULL si agrega otra columna NOT NULL al índice o incluso una constante.

El siguiente script muestra una forma de indexar valores NULL:

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;

Diría que se requieren pruebas, pero es bueno conocer las experiencias de otras personas. En mi experiencia en el servidor ms sql, los valores nulos pueden causar problemas de rendimiento masivos (diferencias). En una prueba muy simple, ahora he visto una consulta que regresa en 45 segundos cuando no se estableció nulo en los campos relacionados en la instrucción de creación de la tabla y más de 25 minutos donde no se configuró (dejé de esperar y tomé un pico en el plan de consulta estimado).

Los datos de prueba son 1 millón de filas x 20 columnas que se construyen a partir de 62 caracteres alfabéticos aleatorios en minúsculas en un i5-3320 HD normal y 8 GB de RAM (SQL Server con 2 GB) / SQL Server 2012 Enterprise Edition en Windows 8.1. Es importante utilizar datos aleatorios / datos irregulares para hacer que la prueba sea realista "peor". caso. En ambos casos, la tabla se recreó y se volvió a cargar con datos aleatorios que tardaron aproximadamente 30 segundos en archivos de base de datos que ya tenían una cantidad adecuada de espacio libre.

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 razones de rendimiento, ambos tenían la opción de tabla data_compression = conjunto de páginas y todo lo demás estaba predeterminado. Sin índices.

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

No tener nulos es un requisito para las tablas optimizadas en memoria para las que no estoy usando específicamente, sin embargo, el servidor SQL obviamente hará lo que sea más rápido, lo que en este caso específico parece ser masivo a favor de no tener nulos en los datos y usar nulo en la tabla crear.

Cualquier consulta posterior de la misma forma en esta tabla regresa en dos segundos, por lo que supongo que las estadísticas estándar predeterminadas y posiblemente tener la tabla (1.3GB) en la memoria están funcionando bien. es decir

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

Por otro lado, no tener nulos y no tener que lidiar con casos nulos también hace que las consultas sean mucho más simples, más cortas, menos propensas a errores y muy normalmente más rápidas. Si es posible, lo mejor es evitar los valores nulos en general en el servidor ms sql al menos a menos que sean explícitamente necesarios y no puedan resolverse razonablemente de la solución.

Comenzar con una nueva tabla y dimensionar esto hasta 10 millones de filas / 13 GB de la misma consulta lleva 12 minutos, lo cual es muy respetable considerando el hardware y sin índices en uso. Para la consulta de información, IO estaba completamente vinculado con IO que oscilaba entre 20MB / sa 60MB / s. Una repetición de la misma consulta tomó 9 minutos.

Los campos anulables pueden tener un gran impacto en el rendimiento cuando se hace " NOT IN " consultas Debido a que las filas con todos los campos indexados configurados como nulos no se indexan en un índice B-Tree, Oracle debe hacer un análisis completo de la tabla para verificar si hay entradas nulas, incluso cuando existe un índice.

Por ejemplo:

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

La consulta debe verificar los valores nulos, por lo que debe realizar un análisis completo de la tabla de t2 para cada fila en t1.

Ahora, si hacemos que los campos no sean anulables, puede usar el í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 |
-----------------------------------------------------------------------------

La cuestión de si usar Nulls porque afectan el rendimiento es uno de esos actos de equilibrio del diseño de la base de datos. Debe equilibrar las necesidades comerciales con el rendimiento.

Los valores nulos deben usarse si son necesarios. Por ejemplo, puede tener una fecha de inicio y una fecha de finalización en una tabla. A menudo no sabría la fecha de finalización en el momento en que se crea el registro. Por lo tanto, debe permitir valores nulos, ya sea que afecten el rendimiento o no, ya que los datos simplemente no están allí para ser ingresados. Sin embargo, si los datos deben, según las reglas comerciales, estar presentes en el momento en que se crea el registro, entonces no debe permitir nulos Esto mejoraría el rendimiento, simplificaría un poco la codificación y garantizaría la preservación de la integridad de los datos.

Si tiene datos existentes que le gustaría cambiar para dejar de permitir valores nulos, entonces debe considerar el impacto de ese cambio. Primero, ¿sabe qué valor necesita poner en los registros que actualmente son nulos? En segundo lugar, ¿tiene una gran cantidad de código que utiliza isnull o coalesce que necesita actualizar (estas cosas ralentizan el rendimiento, por lo que si ya no necesita verificarlas) , deberías cambiar el código)? ¿Necesita un valor predeterminado? ¿Realmente puedes asignar uno? De lo contrario, parte del código de inserción o actualización se romperá si no se considera que el campo ya no puede ser nulo. A veces las personas ponen información incorrecta para permitirles deshacerse de los nulos. Entonces, el campo de precio debe contener valores decimales y cosas como 'desconocido' y, por lo tanto, no puede ser un tipo de datos decimal y luego debe ir a todo tipo de longitudes para hacer los cálculos. Esto a menudo crea problemas de rendimiento tan malos o peores que los nulos creados. ADEMÁS, necesita revisar todo su código y siempre que haya utilizado una referencia para que el archivo sea nulo o no, debe volver a escribir para excluir o incluir en función de los posibles valores incorrectos que alguien pondrá porque los datos no están permitidos ser nulo

Realizo muchas importaciones de datos de los datos del cliente y cada vez que obtenemos un archivo donde algún campo que debería permitir valores nulos no, obtenemos datos basura que deben limpiarse antes de importar a nuestro sistema. El correo electrónico es uno de estos. A menudo, los datos se ingresan sin conocer este valor y generalmente son algún tipo de datos de cadena, por lo que el usuario puede escribir cualquier cosa aquí. Vamos a importar correos electrónicos y encontrar cosas '' No sé ''. Es difícil tratar de enviar un correo electrónico a "No lo sé". Si el sistema requiere una dirección de correo electrónico válida y verifica algo como la existencia de un signo @, obtendríamos 'I@dont.know" ¿Cómo son útiles los datos basura como este para los usuarios de los datos?

Algunos de los problemas de rendimiento con los valores nulos son el resultado de escribir consultas no retenibles. A veces, simplemente reorganizar la cláusula where en lugar de eliminar un nulo necesario puede mejorar el rendimiento.

En mi experiencia, NULL es un valor válido y generalmente significa "no sé". Si no lo sabe, entonces realmente no tiene sentido inventar un valor predeterminado para la columna o intentar aplicar alguna restricción NOT NULL. NULL resulta ser un caso específico.

El verdadero desafío para los NULL es que complica un poco la recuperación. Por ejemplo, no puede decir WHERE column_name IN (NULL, 'value1', 'value2').

Personalmente, si encuentra muchas de sus columnas, o ciertas columnas contienen muchos NULL, creo que es posible que desee volver a visitar su modelo de datos. ¿Quizás esas columnas nulas se pueden poner en una tabla secundaria? Por ejemplo: una tabla con números de teléfono donde está su nombre, teléfono de casa, teléfono celular, número de fax, número de trabajo, número de emergencia, etc ... Solo puede completar uno o dos de ellos y sería mejor normalizarlo.

Lo que debe hacer es retroceder y ver cómo se accederá a los datos. ¿Es esta una columna que debería tener un valor? ¿Es esta una columna que solo tiene un valor para ciertos casos? ¿Es esta una columna que será consultada mucho?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top