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.
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)
);
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.
Fuente base: apuntes sobre tipos de datos en T-SQL y documentación de Microsoft Learn sobre Data Types en Transact-SQL.
Comentarios
Publicar un comentario