Cuando usar nchar, varchar, int, smalllint, date, datetime, etc en SQL Server

Tipos de Datos en T-SQL: Guía Práctica para Diseñar Tablas en SQL Server

Cuando se diseña una base de datos en SQL Server, una de las decisiones más importantes es elegir correctamente el tipo de dato para cada columna. Esta elección influye en el rendimiento, la precisión de los cálculos, la calidad de la información y la facilidad para hacer consultas, reportes y validaciones.

En T-SQL no se debe guardar todo como texto ni usar números para datos que realmente no se calculan. Por ejemplo, un teléfono, un CI o un NIT no deberían ser almacenados como números, porque pueden tener guiones, ceros iniciales, extensiones o caracteres especiales.

Regla de oro: elige el tipo de dato más específico posible. Usa números para datos que se calculan, texto para datos descriptivos, fechas para fechas reales y tipos exactos para dinero.

1. Tipos numéricos exactos

Los tipos numéricos exactos se usan cuando el dato representa una cantidad precisa. Son adecuados para identificadores, cantidades, stock, precios, saldos y porcentajes.

Tipo Uso recomendado Ejemplo
TINYINT Enteros pequeños de 0 a 255. edad TINYINT
SMALLINT Enteros medianos. cantidadCuotas SMALLINT
INT Entero más común para IDs, cantidades y stock. idProducto INT IDENTITY
BIGINT Enteros muy grandes. idLog BIGINT IDENTITY
DECIMAL(p,s) Número exacto con decimales. Ideal para dinero. precio DECIMAL(10,2)
BIT Valor lógico: 1, 0 o NULL. activo BIT DEFAULT 1

Para valores monetarios como precios, costos, saldos o totales, lo recomendable es usar DECIMAL. Por ejemplo, DECIMAL(10,2) permite guardar hasta 10 dígitos en total, de los cuales 2 son decimales.

CREATE TABLE VentaDetalle (
  idDetalle INT IDENTITY(1,1) PRIMARY KEY,
  idVenta INT NOT NULL,
  idProducto INT NOT NULL,
  cantidad INT NOT NULL,
  precioUnitario DECIMAL(10,2) NOT NULL,
  descuento DECIMAL(5,2) NOT NULL DEFAULT 0,
  subtotal AS (cantidad * precioUnitario)
);
Consejo: evita usar FLOAT para dinero, porque FLOAT es aproximado y puede generar pequeñas diferencias en los cálculos.

2. Tipos numéricos aproximados

Los tipos FLOAT y REAL se usan para datos aproximados, como mediciones científicas, simulaciones o cálculos estadísticos. No son recomendables para facturación, inventario, impuestos o pagos.

Tipo Cuándo usar Cuándo evitar
REAL Mediciones aproximadas pequeñas. Dinero, precios e impuestos.
FLOAT Ciencia, estadísticas y simulaciones. Facturación, contabilidad y pagos.

3. Tipos de texto

Los tipos de texto se usan para nombres, apellidos, direcciones, observaciones, códigos, teléfonos, CI y NIT. La clave está en identificar si el dato se calcula o simplemente se muestra.

Tipo Uso Ejemplo
CHAR(n) Texto de longitud fija. sexo CHAR(1)
VARCHAR(n) Texto variable sin caracteres especiales Unicode. telefono VARCHAR(20)
NVARCHAR(n) Texto Unicode. Recomendado para nombres con tildes y ñ. nombre NVARCHAR(100)
NVARCHAR(MAX) Texto muy largo. descripcion NVARCHAR(MAX)

Un error común es guardar teléfonos, CI o NIT como INT. Esto no es recomendable porque esos datos pueden incluir signos, letras, ceros iniciales o formatos especiales.

CREATE TABLE Cliente (
  idCliente INT IDENTITY(1,1) PRIMARY KEY,
  nombres NVARCHAR(80) NOT NULL,
  apellidos NVARCHAR(80) NOT NULL,
  ci VARCHAR(20) NOT NULL,
  nit VARCHAR(20) NULL,
  telefono VARCHAR(20) NULL,
  direccion NVARCHAR(150) NULL,
  activo BIT NOT NULL DEFAULT 1
);

4. Fechas y horas

Las fechas no deben guardarse como texto. Si se almacenan como VARCHAR, luego será más difícil ordenar, filtrar por mes, calcular vencimientos o generar reportes.

Tipo Uso recomendado Ejemplo
DATE Solo fecha, sin hora. fechaVencimiento DATE
TIME Solo hora. horaApertura TIME
DATETIME2 Fecha y hora. Recomendado para diseños nuevos. fechaRegistro DATETIME2
DATETIMEOFFSET Fecha y hora con zona horaria. fechaEvento DATETIMEOFFSET
CREATE TABLE LoteProducto (
  idLote INT IDENTITY(1,1) PRIMARY KEY,
  idProducto INT NOT NULL,
  numeroLote VARCHAR(30) NOT NULL,
  fechaVencimiento DATE NOT NULL,
  cantidad INT NOT NULL,
  fechaIngreso DATETIME2 NOT NULL DEFAULT SYSDATETIME()
);

5. Booleanos, estados y catálogos

Cuando una columna solo representa sí o no, se debe usar BIT. Por ejemplo: activo, requiere receta, es controlado o está habilitado.

Cuando existen varios estados posibles, como pendiente, pagada o anulada, se puede usar una restricción CHECK o una tabla catálogo.

CREATE TABLE Venta (
  idVenta INT IDENTITY(1,1) PRIMARY KEY,
  idCliente INT NULL,
  fechaVenta DATETIME2 NOT NULL DEFAULT SYSDATETIME(),
  tipoPago VARCHAR(20) NOT NULL,
  estado VARCHAR(20) NOT NULL DEFAULT 'Pendiente',
  total DECIMAL(12,2) NOT NULL,
  CONSTRAINT CK_Venta_TipoPago
    CHECK (tipoPago IN ('Efectivo', 'QR', 'Tarjeta')),
  CONSTRAINT CK_Venta_Estado
    CHECK (estado IN ('Pendiente', 'Pagada', 'Anulada'))
);

6. Identificadores y claves

El ID interno de una tabla normalmente no debe tener significado de negocio. Es mejor usar un número autogenerado con IDENTITY, porque así no dependemos de datos como CI, NIT o códigos comerciales.

Elemento Tipo recomendado Comentario
Clave primaria común INT IDENTITY(1,1) Adecuada para la mayoría de sistemas.
Clave primaria enorme BIGINT IDENTITY(1,1) Para tablas con muchísimos registros.
Clave externa Mismo tipo que la clave primaria Si la PK es INT, la FK también debe ser INT.
Código de negocio VARCHAR o NVARCHAR Puede ser UNIQUE, pero no necesariamente PK.

7. Archivos, imágenes y datos binarios

Para archivos, imágenes o documentos se puede usar VARBINARY(MAX). Sin embargo, muchas veces es más práctico guardar únicamente la ruta del archivo y almacenar el documento en una carpeta o servicio externo.

CREATE TABLE DocumentoCliente (
  idDocumento INT IDENTITY(1,1) PRIMARY KEY,
  idCliente INT NOT NULL,
  nombreArchivo NVARCHAR(150) NOT NULL,
  rutaArchivo NVARCHAR(260) NULL,
  archivo VARBINARY(MAX) NULL,
  fechaSubida DATETIME2 NOT NULL DEFAULT SYSDATETIME()
);

8. NULL, NOT NULL y DEFAULT

Elegir el tipo de dato no es suficiente. También se debe definir si una columna puede quedar vacía, si tendrá un valor por defecto o si debe cumplir una regla específica.

Elemento Qué significa Ejemplo
NULL La columna puede quedar sin dato. nit VARCHAR(20) NULL
NOT NULL La columna siempre debe tener dato. nombre NVARCHAR(100) NOT NULL
DEFAULT Valor automático si no se manda uno. activo BIT DEFAULT 1
CHECK Regla para limitar valores permitidos. stock >= 0
UNIQUE Evita valores repetidos. ci UNIQUE
CREATE TABLE ProductoRegla (
  idProducto INT IDENTITY(1,1) PRIMARY KEY,
  nombre NVARCHAR(120) NOT NULL,
  precioVenta DECIMAL(10,2) NOT NULL,
  stock INT NOT NULL DEFAULT 0,
  activo BIT NOT NULL DEFAULT 1,
  CONSTRAINT CK_ProductoRegla_Precio CHECK (precioVenta >= 0),
  CONSTRAINT CK_ProductoRegla_Stock CHECK (stock >= 0)
);

9. Tabla guía rápida

Dato que necesito guardar Tipo recomendado Razón
ID principal INT IDENTITY Identificador interno simple.
CI, NIT, teléfono VARCHAR(20) No se calcula y puede tener formato.
Nombre de persona NVARCHAR(80) Acepta tildes y ñ.
Dirección NVARCHAR(150) Texto variable con caracteres especiales.
Stock INT Cantidad entera que se suma o resta.
Precio DECIMAL(10,2) Valor exacto con dos decimales.
Fecha de vencimiento DATE Solo importa la fecha.
Fecha de venta DATETIME2 Importa fecha y hora.
Activo BIT Solo representa sí o no.

10. Errores comunes al elegir tipos de datos

Error Por qué está mal Corrección
precio FLOAT FLOAT es aproximado. precio DECIMAL(10,2)
telefono INT Puede tener +591, guiones o ceros iniciales. telefono VARCHAR(20)
ci INT El CI puede incluir extensión o formato. ci VARCHAR(20)
fecha VARCHAR(20) Dificulta ordenar y calcular vencimientos. fecha DATE o DATETIME2
nombre VARCHAR(MAX) Es excesivo para un nombre. nombre NVARCHAR(80)
permitir NULL en todo Genera registros incompletos. Usar NOT NULL donde sea obligatorio.

Conclusión

Elegir correctamente los tipos de datos en T-SQL es una parte fundamental del diseño de bases de datos. No se trata de usar el tipo más grande, sino el más adecuado para cada columna. Un buen diseño permite mantener datos ordenados, evitar errores, mejorar el rendimiento y facilitar consultas futuras.

En resumen: usa INT IDENTITY para identificadores, NVARCHAR para nombres, VARCHAR para CI, NIT o teléfonos, DECIMAL para dinero, DATE o DATETIME2 para fechas, y BIT para campos de sí/no.

Idea final: un buen diseño de tipos de datos mejora la validación, evita inconsistencias y hace que la base de datos sea más clara, segura y eficiente.

Fuente base: apuntes sobre tipos de datos en T-SQL y documentación de Microsoft Learn sobre Data Types en Transact-SQL.

Comentarios

Entradas populares de este blog

Lo nuevo de SQL Server 2008 respecto a SQL Server 2005

Trabajar para Jalasoft

10 trucos para Excel para aprovecharlo al máximo (II)