Cómo dar formato a SQL como un profesional – formatear a estándares y guías Microsoft implícitas

Hay pocas guías de formatos relacionadas a formatear SQL y estilos de código, pero no hay un estándar universalmente aceptado para SQL Server. En este artículo, sin embargo, seguiremos las guías implícitas de:

  • MSDN
  • Documentación de Libros en Pantalla de SQL Server 2012
  • Y la base de datos Adventure Works 2012 SQL Server

Veremos cómo podemos implementar estos estándares vía ApexSQL Refactor.

ApexSQL Refactor es un complemento gratis para SQL Server Management Studio and Visual Studio add-in and SQL Server Management Studio y Visual Studio y un formateador wSQL con cerca de 200 opciones de formato.

Use el terminador de sentencias

Estándar: Documentación de Libros en Pantalla de SQL Server 2012.

SQL Server requiere el punto y coma sólo en casos particulares:

  1. Para terminar la sentencia previa a una cláusula WITH definiendo una Expresión de Tabla Común (CTE, por sus siglas en inglés)
  2. Para terminar la sentencia previa a una sentencia de servicio SEND o RECEIVE
  3. Para terminar la sentencia MERGE

Por ejemplo, no usar punto y coma para terminar la sentencia MERGE resultará en un error:

Msg 10713, Level 15, State 1, Line 17
La documentación de Libros en Pantalla de SQL Server 2012 indica que no terminar sentencias T-SQL con un punto y coma es una característica deprecada, y que no será soportada en una versión futura de SQL Server.

ApexSQL Refactor tiene una opción para estilos de código SQL, que añade el punto y coma al final de cada sentencia. Debajo del menú ApexSQL Refactor, en el diálogo Formatting options debajo de la pestaña General, seleccione la opción Always use statemente terminator:

Checking the Always use statement terminator option in ApexSQL Refactor

Esta opción terminará todas las sentencias con un punto y coma. Por ejemplo, el siguiente código:

CREATE FUNCTION [dbo].[ufnGetDocumentStatusText](@Status [tinyint])
RETURNS [nvarchar](16) 
AS 

BEGIN
    DECLARE @ret [nvarchar](16);

    SET @ret = 
        CASE @Status
            WHEN 1 THEN N'Pending approval'
            WHEN 2 THEN N'Approved'
            WHEN 3 THEN N'Obsolete'
            ELSE N'** Invalid **'
        END
    
    RETURN @ret
END

Será formateado con la terminación de sentencias:

CREATE FUNCTION [dbo].[ufnGetDocumentStatusText](
                           @Status [tinyint])
RETURNS [nvarchar](16)
AS
BEGIN
       DECLARE @ret [nvarchar](16);

       SET @ret = CASE @Status
		        WHEN 1 THEN N'Pending approval'
			WHEN 2 THEN N'Approved'
			WHEN 3 THEN N'Obsolete'
			ELSE N'** Invalid **'
			END;

       RETURN @ret;
END;

Sangría

Estándar: Documentación de Libros en Pantalla de SQL Server 2012 y base de datos Adventure Works 2012.

Aquí usaremos 4 espacios para la sangría siguiendo la sangría de la base de datos Adventure Works 2012.

Hay tres clases estilos diferentes de sangría de código SQL en SSMS, y también se puede especificar cuántos espacios componen una sola sangría o tabulación.

En SSMS, debajo del menú Tools, haga clic en Options. En la lista desplegable Text Editor elija Transact-SQL y Tabs:

How to format SQL like a pro - Indentation options in SSMS

Cuando la opción None está seleccionada, no se aplica la sangría a la nueva línea cuando se presiona ENTER.

Cuando la opción Block está seleccionada, se aplica automáticamente la sangría a la nueva línea creada al presionar ENTER, en la misma distancia de la línea previa. La opción Smart no está disponible para T-SQL.

La opción Tab size establece la distancia en espacios entre las paradas de tabulación. La opción Indent size establece el tamaño en espacios de una sangría automática.

La opción Insert spaces aplica sangría insertando sólo caracteres de espacio. Si el tamaño de la sangría es establecido a 4, entonces cuatro caracteres de espacio son insertados cuando la tecla TAB es presionada o se hace clic en el botón Increase Indent en la barra de herramientas en la ventana principal de SSMS.

Cuando la opción Keep tabs es seleccionada, la operación de sangría inserta para cada carácter tabulador el número de espacios especificado en Tab size.

En ApexSQL Refactor hay muchas opciones de estilo de código SQL para sangría. Debajo la pestaña General podemos establecer la regla de sangría general, así como la sangría de paréntesis de apertura y cierre. Estableceremos la opción Indent using tabs igual a 4 espacios:

SQL coding style options for indentation in ApexSQL Refactor

Combinaciones

Estándar: Base de datos Adventure Works 2012

Un operador JOIN opera en tablas de dos entradas. Hay tres tipos fundamentales de combinaciones (joins en inglés) – combinaciones cruzadas, combinaciones internas y combinaciones externas. Cada tipo aplica a diferentes conjuntos de fases.

Una COMBINACIÓN CRUZADA aplica sólo a una fase – el Producto Cartesiano. Un producto Cartesiano es una operación matemática que retorna un conjunto de productos desde conjuntos múltiples. Para los conjuntos A y B, el producto Cartesiano A x B es un conjunto de todos los pares ordenados (a, b) donde a ∈ A y b ∈ B. Eso es, cada fila de una entrada es emparejada con todas las filas de la otra. Una tabla puede ser creada tomando el producto Cartesiano de un conjunto de filas y un conjunto de columnas.

A Cartesian product table

Una COMBINACIÓN INTERNA aplica dos fases lógicas de procesamiento de la consulta – un producto Cartesiano entre las tablas de dos entradas como en una combinación cruzada, y luego filtra las filas en base en el predicado especificado en la cláusula ON.

Una COMBINACIÓN EXTERNA aplica tres fases – las dos fases de procesamiento lógico que las combinaciones internas aplican (producto Cartesiano y el filtro ON) y una fase de Añadir Filas Externas. La tercera fase identifica las filas desde la tabla preservada que no encontraron coincidencias en la otra tabla basado en el predicado ON y añade esas filas a la tabla resultado producida por las dos primeras fases de la combinación, y usa marcas NULL para los atributos del lado no preservado de la combinación.

Para lograr formatear el estilo siguiendo la base de datos SQL Adventure Works 2012, debajo del menú ApexSQL Refactor, en el diálogo Formatting options debajo de la pestaña Joins elegiremos las siguientes opciones:

Joins options in ApexSQL Refactor used to achieve formatting style following AW 2012 SQL database

Esto formateará el siguiente código SQL:

 FROM [BOM_cte] cte INNER JOIN [Production].[BillOfMaterials] b 
ON b.[ProductAssemblyID]
= cte.[ComponentID]
      INNER JOIN [Production].[Product] p 
 ON b.[ComponentID] = p.[ProductID]

En código SQL justo como está en la base de datos Adventure Works 2012:

FROM [BOM_cte] AS cte
    INNER JOIN [Production].[BillOfMaterials] AS b
    ON b.[ProductAssemblyID] = cte.[ComponentID]
    INNER JOIN [Production].[Product] AS p
    ON b.[ComponentID] = p.[ProductID]

ApexSQL Refactor también tiene una opción para formatear la cláusula FROM. En el diálogo Formatting options, debajo de la pestaña Data statements elija la opción Move FROM clause to new line y establezca Indent a 0 espacios:

Move FROM clause to new line option in ApexSQL Refactor

Establecer esta opción ayudará adicionalmente a lograr formatear la base de datos Adventure Works 2012 y la siguiente declaración SQL:

CREATE PROCEDURE [dbo].[uspGetAddressInfo] @City nvarchar(30)
AS
BEGIN
SELECT * FROM AdventureWorks2012.Person.Address
WHERE City = @City
END
GO

Será formateada por el estándar de Adventure Works 2012:

CREATE PROCEDURE [dbo].[uspGetAddressInfo]
	  @City nvarchar(30)
AS
BEGIN
       SELECT
	      *
       FROM AdventureWorks2012.Person.Address
       WHERE City = @City;
END;

Elegiremos el mismo formato para la cláusula WHERE y formatearemos las sentencias SELECT anidadas.

Las listas de columnas en la base de datos SQL Adventure Works 2012 están formateadas colocando una lista de columnas en una nueva línea, y poniendo una coma antes de los nombres de las columnas para facilitar la conexión del código. En el menú ApexSQL Refactor, bajo la pestaña Data statements también configuraremos las reglas de formato para la opción Column lists, la cual formateará la siguiente sentencia SQL:

CREATE VIEW [Purchasing].[vVendorWithContacts] AS 
SELECT  v.[BusinessEntityID],v.[Name],ct.[Name] AS [ContactType]  ,p.[Title] 
    ,p.[FirstName] 
    ,p.[MiddleName] 
    ,p.[LastName] ,p.[Suffix] 
    ,pp.[PhoneNumber],pnt.[Name] AS [PhoneNumberType]
    ,ea.[EmailAddress] ,p.[EmailPromotion] 

Setting the formatting rules for the Column lists option

De acuerdo con el estándar de la base de datos SQL Adventure Works 2012:

CREATE VIEW [Purchasing].[vVendorWithContacts] AS 
SELECT 
    v.[BusinessEntityID]
    ,v.[Name]
    ,ct.[Name] AS [ContactType] 
    ,p.[Title] 
    ,p.[FirstName] 
    ,p.[MiddleName] 
    ,p.[LastName] 
    ,p.[Suffix] 
    ,pp.[PhoneNumber] 
    ,pnt.[Name] AS [PhoneNumberType]
    ,ea.[EmailAddress] 
    ,p.[EmailPromotion] 

Alias

Estándar: Sitio MSDN y la base de datos Adventure Works 2012

La legibilidad de una sentencia SELECT puede ser mejorada dando a una tabla un alias. Un alias de tabla puede ser asignado con o sin la palabra reservada AS. En la sentencia FROM un alias puede ser usado para distinguir una tabla o una vista en una combinación a sí misma o una sub consulta y usualmente es un nombre de tabla acortado. Si el mismo nombre de columna existe en más de una tabla en una sentencia JOIN, SQL Server requiere que el nombre de la columna sea calificado por un nombre de tabla, un nombre de vista o alias. Si un alias es definido el nombre de la tabla no puede ser usado.

Por ejemplo, en el siguiente alias de código SQL para la fuente de la tabla de la StateProvince es sp:

CREATE VIEW [dbo].[vw_NewYork]
AS
SELECT p.LastName, p.FirstName, e.JobTitle, a.City, sp.StateProvinceCode
FROM HumanResources.Employee e
       INNER JOIN Person.Person p
       ON p.BusinessEntityID = e.BusinessEntityID
    INNER JOIN Person.BusinessEntityAddress bea 
    ON bea.BusinessEntityID = e.BusinessEntityID 
    INNER JOIN Person.Address a 
    ON a.AddressID = bea.AddressID
    INNER JOIN Person.StateProvince sp 
    ON sp.StateProvinceID = a.StateProvinceID
WHERE a.City = 'Seattle'
 

Si en lugar de un alias intentamos ejecutar el código SQL usando el nombre real de la tabla Person:

CREATE VIEW [dbo].[vw_Seattle]
AS
SELECT p.LastName, p.FirstName, e.JobTitle, a.City, sp.StateProvinceCode
FROM HumanResources.Employee e
       INNER JOIN Person.Person p
       ON p.BusinessEntityID = e.BusinessEntityID
    INNER JOIN Person.BusinessEntityAddress bea 
    ON bea.BusinessEntityID = e.BusinessEntityID 
    INNER JOIN Person.Address a 
    ON a.AddressID = bea.AddressID
    INNER JOIN Person.StateProvince sp 
    ON Person.StateProvinceID = a.StateProvinceID
WHERE a.City = 'Seattle'

 

La ejecución de la consulta SQL resultará en un error:

Msg 4104, Level 16, State 1, Procedure vw_Seattle, Line 12
The multi-part identifier «Person.StateProvinceID» could not be bound.

En ApexSQL Refactor, la opción Alias puede ser establecida bajo la pestaña Data statements:

In ApexSQL Refactor the Alias option can be set under the Data Statements tab

Y la sentencia SQL:

CREATE VIEW [Purchasing].[vVendorWithContacts]  
SELECT  v.[BusinessEntityID],v.[Name],ct.[Name]  [ContactType]  ,p.[Title] 
    ,p.[FirstName] 
    ,p.[MiddleName] 
    ,p.[LastName] ,p.[Suffix] 
    ,pp.[PhoneNumber],pnt.[Name]  [PhoneNumberType]
    ,ea.[EmailAddress] ,p.[EmailPromotion] 

Será formateada por el estándar Adventure Works 2012:

CREATE VIEW [Purchasing].[vVendorWithContacts]  
SELECT
       v.[BusinessEntityID]
  , v.[Name]
  , ct.[Name] AS [ContactType]
  , p.[Title]
  , p.[FirstName]
  , p.[MiddleName]
  , p.[LastName]
  , p.[Suffix]
  , pp.[PhoneNumber]
  , pnt.[Name] AS [PhoneNumberType]
  , ea.[EmailAddress]
  , p.[EmailPromotion]

Mayúsculas y minúsculas

Estándar: Sitio MSDN

De acuerdo a las convenciones de sintaxis MSDN Transact-SQL, las MAYÚSCULAS deberían ser usadas para palabras reservadas de SQL y funciones incorporadas. Los tipos deberían estar en minúsculas:

Capitalization according to MSDN Transact-SQL syntax conventions

Dejaremos el formato de identificadores y variables como están, pero note que no es una buena práctica usar palabras reservadas como identificadores, y que la consistencia en el nombramiento de objetos SQL debería ser preservada usando ya sea Proper case o Camel case y siempre usando identificadores regulares, por ejemplo, ContactType en lugar de “Contact Type”.

Sentencias de esquemas

Estándar: Base de datos Adventure Works 2012

Siguiendo el estándar de formato de la base de datos SQL Adventure Works 2012 en la creación de procedimientos almacenados, bajo el menú ApexSQL Refactor, en el diálogo Formatting options bajo la pestaña Schema statements dé formato a la opción Parameters:

Adventure Works 2012 SQL database formatting standard in creating stored procedures

Usando esta opción lograremos el estilo de formato de la base de datos Adventure Works 2012 para parámetros de procedimientos almacenados desde este código SQL:

CREATE PROCEDURE [dbo].[uspGetWhereUsedProductID] @StartProductID [int],
@CheckDate
[datetime]
AS
BEGIN

A una sentencia SQL apropiadamente formateada:

CREATE PROCEDURE [dbo].[uspGetWhereUsedProductID]
       @StartProductID [int],
       @CheckDate [datetime]
AS
BEGIN

Expresiones

Estándar: Sitio MSDN y base de datos Adventure Works 2012

ApexSQL Refactor ofrece opciones de formato SQL para operaciones lógicas, de comparación y aritméticas.

Operadores lógicos – verifican la veracidad de una condición y retornan un tipo de dato Booleano con un valor de TRUE, FALSE o UNKNOWN.

Operadores aritméticos – usados para realizar operaciones matemáticas en múltiples expresiones y pueden ser usados con tipos de datos numéricos.

Operadores de comparación – verifican si dos expresiones son lo mismo y pueden ser usados con todas las expresiones excepto los tipos de datos text, ntext o image.

Bajo la pestaña Expressions en el diálogo Formatting options establezca la opción Logical operations en Show operations on multiple lines siguiendo el formato de la base de datos Adventure Works 2012:

ApexSQL Refactor's SQL formatting options for formatting logical, comparison and arithmetic operations

Esta opción dará formato al código SQL:

FROM [Production].[BillOfMaterials] b
            INNER JOIN [Production].[Product] p 
            ON b.[ProductAssemblyID] = p.[ProductID] 
        WHERE b.[ComponentID] = @StartProductID 
AND @CheckDate >= b.[StartDate] AND 
@CheckDate <= ISNULL(b.[EndDate], @CheckDate)
        UNION ALL

De acuerdo al estándar de la base de datos Adventure Works 2012:

FROM [Production].[BillOfMaterials] b
     INNER JOIN [Production].[Product] p 
     ON b.[ProductAssemblyID] = p.[ProductID] 
WHERE b.[ComponentID] = @StartProductID 
    AND @CheckDate >= b.[StartDate] 
    AND @CheckDate <= ISNULL(b.[EndDate], @CheckDate)
UNION ALL

Control de flujo

Estándar: Sitio MSDN

Indicar que una sentencia cubre más que una línea usando la combinación BEGIN/END hace al código más fácil de leer porque eso indica claramente el inicio y el final de la sentencia. Generalmente, las sentencias BEGIN/END son usadas en bucles WHILE, pero también son usados en procedimientos almacenados y sentencias IF según el estándar MSDN.

Para reforzar el envolvimiento de procedimientos almacenados con la combinación de las sentencias BEGIN/END en el diálogo Formatting options, bajo la pestaña Flow control seleccione las opciones Always use BEGIN and END in the IF statements y Always use BEGIN and END in stored procedures:

Always use BEGIN and END in stored procedures options found in ApexSQL Refactor

De esta manera todos los procedimientos almacenados serán formateados con la sentencia BEGIN/END, y escribir la sentencia CREATE PROCEDURE:

CREATE PROCEDURE [dbo].[uspGetAddressInfo] @City nvarchar(30)
AS
SELECT * FROM AdventureWorks2012.Person.Address
WHERE City = @City

GO

Envolverá la sentencia con las sentencias BEGIN/END:

CREATE PROCEDURE [dbo].[uspGetAddressInfo]
	  @City nvarchar(30)
AS
BEGIN
       SELECT
	      *
       FROM AdventureWorks2012.Person.Address
       WHERE City = @City;
END;

GO

Esto redondeará las opciones de formato. Para probar las configuraciones de formato formatearemos el siguiente código:

FROM Purchasing.Vendor v
INNER JOIN Person.BusinessEntityContact bec ON bec.BusinessEntityID 
= v.BusinessEntityID
INNER JOIN Person.ContactType ct ON ct.ContactTypeID = bec.ContactTypeID 
INNER JOIN Person.Person p
ON p.BusinessEntityID = bec.PersonID LEFT OUTER JOIN Person.EmailAddress ea
	     ON ea.BusinessEntityID = p.BusinessEntityID
	     LEFT OUTER JOIN Person.PersonPhone pp
	     ON pp.BusinessEntityID = p.BusinessEntityID
	     LEFT OUTER JOIN Person.PhoneNumberType pnt ON pnt.PhoneNumberTypeID 
= pp.PhoneNumberTypeID;

Nuestras configuraciones de formato nos darán el siguiente estilo de código:

   FROM Purchasing.Vendor AS v
       INNER JOIN Person.BusinessEntityContact AS bec
       ON bec.BusinessEntityID = v.BusinessEntityID
       INNER JOIN Person.ContactType AS ct
       ON ct.ContactTypeID = bec.ContactTypeID
       INNER JOIN Person.Person AS p
       ON p.BusinessEntityID = bec.PersonID
       LEFT OUTER JOIN Person.EmailAddress AS ea
       ON ea.BusinessEntityID = p.BusinessEntityID
       LEFT OUTER JOIN Person.PersonPhone AS pp
       ON pp.BusinessEntityID = p.BusinessEntityID
       LEFT OUTER JOIN Person.PhoneNumberType AS pnt
       ON pnt.PhoneNumberTypeID = pp.PhoneNumberTypeID;

aquí

Para usar estas configuraciones simplemente extraiga el código XML descargado.

En el diálogo Formatting options de ApexSQL Refactor haga clic en el botón Import e importe la plantilla de estilo de código SQL a ApexSQL Refactor. En el menú desplegable Formatting template nombre la nueva plantilla y seleccione la opción Use as default:

Importing SQL coding style template to ApexSQL Refactor

Recursos útiles:
Books Online for SQL Server 2012 – Deprecated Database Engine Features in SQL Server 2012
Books Online for SQL Server 2012 – Manage code formatting

Traductor: Daniel Calbimonte

noviembre 14, 2015