Capítulo 2 de 15 8 secciones 10 min

Conectarte y mirar lo que hay dentro

El cliente que te toca según el motor, y las consultas que se usan para entender una base que no conoces.

Para consultar una base necesitas un cliente: DBeaver funciona con los cuatro motores, y cada uno tiene el suyo (pgAdmin para PostgreSQL, MySQL Workbench, SQL Server Management Studio, DB Browser para SQLite). Una vez dentro, lo primero es entender el esquema: qué tablas hay, qué columnas tiene cada una y cómo se relacionan entre sí.

Hola! Aquí es donde se atasca la gente

En el capítulo 1 escribiste un SELECT y salió. Pero en tu trabajo la base no está en un archivo al lado del cuaderno: está en un servidor, con un usuario, una contraseña y un puerto 🔐

Y este paso, que ningún curso explica bien, es donde muchas personas se quedan. Vamos por partes.

Qué es un cliente

Un cliente es el programa donde escribes tus consultas y ves los resultados. La base vive en el servidor; el cliente es tu ventana a ella.

MotorCliente oficialNota
PostgreSQLpgAdmin, o psql en terminalpsql es feo y es lo que usan los que saben
MySQLMySQL WorkbenchTambién sirve phpMyAdmin si es una web
SQL ServerSQL Server Management Studio (SSMS)Solo Windows. Azure Data Studio es la alternativa multiplataforma
SQLiteDB Browser for SQLiteGratis, ligero, abre el archivo y ya

Y el que te recomiendo de verdad: DBeaver. Es gratis, funciona con los cuatro (y con veinte más), y así aprendes una sola interfaz para toda tu carrera. Yo lo tengo abierto todo el día 💜

La cadena de conexión

Para conectarte hacen falta cinco datos, siempre los mismos: servidor, puerto, base, usuario y contraseña. Lo que cambia es el puerto por defecto y cómo se escribe todo junto.

La cadena de conexión y el puerto de fábrica

PostgreSQLpostgresql://usuario:clave@servidor:5432/mi_base
MySQLmysql://usuario:clave@servidor:3306/mi_base
SQL ServerServer=servidor,1433;Database=mi_base;User Id=usuario;Password=clave;
SQLiteruta/a/tienda.db (no hay servidor, usuario ni puerto)

SQLite es el raro y por una buena razón: no tiene servidor. La base es el archivo, así que conectarte es abrirlo.

Tres avisos que te van a ahorrar una tarde:

  • 🔑 La contraseña nunca va escrita en el código. Va en una variable de entorno o en un gestor de secretos. Si subes una a un repositorio, considérala quemada y cámbiala.
  • 🌐 Si no conecta, casi siempre es red y no clave. Un firewall, una VPN que falta o el servidor que no acepta conexiones de fuera.
  • 👤 Pide un usuario de solo lectura. Para analizar no necesitas poder borrar nada, y así no puedes romper nada por accidente.
De la base transaccional salen dos copias: por ETL hacia el data warehouse, ya ordenado en tablas, y una copia cruda hacia el data lake. Consultar la base transaccional en hora punta frena la venta, así que no es donde se analiza.
Cuando no te dan acceso directo al sistema que factura no es desconfianza: una consulta pesada en hora punta pone lenta la caja. Casi todo el SQL que escribas en tu vida laboral va contra un warehouse, que es la copia hecha para que la interrogues.

Dónde viven los datos que vas a consultar

Un detalle que ahorra un malentendido incómodo el primer mes de trabajo: cuando pides acceso a "la base de datos" y te dan otra cosa, no es desconfianza.

La base donde se registran las ventas mientras ocurren se llama transaccional, y está afinada para escribir rapidísimo, no para que tú le hagas preguntas pesadas. Una consulta tuya con tres JOIN a las once de la mañana puede poner lenta la caja de la tienda 😬

Por eso casi todas las empresas tienen una copia aparte:

  • 🏭 El data warehouse es esa copia ya ordenada en tablas, pensada justo para que la interrogues. El 90% del SQL que escribas en tu vida laboral va contra uno de estos.
  • 🌊 El data lake es la copia cruda, tal como llegó, sin ordenar. Sirve para guardar todo por si acaso, y para trabajar ahí hace falta más herramienta que SQL.
  • 🔁 Y el proceso que mueve los datos de un sitio a otro y los limpia por el camino se llama ETL: extraer, transformar y cargar. Suele correr de madrugada, y por eso el reporte de hoy a veces trae los datos de ayer.

Todo eso es trabajo de ingeniería de datos y no hace falta para este libro. Solo quiero que la palabra no te suene nueva el día que alguien la diga en una reunión 💛

Lo primero al entrar a una base que no conoces

Te dan acceso y te encuentras con doscientas tablas con nombres como TB_MOV_CAB. Esto es lo que hago yo, en este orden 🔍

1. Qué tablas hay

SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;
name
-----------
clientes
detalle
direcciones
pedidos
productos

Listar las tablas

PostgreSQLSELECT table_name FROM information_schema.tables WHERE table_schema = 'public';
MySQLSHOW TABLES;
SQL ServerSELECT name FROM sys.tables;
SQLiteSELECT name FROM sqlite_master WHERE type = 'table';

PostgreSQL, MySQL y SQL Server tienen information_schema.tables, que es lo estándar. Si escribes esa, la misma consulta te sirve en los tres.

2. Qué columnas tiene cada una

PRAGMA table_info(pedidos);
cid  name        type     notnull  dflt_value  pk
---  ----------  -------  -------  ----------  --
0    id          INTEGER  0                    1
1    id_cliente  INTEGER  0                    0
2    fecha       TEXT     0                    0
3    canal       TEXT     0                    0
4    monto       REAL     0                    0

Ver las columnas de una tabla

PostgreSQLSELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'pedidos';
MySQLDESCRIBE pedidos;
SQL Serversp_help 'pedidos';
SQLitePRAGMA table_info(pedidos);

El DESCRIBE de MySQL es el más cómodo y no existe en los otros. information_schema.columns vuelve a ser la forma que viaja.

Fíjate en la columna pk de la salida: dice que id es la clave primaria, o sea lo que identifica a cada fila sin repetirse. Eso lo vas a necesitar en el capítulo de los JOIN.

3. Cómo se relacionan

Los nombres ya te lo cuentan casi todo: pedidos.id_cliente apunta a clientes.id. Esa es una clave foránea, el hilo que une dos tablas.

PRAGMA foreign_key_list(detalle);
id  seq  table      from         to  on_update  on_delete  match
--  ---  ---------  -----------  --  ---------  ---------  -----
0   0    productos  id_producto  id  NO ACTION  NO ACTION  NONE
1   0    pedidos    id_pedido    id  NO ACTION  NO ACTION  NONE

Ahí está el mapa de la base en dos líneas: detalle apunta a productos y a pedidos. O sea que cada línea de detalle es un producto dentro de un pedido 🌟

Ver las claves foráneas

PostgreSQLSELECT * FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY';
MySQLSELECT * FROM information_schema.key_column_usage WHERE referenced_table_name IS NOT NULL;
SQL ServerSELECT * FROM sys.foreign_keys;
SQLitePRAGMA foreign_key_list(detalle);

Y un aviso sobre SQLite: por defecto NO obliga a que se cumplan. Están declaradas y no se revisan salvo que actives PRAGMA foreign_keys = ON. En los otros tres se cumplen siempre.

4. Cuánto pesa cada tabla y qué hay dentro

SELECT COUNT(*) AS pedidos,
       MIN(fecha) AS desde,
       MAX(fecha) AS hasta,
       COUNT(DISTINCT id_cliente) AS clientes,
       COUNT(DISTINCT canal) AS canales
FROM pedidos;
pedidos  desde       hasta       clientes  canales
-------  ----------  ----------  --------  -------
900      2025-01-01  2026-06-24  119       4

Esa consulta la escribo siempre y en cualquier motor funciona igual. En una línea te dice el tamaño, el periodo que cubre y cuánta variedad hay. Si el periodo no es el que esperabas, ya te ahorraste el análisis entero 💜

Y mira el detalle que suelta gratis: hay 120 clientes en la tabla pero solo 119 hicieron algún pedido. O sea que hay uno que se dio de alta y nunca compró. Eso, en un negocio de verdad, es una pregunta 🔍

Y para ver qué valores toma una columna:

SELECT canal, COUNT(*) AS pedidos
FROM pedidos
GROUP BY canal
ORDER BY pedidos DESC;
canal        pedidos
-----------  -------
Web          234
WhatsApp     231
Marketplace  229
Tienda       206

Mirar antes de contar

Antes de sacar un solo número, mira filas de verdad. Diez filas te dicen más que cualquier documentación.

SELECT * FROM pedidos LIMIT 5;
id  id_cliente  fecha       canal        monto
--  ----------  ----------  -----------  ------
1   69          2025-05-30  Web          892.06
2   95          2025-06-03  Marketplace  731.09
3   63          2025-09-23  Tienda       407.39
4   114         2025-02-18  Tienda       358.23
5   67          2025-07-09  Web          844.94

Ahí ya se ve que la fecha viene como texto, que el monto es decimal y que id_cliente es un número que apunta a otra tabla.

Ese SELECT * está bien para mirar y mal para trabajar: en una tabla de cuarenta columnas y millones de filas, traes todo para nada. Para explorar, perfecto; en una consulta que va a quedar guardada, nombra las columnas.

Ejercicios

1. El esquema de clientes

Mira qué columnas tiene la tabla clientes.

PRAGMA table_info(clientes);
cid  name        type     notnull  dflt_value  pk
---  ----------  -------  -------  ----------  --
0    id          INTEGER  0                    1
1    nombre      TEXT     1                    0
2    ciudad      TEXT     0                    0
3    segmento    TEXT     0                    0
4    fecha_alta  TEXT     0                    0
2. La ficha de productos

Saca cuántos productos hay, cuántas categorías, y el precio mínimo y máximo.

SELECT COUNT(*) AS productos,
       COUNT(DISTINCT categoria) AS categorias,
       MIN(precio) AS mas_barato,
       MAX(precio) AS mas_caro
FROM productos;
productos  categorias  mas_barato  mas_caro
---------  ----------  ----------  --------
40         5           4.01        89.35
3. Los valores de una columna

Lista los segmentos de cliente con cuántos hay de cada uno.

SELECT segmento, COUNT(*) AS clientes
FROM clientes
GROUP BY segmento
ORDER BY clientes DESC;
segmento    clientes
----------  --------
Horeca      36
Bodega      35
Minimarket  25
Mayorista   24

Treinta de cada uno, o sea que la base está balanceada a propósito. En datos reales esto casi nunca sale tan redondo, y cuando sale conviene preguntarse por qué 👀

4. El periodo de la base

Averigua desde cuándo y hasta cuándo hay pedidos, y cuántos días son.

SELECT MIN(fecha) AS desde,
       MAX(fecha) AS hasta,
       CAST(julianday(MAX(fecha)) - julianday(MIN(fecha)) AS INTEGER) AS dias
FROM pedidos;
desde       hasta       dias
----------  ----------  ----
2025-01-01  2026-06-24  539

Ese julianday es de SQLite. Aquí es donde más se separan los cuatro motores, y lo vemos entero en el capítulo 5.

5. Escríbelo para los cuatro

Sin ejecutar: escribe "lista las tablas de la base" en los cuatro motores, y di cuál de las formas te serviría en tres de ellos.

-- PostgreSQL, MySQL y SQL Server: la forma estándar
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public';      -- en MySQL, el nombre de tu base

-- Los atajos de cada uno
SHOW TABLES;                              -- MySQL
SELECT name FROM sys.tables;              -- SQL Server
SELECT name FROM sqlite_master
WHERE type = 'table';                     -- SQLite, el único sin information_schema

La de information_schema te sirve en tres. SQLite es el que no la tiene, y tiene sentido: es un archivo, no un servidor con catálogo.

6. La tabla que no existe

Consulta una tabla inventada y lee el error.

SELECT * FROM ventas LIMIT 3;
OperationalError: no such table: ventas

En PostgreSQL sería relation "ventas" does not exist, en MySQL Table 'mi_base.ventas' doesn't exist y en SQL Server Invalid object name 'ventas'. Cuando te pase, casi siempre es que estás en otra base o en otro esquema del que crees 🙃

Lo que te llevas

  • 🔌 Un cliente es tu ventana a la base. DBeaver te sirve para los cuatro.
  • 🔑 La contraseña nunca en el código, y pide usuario de solo lectura.
  • 🗺️ Al entrar a una base nueva: qué tablas hay, qué columnas, cómo se relacionan y cuánto pesan.
  • 📖 information_schema es la forma que viaja entre PostgreSQL, MySQL y SQL Server.
  • 👀 Mira diez filas de verdad antes de contar nada.

En el capítulo 3 empieza lo bueno: filtrar. Y ahí aparece la primera diferencia gorda entre motores, la de limitar filas.

Que tengas lindo día! 🌸

¿Tienes alguna duda o consulta?