AdSense Placeholder
Slot: header_reference_page
SQL es el lenguaje para consultar y modificar bases de datos relacionales. Todas las sentencias de esta página se ejecutaron en SQLite 3.46 con el pequeño esquema customers / orders creado en la primera sección; donde MySQL, PostgreSQL o SQL Server difieren, la última sección lo indica. Ordene las consultas largas con el formateador de SQL.
Crear tablas
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE,
country TEXT DEFAULT 'US',
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
total NUMERIC NOT NULL CHECK (total >= 0),
status TEXT NOT NULL DEFAULT 'new'
);
SQLite solo aplica REFERENCES (y, por tanto, ON DELETE CASCADE) tras ejecutar PRAGMA foreign_keys = ON; en cada conexión; las demás bases de datos siempre lo aplican.
| Sentencia |
Qué hace |
ALTER TABLE customers ADD COLUMN phone TEXT; |
Añade una columna |
ALTER TABLE customers RENAME COLUMN phone TO mobile; |
Renombra una columna |
ALTER TABLE customers DROP COLUMN mobile; |
Elimina una columna |
CREATE INDEX idx_orders_customer ON orders(customer_id); |
Acelera las búsquedas y los joins por esa columna |
CREATE UNIQUE INDEX idx_email ON customers(email); |
Además impone unicidad |
DROP TABLE IF EXISTS old_orders; |
Elimina una tabla y sus datos |
CREATE VIEW big_orders AS SELECT * FROM orders WHERE total > 100; |
Guarda una consulta como tabla virtual |
Insertar, actualizar y eliminar
| Sentencia |
Qué hace |
INSERT INTO customers (name, email) VALUES ('Ana', '[email protected]'); |
Añade una fila |
INSERT INTO orders (customer_id, total) VALUES (1, 40), (1, 120), (2, 75); |
Añade varias filas |
INSERT INTO archive SELECT * FROM orders WHERE status = 'done'; |
Copia filas desde una consulta |
UPDATE orders SET status = 'paid' WHERE id = 2; |
Modifica las filas que coinciden |
DELETE FROM orders WHERE status = 'cancelled'; |
Elimina las filas que coinciden |
INSERT INTO customers (id, name, email) VALUES (1, 'Ana B.', '[email protected]') ON CONFLICT(id) DO UPDATE SET name = excluded.name; |
Upsert: inserta o, si la clave existe, actualiza |
INSERT INTO customers (name) VALUES ('Di') RETURNING id; |
Devuelve los valores de la fila nueva (SQLite 3.35 y posteriores) |
Un UPDATE o DELETE sin WHERE modifica todas las filas. Ejecute antes el mismo WHERE en un SELECT. Convierta la exportación de una hoja de cálculo en sentencias INSERT con el conversor de CSV a SQL.
Consultar
| Cláusula |
Ejemplo |
Notas |
SELECT |
SELECT name, email FROM customers; |
SELECT * devuelve todas las columnas |
WHERE |
WHERE country = 'US' AND total > 50 |
Filtra las filas antes de agrupar |
IN, BETWEEN |
WHERE status IN ('new','paid'), WHERE total BETWEEN 50 AND 100 |
BETWEEN incluye ambos extremos |
LIKE |
WHERE email LIKE '%@example.com' |
% cualquier texto, _ un carácter |
IS NULL |
WHERE email IS NULL |
= NULL nunca coincide |
ORDER BY |
ORDER BY total DESC, id |
Por defecto es ascendente |
LIMIT / OFFSET |
LIMIT 10 OFFSET 20 |
Filas 21 a 30 |
DISTINCT |
SELECT DISTINCT country FROM customers; |
Elimina las filas duplicadas |
AS |
SELECT total * 1.2 AS gross FROM orders; |
Alias de columna o de tabla |
CASE |
CASE WHEN total >= 100 THEN 'large' ELSE 'small' END |
Valor condicional |
COALESCE |
COALESCE(email, 'none') |
Primer argumento no NULL |
Las cláusulas aparecen siempre en este orden: SELECT ... FROM ... JOIN ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT.
Joins
| Join |
Devuelve |
INNER JOIN |
Solo las filas con coincidencia en ambos lados |
LEFT JOIN |
Todas las filas de la izquierda, con NULL donde la derecha no coincide |
RIGHT JOIN |
Todas las filas de la derecha (SQLite 3.39 y posteriores) |
FULL OUTER JOIN |
Todas las filas de ambos lados (SQLite 3.39 y posteriores; no existe en MySQL) |
CROSS JOIN |
Todas las combinaciones de filas |
-- Customers with no orders
SELECT c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;
Agrupaciones y agregados
| Función |
Devuelve |
COUNT(*) |
Número de filas |
COUNT(col) |
Número de valores no NULL |
SUM(col), AVG(col) |
Total y media (los NULL se ignoran) |
MIN(col), MAX(col) |
Valor menor y mayor |
-- Revenue per customer, only customers above 100
SELECT customer_id, COUNT(*) AS orders, SUM(total) AS revenue
FROM orders
GROUP BY customer_id
HAVING SUM(total) > 100
ORDER BY revenue DESC;
WHERE filtra las filas antes de agrupar; HAVING filtra los grupos después.
Subconsultas, CTE y funciones de ventana
-- Orders above the average
SELECT * FROM orders WHERE total > (SELECT AVG(total) FROM orders);
-- Common table expression
WITH revenue AS (
SELECT customer_id, SUM(total) AS amount FROM orders GROUP BY customer_id
)
SELECT c.name, r.amount FROM revenue r JOIN customers c ON c.id = r.customer_id;
-- Rank each customer's orders and keep a running total
SELECT customer_id, id, total,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY total DESC) AS rank_in_customer,
SUM(total) OVER (ORDER BY id) AS running_total
FROM orders;
Transacciones
| Sentencia |
Qué hace |
BEGIN; |
Inicia una transacción (START TRANSACTION en MySQL, BEGIN TRANSACTION en SQL Server) |
COMMIT; |
Hace permanentes los cambios |
ROLLBACK; |
Deshace todo desde el BEGIN |
SAVEPOINT s1; / ROLLBACK TO s1; |
Deshacer parcial dentro de una transacción |
Diferencias entre dialectos
| Tarea |
SQLite |
PostgreSQL |
MySQL / MariaDB |
SQL Server |
| Primeras 10 filas |
LIMIT 10 |
LIMIT 10 |
LIMIT 10 |
SELECT TOP 10 ... o OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY |
| Clave autoincremental |
INTEGER PRIMARY KEY |
GENERATED ALWAYS AS IDENTITY o SERIAL |
AUTO_INCREMENT |
IDENTITY(1,1) |
| Concatenar cadenas |
a || b |
a || b |
CONCAT(a, b) |
a + b o CONCAT(a, b) |
| Upsert |
ON CONFLICT ... DO UPDATE |
ON CONFLICT ... DO UPDATE |
ON DUPLICATE KEY UPDATE |
MERGE |
| Devolver la fila insertada |
RETURNING |
RETURNING |
no admitido (MariaDB: RETURNING) |
OUTPUT inserted.* |
| Hora actual |
CURRENT_TIMESTAMP, datetime('now') |
CURRENT_TIMESTAMP, now() |
CURRENT_TIMESTAMP, NOW() |
CURRENT_TIMESTAMP, GETDATE() |
| Entrecomillar un identificador |
"name" |
"name" |
`name` |
[name] |
| Comparación sin distinguir mayúsculas |
LIKE (solo ASCII) |
ILIKE |
LIKE (con las intercalaciones por defecto) |
LIKE (con las intercalaciones por defecto) |
| Tipo booleano |
ninguno: 0 y 1 |
BOOLEAN |
BOOLEAN (alias de TINYINT(1)) |
BIT |
Seguridad
No construya nunca SQL pegando entradas del usuario en una cadena: así ocurre la inyección SQL. Use marcadores de posición (?, $1, :name o %s, según el controlador) y pase los valores por separado.