Ir al contenido principal

Haz bien tus consultas: Llama a las cosas por su nombre (y esquema)

La MSDN nos explica cual es la forma correcta de referenciar a los objetos en tus consultas, y es la siguiente:

Esquema.Nombre

¿Por qué? Hay diversos motivos y, aunque parezca algo trivial, pueden evitarnos acceder a objetos incorrectos, errores en consultas para usuarios de la base de datos distintos al que creó el objeto e incluso problemas de rendimiento.

Resolución de nombres

Cuando ejecutamos una consulta, el servidor SQL realiza un proceso llamado resolución de nombres (name resolution, en inglés) para identificar los objetos referidos en ella.

Si identificamos cada objeto por su nombre completo (fully qualified name), SQL sabrá determinar inmediatamente a qué objeto nos referimos y si existe o no. Por el contrario, si solamente especificamos su nombre, el motor de bases de datos tendrá que determinar a qué objeto nos referimos. Para ello usará el default schema del usuario conectado.

Esquema por defecto o Default schema

Cada usuario de una base de datos puede tener un esquema por defecto (desde SQL Server 2008 R2, también los grupos de Windows. Esto es importante, como veremos a continuación). Si para un usuario no está definido el esquema por defecto, se asume que éste es "dbo".

Para comprobar el esquema por defecto que tienen los usuarios de nuestra base de datos, podemos ejecutar la siguiente consulta:

Cuando SQL tiene que resolver un nombre de objeto sin esquema, primero busca en el esquema por defecto del usuario actual. Si no lo encuentra ahí, entonces lo buscará en el esquema dbo. Si tampoco estuviera ahí, la consulta fallaría.

Es conveniente que el esquema por defecto de todos los usuarios y grupos sea dbo o no esté definido (NULL), salvo que haya razones muy concretas para que no sea así.

Cuando el esquema por defecto de un usuario es otro, se dan los problemas descritos a continuación.

Problemas relacionados con el esquema por defecto

  • Si un usuario tiene un esquema por defecto que no es dbo, cuando cree nuevos objetos en la base de datos, estos se llamarán esquema_usuario.nombre_objeto. Si cualquier otro usuario con un esquema por defecto distinto hace una consulta sobre dicho objeto sin especificar su esquema, la consulta fallará.
  • Si se crea un objeto con el esquema dbo, pero un usuario con el esquema por defecto distinto de dbo hace una consulta se hace sin especificar el esquema, SQL buscará el objeto primero en su esquema por defecto y después en dbo (name resolution). Esto puede producir problemas de rendimiento. Si bien estos problemas no serían dramáticos, se han medido que entre el 10% y el 15% del proceso de ejecución de una consulta puede ser debido a la resolución de nombres.
Los esquemas existen desde SQL Server 2005 y ofrecen multitud de posibilidades de organización y control de seguridad sobre nuestros objetos. Sin embargo, aunque no los uses y todos tus objetos estén en el esquema dbo, que es el por defecto, es posible que el esquema por defecto de alguno de tus usuarios no sea dbo.

Para comprobar qué usuarios no tienen por defecto el esquema "dbo", puedes ejecutar el siguiente script:

La MSDN explica muy bien todo este proceso en su sección "How to Refer to Objects" (como referirse a objetos) del artículo -en inglés- "SQL Server Best Practices - Implementation of Database Object" (Prácticas recomendadas en SQL Server - Implementación de Objetos de Base de Datos):
Es siempre una buena práctica referirse a los objetos de la base de datos por el nombre de esquema y el nombre de objeto separados por un punto.
...
El proceso [de resolución de nombres] puede mejorarse usando el nombre completo o configurando correctamente el esquema por defecto del usuario, eliminando un proceso innecesario de comprobación de propiedad. Esto puede mejorar considerablemente el rendimiento en sistemas utilizados intensivamente.

Problema adicional con grupos de usuarios de Windows en versiones anteriores a SQL Server 2012

Si asignas permisos a tus bases de datos a grupos de usuarios de Windows en versiones de SQL Server anteriores a 2012, cuando los usuarios de dichos grupos accedan a la base de datos, SQL Server les creará un usuario cuyo esquema por defecto será su login de Windows.

Esto hace que el problema descrito de resolución de nombres se agrave especialmente en este tipo de entornos con versiones de SQL Server 2005 o 2008, en los que el administrador de bases de datos gestione los permisos de esta manera.

Por ejemplo: Si el usuario MIDOMINIO\Usuario1 pertenece al grupo MIDOMINIO\Grupo1 y el Usuario1 se conecte por primera vez a una base de datos para la que tenga permisos asignados a través del Grupo1, SQL Server le asignará como esquema por defecto el valor "MIDOMINIO\Usuario1".

A partir de este momento, cada objeto que cree el Usuario1 sin definir el esquema correctamente se llamará de la forma:

MIDOMINIO\Usuario1.NombreObjeto.

Y todas las consultas que haga el usuario sin especificar el esquema en los nombres de objeto penalizarán el rendimiento de SQL Server. Por ejemplo:

Además, si otros usuarios realizan esta misma consulta, fallará, ya que SQL Server no será capaz de encontrar el objeto si no se especifica el nombre de esquema.

Conclusiones

  • Si eres DBA: Asegúrate que los usuarios de tus bases de datos tengan asignado como esquema por defecto dbo o NULL.
  • Si eres programador: Escribe todas tus consultas haciendo referencia a los objetos por su nombre completo, de la forma nombre_de_esquema.nombre_de_objeto

Comentarios

Entradas populares de este blog

Aprendiendo a usar LEFT OUTER JOIN

En esta entrada pretendemos explicar los diferentes resultados obtenidos por distintas construcciones de consultas que, aparentemente, deberían producir el mismo conjunto de resultados. Así, veremos las diferencias entre filtrar los resultados de una query en la unión (Join) mediante condiciones ON y mediante cláusulas WHERE.

Variantes del SELECT COUNT con DISTINCT

Seguramente, muchos de vosotros habréis usado en innumerables ocasiones la función de T-SQL COUNT , que no hace sino devolver un número de registros: de una tabla, de un conjunto de resultados, etc... En una de sus aplicaciones, combinado con el DISTINCT -uno de los dos argumentos que admite- COUNT nos devuelve el número de valores únicos no nulos de la tabla o conjunto de resultados que estemos consultando. Pero ¡ojo! Cuidado con la sintaxis , o podemos obtener el valor equivocado sin darnos cuenta. No es lo mismo: SELECT COUNT (DISTINCT NombreCampo) FROM NombreTabla que: SELECT COUNT(*), DISTINCT NombreCampo FROM NombreTabla

Script para obtener el tamaño de todas las tablas de la base de datos

En algunas ocasiones podemos vernos con la necesidad de conocer qué tablas de nuestra base de datos están ocupando más espacio en disco. Por ejemplo, si disponemos de SQL Server Express , cuyas bases de datos están limitadas a 4GB o 10GB, según la versión que estemos usando -4, hasta 2005; 10, a partir de 2008-, aparte de usar las opciones de comprimir la base de datos, poner el log en el modo simple de recuperación o ajustar las políticas de crecimiento automático de nuestros ficheros, podemos necesitar averiguar qué tablas crecen más para tomar las decisiones oportunas.