Veamos...
SQL_9iR2> CREATE TABLE test AS
2 SELECT level id, 'texto_'||level texto
3 FROM dual
4 CONNECT BY level <= 100000 ; Table created. SQL_9iR2> EXEC dbms_stats.GATHER_TABLE_STATS(USER,'TEST') ;
PL/SQL procedure successfully completed.
Bien, ahora ejecutemos las 2 consultas:
SQL_9iR2> ALTER SESSION SET SQL_TRACE = TRUE ;
Session altered.
SQL_9iR2> SELECT COUNT(1)
2 FROM test ;
COUNT(1)
----------
100000
1 row selected.
SQL_9iR2> SELECT COUNT(*)
2 FROM test ;
COUNT(*)
----------
100000
1 row selected.
SQL_9iR2> ALTER SESSION SET SQL_TRACE = FALSE ;
Session altered.
Veamos lo que nos muestra el TKPROF:
select count(1)
from
test
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.01 0 306 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.01 0 306 0 1
select count(*)
from
test
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.01 0 306 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.01 0 306 0 1
Observamos que en las 2 consultas tenemos la misma cantidad de lecturas de bloques (disk, query, current) y el mismo tiempo de elapsed y cpu.
Las 2 consultas son idénticas y no hay un incremento en la performance por utilizar el COUNT(1) en vez del COUNT(*).
Incluso podría poner cualquier cosa en el COUNT... obtendríamos los mismos resultados:
SELECT COUNT(2222222)
FROM
TEST
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.01 0 306 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.01 0 306 0 1
SELECT COUNT('EJEMPLO')
FROM
TEST
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.01 0 306 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.01 0 306 0 1
SELECT COUNT(12345)
FROM
TEST
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.01 0 306 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.01 0 306 0 1
Bien, por último veamos los siguiente. Qué sucede si realizo un COUNT de alguna de las columnas de la tabla? Veamos...
SQL_9iR2> SELECT COUNT(texto)
2 FROM test ;
COUNT(TEXTO)
------------
100000
1 row selected.
La consulta nos devolvió la cantidad de registros totales de la tabla. Ahora veamos qué me devuelve la consulta si la columna TEXTO tiene valores nulos.
SQL_9iR2> UPDATE test
2 SET texto = NULL
3 WHERE id > 50000 ;
50000 rows updated.
SQL_9iR2> EXEC dbms_stats.GATHER_TABLE_STATS(USER,'TEST') ;
PL/SQL procedure successfully completed.
SQL_9iR2> SELECT COUNT(texto)
2 FROM test ;
COUNT(TEXTO)
------------
50000
1 row selected.
Como podemos observar, el COUNT sobre una columna sólo cuenta la cantidad de valores que no son nulos. Por lo tanto, tenemos que tener cuidado a la hora de realizar un COUNT y tener en cuenta estos pequeños detalles.
7 comentarios:
Hola Leonardo, muy interesante artículo. Te queria consultar algo que lei en Internet y queria tu opinión. Es sobre el tema de comprobar si existen registros para cierta condición, donde usan la siguiente sentencia: SELECT DISTINCT 1 FROM X WHERE xxx; y mencionan que es más efectivo que realizar SELECT COUNT(*) FROM X WHERE xxx. Gracias
Hola, no se donde leíste eso, pero es muy raro que SELECT DISTINCT 1 FROM X WHERE xxx; sea más eficiente que hacer un COUNT(*). Sinceramente no le veo sentido el hacer un DISTINCT; por otro lado, el DISTINCT realiza un Sort que consume memoria y/o disco, en cambio el COUNT no realiza ningún tipo de sort. Sería conveniente que hagas una prueba utilizando las 2 alternativas (la que yo propongo y la que vos me mencionas) y veas mediante un AUTOTRACE las diferencias entre ambas.
Hola Leonardo, primero muchas felicidades por el apoyo y compartir tus conocimientos, la verdad yo tambien tenia esa incognita...pero ahora veo q no hay diferencia entre las dos instrucciones. Pero tenia otra duda espero que me la puedas despejar es algo similar "Cual la diferencia entre una tabla generada con una consulta SQL y una tabla generada con una vista"
Hola Leonardo, primero muchas felicidades por el apoyo y compartir tus conocimientos, la verdad yo tambien tenia esa incognita...pero ahora veo q no hay diferencia entre las dos instrucciones. Pero tenia otra duda espero que me la puedas despejar es algo similar "Cual la diferencia entre una tabla generada con una consulta SQL y una tabla generada con una vista"
Hola, no logro entender tu pregunta. Podrías explicarla más en detalle? Saludos.
Hola, no se si todavía den soporte a este blog... había escuchado algo de que si en un SP hay una sentencia con un count(*) y se realiza una alteración sobre esta tabla (ejemplo eliminarle una columna)esto causa que se descompile el paquete o el SP...no se si sea cierto, pero de ser así, entonces sería mas eficiente usar el count(1) para que no descompile. Saludos
Hola Pablo Moreno,
Lo que mencionás NO es cierto; es sólo un mito muy antiguo.
Voy a mostrarte que esto que mencionás no es cierto con un simple ejemplo:
SQL> CREATE TABLE tbl_prueba (id NUMBER);
Table created.
SQL> CREATE OR REPLACE PROCEDURE prc_prueba
IS
vCount NUMBER;
BEGIN
SELECT COUNT(*) INTO vCount FROM tbl_prueba;
END;
/
Procedure created.
SQL> SELECT status FROM dba_objects WHERE object_name='PRC_PRUEBA';
STATUS
-------
VALID
SQL> ALTER TABLE tbl_prueba ADD nombre VARCHAR2(40);
Table altered.
SQL> SELECT status FROM dba_objects WHERE object_name='PRC_PRUEBA';
STATUS
-------
VALID
En este ejemplo lo que hice fue crear una tabla con una columna y un procedimiento que realiza un count(*) sobre la tabla. Luego procedí a verificar el estado del procedimiento para asegurarme de que se encuentre en estado válido.
Por último agregué una columna más a la tabla y comprobé que el procedimiento siga estando en estado válido.
Saludos
Publicar un comentario