SQL es la habilidad que aparece en casi todas las entrevistas para analista de datos, científico de datos e ingeniero de analítica. Ya sea en una sesión de programación en vivo, una tarea para hacer en casa o una pregunta en el pizarrón, te van a pedir que conviertas una pregunta de negocio en una consulta, y que expliques cómo llegaste a ella.

La mejor forma de prepararte no es memorizar la sintaxis. Es practicar con datos realistas hasta que puedas leer una pregunta, imaginar las tablas que necesitas y escribir la consulta paso a paso. Para eso es esta guía.

Vas a resolver seis preguntas de SQL tipo entrevista (tres fáciles, dos intermedias y una difícil), todas basadas en el mismo conjunto de datos: una pequeña tienda en línea. Para cada una verás la pregunta, la respuesta, el resultado y una explicación del razonamiento, incluidas las trampas que les gusta poner a los entrevistadores. Puedes descargar los datos y ejecutar tú mismo cada consulta.

El Conjunto de Datos: Una Pequeña Tienda en Línea

El conjunto de datos tiene cuatro tablas relacionadas, el mismo tipo de estructura que verás en muchas preguntas de entrevista:

  • customers (clientes): quiénes son los clientes y cuándo se registraron.
  • products (productos): lo que vende la tienda, por categoría y precio.
  • orders (pedidos): cada pedido que hizo un cliente, con su estado.
  • order_items (artículos del pedido): los productos y las cantidades de cada pedido.

Se conectan mediante columnas de ID:

customers                orders                    order_items               products
---------                ------                    -----------               --------
customer_id  ◄───────┐   order_id      ◄───────┐   order_item_id        ┌──► product_id
name                 └── customer_id           └── order_id             │    product_name
city                     order_date                product_id  ─────────┘    category
signup_date              status                    quantity                  price

Cada cliente puede tener muchos pedidos, y cada pedido puede contener muchos productos. (Para saber más sobre cómo diseñar tablas como estas, consulta Diseño de Bases de Datos: Guía para Principiantes.)

Descarga los Datos

Descarga el conjunto de datos desde GitHub

La carpeta incluye:

  • setup.sql: crea las cuatro tablas e inserta todos los datos. Funciona en SQLite, PostgreSQL y MySQL 8+.
  • store.db: una base de datos SQLite lista para usar, la forma más rápida de empezar.
  • customers.csv, products.csv, orders.csv y order_items.csv: los datos en bruto, por si prefieres cargarlos en una hoja de cálculo, en Pandas o en otra herramienta.

La forma más rápida de empezar es con SQLite, que es gratuito y no necesita un servidor:

sqlite3 store.db

O crea tú mismo la base de datos con el script:

sqlite3 store.db < setup.sql        # SQLite
psql -d mydb -f setup.sql           # PostgreSQL
mysql mydb < setup.sql              # MySQL 8+

Para practicar con MySQL sin instalarlo, consulta MySQL con Docker.

Las Tablas

La tienda tiene 25 clientes, 16 productos en cuatro categorías, 60 pedidos y 99 artículos de pedido. Aquí están todos los productos y las primeras filas de las demás tablas, para que veas su estructura; los datos completos están en la descarga.

customers

customer_idnamecitysignup_date
1Maria LopezEl Centro2025-01-15
2Carlos RuizCalexico2025-02-03
3Ana TorresBrawley2025-02-20
4Luis GarciaEl Centro2025-03-11
5Sofia MartinezImperial2025-04-02

Primeras 5 de 25 filas.

products

product_idproduct_namecategoryprice
1Wireless MouseElectronics25.00
2Mechanical KeyboardElectronics85.00
3USB-C HubElectronics40.00
4Desk LampHome Office35.00
5Ergonomic ChairHome Office220.00
6Notebook SetStationery12.00
7Gel Pens (12-pack)Stationery9.00
8Monitor StandHome Office45.00
9Webcam HDElectronics60.00
10Noise-Cancelling HeadphonesElectronics150.00
11Standing DeskHome Office380.00
12Desk OrganizerHome Office28.00
13Sticky Notes (6-pack)Stationery7.50
14Planner 2026Stationery18.00
15Laptop SleeveAccessories30.00
16Cable Organizer KitAccessories15.00

orders

order_idcustomer_idorder_datestatus
10112025-08-02completed
10222025-08-05completed
10312025-08-19completed
10432025-08-21cancelled
10542025-09-01completed

Primeras 5 de 60 filas.

order_items

order_item_idorder_idproduct_idquantity
110112
210163
310221
410211
510351

Primeras 5 de 99 filas.

Sobre los resultados de abajo: vienen de ejecutar cada consulta en SQLite. SQLite muestra las cantidades en dólares enteros, como 220, sin decimales, y quita los ceros finales (1682.5); PostgreSQL y MySQL muestran los mismos valores como 220.00 y 1682.50.

Cómo Abordar Cualquier Pregunta de SQL en una Entrevista

Antes de las preguntas, este es un proceso sencillo que funciona para todas, y que a los entrevistadores les encanta verte seguir en voz alta:

  1. Repite la pregunta con tus propias palabras y pregunta todo lo que no esté claro. ¿Cuentan los pedidos cancelados? ¿Qué pasa con los empates?
  2. Identifica las tablas que necesitas y cómo se conectan.
  3. Define el “nivel de detalle” de la respuesta: ¿una fila por qué? ¿Por cliente? ¿Por categoría?
  4. Construye la consulta por pasos: empieza con los JOIN, revisa las filas y luego agrega los filtros, la agrupación y el orden.
  5. Revisa los casos especiales: valores faltantes (NULL), duplicados por los JOIN, empates y resultados vacíos.
  6. Comprueba que el resultado tenga sentido con los datos. Si un número parece demasiado alto, probablemente lo sea.

Preguntas Fáciles

Fácil 1: Filtrar y Ordenar

Pregunta: Muestra los clientes que viven en El Centro, del registro más antiguo al más reciente.

SELECT name, signup_date
FROM customers
WHERE city = 'El Centro'
ORDER BY signup_date;
namesignup_date
Maria Lopez2025-01-15
Luis Garcia2025-03-11
Elena Rossi2025-06-07
Kevin Nguyen2025-08-30
Priya Patel2025-10-28
Mei Tanaka2025-12-02
Lucia Morales2026-01-20

Explicación: WHERE conserva solo las filas que cumplen una condición, y ORDER BY ordena el resultado, de forma ascendente (lo más antiguo primero) de manera predeterminada; agrega DESC para ver lo más reciente primero. Los valores de texto van entre comillas simples.

Lo que revisa el entrevistador: que conozcas la forma básica de una consulta y el orden en que se escriben sus cláusulas: SELECT → FROM → WHERE → ORDER BY. Un buen hábito que vale la pena mencionar: selecciona solo las columnas que necesitas en lugar de usar SELECT *.

Fácil 2: Contar por Grupo

Pregunta: ¿Cuántos pedidos hay en cada estado?

SELECT status, COUNT(*) AS order_count
FROM orders
GROUP BY status
ORDER BY order_count DESC, status;
statusorder_count
completed48
cancelled6
pending6

Explicación: GROUP BY status agrupa los pedidos en un grupo por estado, y COUNT(*) cuenta las filas de cada grupo. AS order_count le da un nombre legible a la columna calculada, que luego puedes usar en ORDER BY.

Lo que revisa el entrevistador: que entiendas la agregación. Una pregunta de seguimiento natural en una entrevista de ciencia de datos es "¿Cuál es la tasa de cancelación?": 6 pedidos cancelados de 60, es decir, el 10%. Puedes calcularla directamente:

SELECT ROUND(100.0 * SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) / COUNT(*), 1) AS cancel_rate_pct
FROM orders;
cancel_rate_pct
10.0

Fíjate en el 100.0: en muchas bases de datos, dividir dos enteros da un entero, así que 6 / 60 daría 0. Multiplicar primero por un decimal evita esa trampa.

Fácil 3: Los N Primeros

Pregunta: ¿Cuáles son los tres productos más caros?

SELECT product_name, category, price
FROM products
ORDER BY price DESC
LIMIT 3;
product_namecategoryprice
Standing DeskHome Office380
Ergonomic ChairHome Office220
Noise-Cancelling HeadphonesElectronics150

Explicación: ordena del precio más alto al más bajo con ORDER BY price DESC y luego conserva las tres primeras filas con LIMIT 3.

Lo que revisa el entrevistador: las preguntas de “los N primeros” son muy comunes. Vale la pena mencionar dos cosas: SQLite, PostgreSQL y MySQL usan LIMIT, mientras que SQL Server usa SELECT TOP 3. Y ¿qué pasa con los empates? Si otro producto también costara $150, LIMIT 3 elegiría uno de los dos de forma arbitraria. Preguntarle al entrevistador cómo deben manejarse los empates muestra justo el tipo de razonamiento que busca, y es la clave de la pregunta difícil más abajo.

Preguntas Intermedias

Intermedia 1: Unir, Agregar y Filtrar Grupos

Pregunta: ¿Qué clientes han gastado más de $800 en pedidos completados? Muestra cuántos pedidos completados hizo cada uno y su gasto total, del mayor al menor.

SELECT
    c.name,
    COUNT(DISTINCT o.order_id) AS completed_orders,
    SUM(oi.quantity * p.price) AS total_spent
FROM customers c
JOIN orders o       ON o.customer_id = c.customer_id
JOIN order_items oi ON oi.order_id   = o.order_id
JOIN products p     ON p.product_id  = oi.product_id
WHERE o.status = 'completed'
GROUP BY c.customer_id, c.name
HAVING SUM(oi.quantity * p.price) > 800
ORDER BY total_spent DESC;
namecompleted_orderstotal_spent
Mei Tanaka42219
Maria Lopez81682.5
Grace Wilson21183
David Kim7896
Sofia Martinez3844

Explicación, paso a paso:

  1. Los JOIN: el gasto está repartido en tres tablas: el cliente está en customers, el estado del pedido en orders, la cantidad en order_items y el precio en products. Los JOIN las conectan mediante sus columnas de ID. (Consulta Los JOINs de SQL Explicados para repasar.)
  2. WHERE conserva solo los pedidos completados, antes de agrupar.
  3. GROUP BY produce una fila por cliente. Agrupar por customer_id además de por name es un buen hábito: dos clientes distintos podrían tener el mismo nombre.
  4. SUM(oi.quantity * p.price) calcula el gasto línea por línea y luego lo suma.
  5. HAVING filtra después de agrupar, sobre el total agregado. Aquí no puedes usar WHERE, porque el total no existe hasta que las filas se agrupan.

Puedes comprobar un total a mano. Grace Wilson tiene dos pedidos completados: el pedido 122 es 2 × $30 + 4 × $85 + $28 = $428 y el pedido 129 es 4 × $150 + $35 + 2 × $60 = $755. En total, $1,183.

La trampa: contar después de un JOIN. Fíjate en el COUNT(DISTINCT o.order_id). Unir con order_items crea una fila por artículo, no por pedido, así que un COUNT normal cuenta artículos. Esto es lo que pasa sin DISTINCT (se muestran los tres primeros):

SELECT c.name, COUNT(o.order_id) AS completed_orders
FROM customers c
JOIN orders o       ON o.customer_id = c.customer_id
JOIN order_items oi ON oi.order_id   = o.order_id
WHERE o.status = 'completed'
GROUP BY c.customer_id, c.name
ORDER BY completed_orders DESC
LIMIT 3;
namecompleted_orders
Maria Lopez14
David Kim10
Mei Tanaka8

Maria hizo 8 pedidos completados, pero esta consulta dice 14, porque esos pedidos contienen 14 artículos en total. Esta “multiplicación por el JOIN” es uno de los errores más comunes en los análisis reales, y una de las trampas más comunes en las entrevistas. Siempre que cuentes después de un JOIN, pregúntate: ¿qué representa ahora una fila?

Lo que revisa el entrevistador: los JOIN entre varias tablas, WHERE vs. HAVING y si notas que los JOIN pueden multiplicar las filas.

Intermedia 2: Encontrar lo que Falta

Pregunta: ¿Qué clientes nunca han hecho un pedido?

SELECT c.customer_id, c.name, c.city, c.signup_date
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL;
customer_idnamecitysignup_date
8James CarterBrawley2025-07-22
10Kevin NguyenEl Centro2025-08-30
21Isabel CruzHoltville2025-12-19
22Noah BennettHeber2026-01-08
24Ethan PriceImperial2026-02-04
25Valeria GomezCalexico2026-02-16

Seis clientes nunca han hecho un pedido, y cuatro de ellos se registraron en los dos últimos meses de los datos. En una entrevista de ciencia de datos, vale la pena comentarlo: ¿los clientes nuevos se están convirtiendo en compradores?

Explicación: un LEFT JOIN conserva todos los clientes, incluso los que no tienen pedidos. En esos clientes, las columnas del pedido se llenan con NULL. Luego WHERE o.order_id IS NULL conserva solo los clientes sin coincidencia. Este patrón se llama anti-join.

Aquí importan dos detalles:

  • Usa IS NULL, nunca = NULL. Nada es igual a NULL (ni siquiera otro NULL), así que = NULL nunca devuelve filas.
  • Revisa una columna que no puede ser nula cuando sí hay coincidencia, como la llave primaria de la tabla (o.order_id).

El mismo patrón responde “¿qué productos nunca se han vendido?”:

SELECT p.product_name, p.category
FROM products p
LEFT JOIN order_items oi ON oi.product_id = p.product_id
WHERE oi.order_item_id IS NULL;
product_namecategory
Monitor StandHome Office

Alternativa con NOT EXISTS: a muchos entrevistadores les gusta ver un segundo enfoque. NOT EXISTS se lee casi como la pregunta: “clientes para los que no existe ningún pedido”:

SELECT c.customer_id, c.name
FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);
customer_idname
8James Carter
10Kevin Nguyen
21Isabel Cruz
22Noah Bennett
24Ethan Price
25Valeria Gomez

(Para saber más sobre subconsultas como esta, consulta Domina las Subconsultas en SQL.)

Lo que revisa el entrevistador: que conozcas la diferencia entre INNER JOIN y LEFT JOIN, cómo se comporta NULL y que muchas veces hay más de una respuesta correcta. Ten cuidado con el parecido NOT IN (SELECT customer_id FROM orders): si la subconsulta llega a devolver un NULL, NOT IN no devuelve ninguna fila, un error clásico que NOT EXISTS evita.

Pregunta Difícil

Difícil: El Producto Principal de Cada Categoría, Incluyendo Empates

Pregunta: Para cada categoría de productos, encuentra el producto que generó más ingresos en pedidos completados. Si dos productos empatan en el primer lugar, muestra ambos.

Esta es una pregunta de “los N primeros por grupo”, una de las preguntas difíciles de SQL más comunes en las entrevistas. El truco es que necesitas clasificar los productos dentro de cada categoría, que es justo para lo que sirven las funciones de ventana.

Paso 1: Calcular los ingresos por producto. Construye siempre las consultas difíciles por pasos, revisando cada uno:

SELECT
    p.category,
    p.product_name,
    SUM(oi.quantity * p.price) AS revenue
FROM order_items oi
JOIN orders o   ON o.order_id   = oi.order_id
JOIN products p ON p.product_id = oi.product_id
WHERE o.status = 'completed'
GROUP BY p.category, p.product_name
ORDER BY p.category, revenue DESC;
categoryproduct_namerevenue
AccessoriesLaptop Sleeve300
AccessoriesCable Organizer Kit135
ElectronicsNoise-Cancelling Headphones1650
ElectronicsMechanical Keyboard1020
ElectronicsWebcam HD900
ElectronicsWireless Mouse350
ElectronicsUSB-C Hub280
Home OfficeStanding Desk3420
Home OfficeErgonomic Chair880
Home OfficeDesk Organizer420
Home OfficeDesk Lamp105
StationeryPlanner 2026180
StationeryNotebook Set180
StationeryGel Pens (12-pack)72
StationerySticky Notes (6-pack)37.5

Ahora puedes ver la respuesta, y el detalle: en Stationery (papelería), Planner 2026 y Notebook Set están empatados con $180.

Paso 2: Clasificar dentro de cada categoría y quedarse con el primero.

WITH product_revenue AS (
    SELECT
        p.category,
        p.product_name,
        SUM(oi.quantity * p.price) AS revenue
    FROM order_items oi
    JOIN orders o   ON o.order_id   = oi.order_id
    JOIN products p ON p.product_id = oi.product_id
    WHERE o.status = 'completed'
    GROUP BY p.category, p.product_name
),
ranked AS (
    SELECT
        category,
        product_name,
        revenue,
        RANK() OVER (PARTITION BY category ORDER BY revenue DESC) AS revenue_rank
    FROM product_revenue
)
SELECT category, product_name, revenue
FROM ranked
WHERE revenue_rank = 1
ORDER BY category, product_name;
categoryproduct_namerevenue
AccessoriesLaptop Sleeve300
ElectronicsNoise-Cancelling Headphones1650
Home OfficeStanding Desk3420
StationeryNotebook Set180
StationeryPlanner 2026180

Explicación:

  • WITH ... AS crea expresiones de tabla comunes (CTE): resultados temporales con nombre que hacen legible una consulta compleja, paso a paso. product_revenue es el Paso 1; ranked agrega la clasificación.
  • RANK() OVER (PARTITION BY category ORDER BY revenue DESC) numera los productos dentro de cada categoría, de los mayores a los menores ingresos. PARTITION BY reinicia la clasificación en cada categoría: es como GROUP BY, pero conserva todas las filas en lugar de juntarlas.
  • Por qué el filtro está en la consulta exterior: no puedes usar una función de ventana en un WHERE, porque WHERE se ejecuta antes de que se calculen las funciones de ventana. Por eso la clasificación se hace en un paso, y el filtro (WHERE revenue_rank = 1) en el siguiente.

La trampa: elegir la función de clasificación equivocada. Muchos candidatos usan ROW_NUMBER(). Esto es lo que pasa si lo haces:

WITH product_revenue AS (
    SELECT p.category, p.product_name, SUM(oi.quantity * p.price) AS revenue
    FROM order_items oi
    JOIN orders o   ON o.order_id   = oi.order_id
    JOIN products p ON p.product_id = oi.product_id
    WHERE o.status = 'completed'
    GROUP BY p.category, p.product_name
)
SELECT category, product_name, revenue
FROM (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY revenue DESC) AS rn
    FROM product_revenue
) t
WHERE rn = 1
ORDER BY category;
categoryproduct_namerevenue
AccessoriesLaptop Sleeve300
ElectronicsNoise-Cancelling Headphones1650
Home OfficeStanding Desk3420
StationeryNotebook Set180

ROW_NUMBER() siempre da números únicos, así que uno de los productos empatados de Stationery desaparece sin avisar, y no está garantizado cuál obtendrás. Las tres funciones de clasificación solo se diferencian en cómo manejan los empates:

IngresosROW_NUMBER()RANK()DENSE_RANK()
100111
90222
90322
80443

Usa RANK() o DENSE_RANK() cuando los empates deban incluirse, y ROW_NUMBER() cuando necesites exactamente una fila por grupo (y agrega un criterio de desempate al ORDER BY para que el resultado sea predecible). Para practicar más con estas funciones, consulta Funciones de Ventana en SQL: Totales Acumulados y Rankings.

Lo que revisa el entrevistador: las CTE, las funciones de ventana, el orden en que SQL ejecuta sus cláusulas y, sobre todo, si preguntaste por los empates antes de escribir la consulta.

Trampas Comunes en las Entrevistas de SQL

  • Contar después de un JOIN. Los JOIN pueden multiplicar las filas. Usa COUNT(DISTINCT ...) o agrega antes de unir.
  • Usar WHERE en lugar de HAVING. WHERE filtra las filas antes de agrupar; HAVING filtra los grupos después.
  • Comparar con NULL usando =. Usa IS NULL e IS NOT NULL.
  • NOT IN con valores NULL. No devuelve filas si la subconsulta contiene un NULL. Prefiere NOT EXISTS.
  • División entera. 6 / 60 puede dar 0. Multiplica primero por 100.0 o convierte a decimal.
  • Ignorar los empates. Pregunta cómo deben manejarse los empates y elige a propósito entre RANK(), DENSE_RANK() y ROW_NUMBER().
  • Filtrar un LEFT JOIN en el lugar equivocado. Una condición sobre la tabla de la derecha en el WHERE (que no sea IS NULL) convierte en silencio un LEFT JOIN en un INNER JOIN. Ponla en el ON.
  • Olvidar las reglas del negocio. ¿Cuentan los pedidos cancelados o pendientes? Di tu suposición en voz alta.

Consejos para Entrevistas de SQL y Ciencia de Datos

  • Explica tu razonamiento en voz alta. A los entrevistadores les importa tanto tu proceso como tu consulta final. Explica qué tablas necesitas y por qué.
  • Haz preguntas para aclarar antes de empezar. Los empates, los NULL, los rangos de fechas y qué estados cuentan son temas válidos, y preguntar por ellos es una fortaleza, no una debilidad.
  • Empieza simple y luego construye. Logra una consulta que funcione para lo esencial de la pregunta, revísala y luego agrega los detalles.
  • Comprueba tu respuesta con los datos. Elige una fila y calcúlala a mano, como los $1,183 de Grace de arriba.
  • Relaciona los resultados con el negocio. En una entrevista de ciencia de datos, después de un número explica qué significa: “Una tasa de cancelación del 10% vale la pena investigarla: ¿las cancelaciones se concentran en un producto o en una ciudad?”.
  • Prepárate para las preguntas de seguimiento. Debes poder hablar del rendimiento (índices y evitar trabajo innecesario; consulta Por Qué Tu Consulta SQL Es Lenta) y de la calidad de los datos (consulta Limpiar Datos Desordenados con SQL Puro).

Practica por tu Cuenta

Estas son más preguntas que puedes responder con el mismo conjunto de datos. Intenta resolverlas antes de buscar nada:

  1. Fácil: ¿Cuál es el precio promedio de los productos de cada categoría?
  2. Fácil: ¿Cuántos clientes se registraron en cada ciudad?
  3. Intermedia: ¿Cuáles fueron los ingresos totales de los pedidos completados en cada mes?
  4. Intermedia: ¿Qué clientes han hecho tanto un pedido completado como uno cancelado?
  5. Difícil: Para cada cliente, muestra cada pedido completado con un total acumulado de su gasto a lo largo del tiempo. (Pista: SUM(...) OVER (PARTITION BY ... ORDER BY ...).)
  6. Difícil: ¿En qué clientes el segundo pedido completado fue mayor que el primero? (Pista: ROW_NUMBER() o LAG().)

Si también trabajas con Python, intenta responder las mismas preguntas con Pandas (cargando los archivos CSV y usando groupby() y merge()) y compara los dos enfoques. Consulta Limpieza de Datos con Python Pandas.

Conclusión

Las preguntas de SQL en las entrevistas siguen unos cuantos patrones: filtrar y ordenar, agrupar y contar, los N primeros, unir y agregar, encontrar lo que falta y clasificar dentro de grupos. Las seis preguntas de esta guía cubren todos, con un solo conjunto de datos que puedes descargar y explorar.

La diferencia entre una buena respuesta y una excelente normalmente no está en la sintaxis: está en notar que un JOIN multiplicó tus filas, que NULL necesita IS NULL o que dos productos empataron en el primer lugar. Practica hasta que esas revisiones se vuelvan un hábito, explica tu razonamiento mientras avanzas, y llegarás a tu próxima entrevista listo para cualquier pregunta.