{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Qué es SQL y por qué hay cuatro\n",
    "\n",
    "MySQL, SQL Server, PostgreSQL y SQLite: qué comparten, qué no, y cuál te va a tocar en el trabajo.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 1 de **SQL desde cero**, de Miss Yera.\n",
    "\n",
    "Corre de arriba abajo. Si lo abres en Google Colab no necesitas instalar nada.\n",
    "\n",
    "Capítulo completo: https://missyera.com/guias/sql-desde-cero/que-es-sql/\n",
    "\n",
    "Este es el cuaderno de **soluciones**. Trae el código de cada ejercicio, la\n",
    "explicación de la trampa y la respuesta del quiz. Si vienes del cuaderno de\n",
    "práctica sin haberlo intentado, vuelve 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Antes de empezar\n",
    "\n",
    "Se baja la base y se deja lista una función `q()` que corre las consultas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sqlite3\n",
    "import urllib.request\n",
    "\n",
    "import pandas as pd\n",
    "\n",
    "urllib.request.urlretrieve(\"https://missyera.com/static/datasets/tienda.db\", \"tienda.db\")\n",
    "con = sqlite3.connect(\"tienda.db\")\n",
    "\n",
    "def q(sql):\n",
    "    \"\"\"Corre las sentencias del bloque y devuelve la ultima como tabla.\n",
    "\n",
    "    Parte por sentencias igual que la consola de SQLite, porque un bloque puede\n",
    "    traer varias y un CREATE TRIGGER lleva punto y coma dentro de su cuerpo.\n",
    "    \"\"\"\n",
    "    resultado = None\n",
    "    trozo = \"\"\n",
    "    for linea in sql.splitlines(keepends=True):\n",
    "        trozo += linea\n",
    "        if sqlite3.complete_statement(trozo):\n",
    "            if trozo.strip():\n",
    "                cur = con.execute(trozo.strip())\n",
    "                resultado = (pd.DataFrame(cur.fetchall(),\n",
    "                                          columns=[d[0] for d in cur.description])\n",
    "                             if cur.description else None)\n",
    "            trozo = \"\"\n",
    "    if trozo.strip():\n",
    "        cur = con.execute(trozo.strip())\n",
    "        resultado = (pd.DataFrame(cur.fetchall(),\n",
    "                                  columns=[d[0] for d in cur.description])\n",
    "                     if cur.description else None)\n",
    "    con.commit()\n",
    "    return resultado\n",
    "\n",
    "q(\"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Hola! Empecemos por la confusión más común"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vas a buscar \"cómo aprender SQL\" y en dos minutos aparecen cuatro nombres:\n",
    "MySQL, SQL Server, PostgreSQL y SQLite. Y la pregunta obvia: **¿cuál\n",
    "aprendo?** 😅\n",
    "\n",
    "Te doy la respuesta de una y después la explico: **aprendes SQL, no un\n",
    "motor**. El 80% de lo que vas a escribir en tu vida es idéntico en los\n",
    "cuatro. Lo que cambia es un 20% que se puede tener en una tabla al lado, y en\n",
    "este libro lo vas a tener en cada capítulo 💜\n",
    "\n",
    "Y antes de arrancar, contéstate una: **¿alguna vez copiaste una consulta de internet y no te corrió?** Casi seguro no era tu culpa ni de la consulta. Era que estaba escrita para otro motor, y al terminar este capítulo vas a saber reconocerlo de un vistazo 🕵️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué es SQL, sin rodeos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SQL es el lenguaje para pedirle datos a una base de datos. Se lee casi como\n",
    "una frase en inglés:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "SELECT nombre, ciudad\n",
    "FROM clientes\n",
    "WHERE ciudad = 'Lima';\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\"Selecciona el nombre y la ciudad, de la tabla clientes, donde la ciudad sea\n",
    "Lima\". Ya está, eso es SQL.\n",
    "\n",
    "Y aquí está lo que lo hace especial: **tú dices QUÉ quieres, no CÓMO\n",
    "buscarlo**. No le explicas que recorra las filas ni por dónde empezar. El\n",
    "motor decide eso solo, y suele decidirlo mejor que tú.\n",
    "\n",
    "Por eso SQL lleva cincuenta años en pie mientras aparecen y desaparecen\n",
    "lenguajes: lo que se pide no cambia aunque cambie la forma de guardarlo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Motor, base de datos y SQL, que no son lo mismo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esta confusión la tiene todo el mundo al empezar, así que la aclaro con una\n",
    "imagen que a mí me sirvió.\n",
    "\n",
    "- 📖 **SQL** es el idioma.\n",
    "\n",
    "- ⚙️ **El motor** (PostgreSQL, MySQL, SQL Server, SQLite) es el\n",
    "programa que lo entiende y guarda los datos. Se le dice también gestor o\n",
    "DBMS.\n",
    "\n",
    "- 🗃️ **La base de datos** es tu conjunto de tablas concreto: la\n",
    "de ventas de tu empresa.\n",
    "\n",
    "Es como decir que el español es el idioma, la persona que lo habla es el\n",
    "motor, y lo que te está contando es la base. Cuatro personas pueden hablar\n",
    "español con acentos distintos y entenderse perfecto 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Los cuatro, y dónde te los vas a encontrar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Motor | Dónde aparece | Qué conviene saber |\n",
    "|---|---|---|\n",
    "| **PostgreSQL** | Startups, fintech, analítica, casi todo lo nuevo | El más completo y el más estricto. Si aprendes aquí, el resto te resulta fácil |\n",
    "| **MySQL** | Web, ecommerce, WordPress, sistemas heredados | El más extendido del mundo. MariaDB es su gemelo y va casi igual |\n",
    "| **SQL Server** | Empresas grandes, banca, retail, todo lo Microsoft | Su dialecto se llama T-SQL y es el que más se aparta. Muy común en Perú |\n",
    "| **SQLite** | Celulares, apps de escritorio, un archivo suelto | No tiene servidor: la base es un archivo. Es con el que vamos a practicar |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si trabajas en una empresa grande peruana, lo más probable es\n",
    "**SQL Server**. Si es una startup o algo montado en los últimos\n",
    "años, **PostgreSQL**. Si es una tienda online o algo con WordPress\n",
    "detrás, **MySQL** 📊\n",
    "\n",
    "Y hay un quinto grupo que conviene nombrar: los de la nube, como BigQuery de\n",
    "Google, Snowflake o Redshift de Amazon. Son otra cosa por dentro, pero el SQL\n",
    "que escribes ahí se parece muchísimo al de PostgreSQL, así que lo de este libro\n",
    "te sirve igual."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué es igual en los cuatro"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esto es lo que quiero que te lleves del capítulo, porque quita el miedo:\n",
    "**casi todo**.\n",
    "\n",
    "Existe un estándar, el ANSI SQL, que los cuatro respetan en lo fundamental.\n",
    "Lo que se escribe igual en todos:\n",
    "\n",
    "- ✅ `SELECT`, `FROM`, `WHERE`\n",
    "\n",
    "- ✅ `GROUP BY` y `HAVING`\n",
    "\n",
    "- ✅ Los cuatro `JOIN`\n",
    "\n",
    "- ✅ `ORDER BY`\n",
    "\n",
    "- ✅ `COUNT`, `SUM`, `AVG`, `MIN`,\n",
    "`MAX`\n",
    "\n",
    "- ✅ `INSERT`, `UPDATE`, `DELETE`\n",
    "\n",
    "- ✅ Las subconsultas, los `CTE` con `WITH` y las\n",
    "funciones de ventana\n",
    "\n",
    "O sea: los capítulos 3 al 9 de este libro, que son el corazón, valen tal cual\n",
    "en los cuatro motores 🌟"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y qué cambia"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El 20% restante. Te lo adelanto entero aquí para que lo veas junto una vez, y\n",
    "después cada capítulo lo retoma en su momento."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Traer solo las primeras 10 filas\n",
    "\n",
    "| PostgreSQL | `SELECT * FROM clientes LIMIT 10;` |\n",
    "|---|---|\n",
    "| MySQL | `SELECT * FROM clientes LIMIT 10;` |\n",
    "| SQL Server | `SELECT TOP 10 * FROM clientes;` |\n",
    "| SQLite | `SELECT * FROM clientes LIMIT 10;` |\n",
    "\n",
    "Tres usan LIMIT y SQL Server usa TOP, que además va antes de las columnas. Es la primera diferencia con la que se choca todo el mundo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Juntar dos textos\n",
    "\n",
    "| PostgreSQL | `SELECT nombre || ' - ' || ciudad FROM clientes;` |\n",
    "|---|---|\n",
    "| MySQL | `SELECT CONCAT(nombre, ' - ', ciudad) FROM clientes;` |\n",
    "| SQL Server | `SELECT nombre + ' - ' + ciudad FROM clientes;` |\n",
    "| SQLite | `SELECT nombre || ' - ' || ciudad FROM clientes;` |\n",
    "\n",
    "Tres formas distintas para lo mismo. El CONCAT de MySQL también funciona en PostgreSQL y en SQL Server, así que si quieres escribir algo que corra en los tres, usa CONCAT."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La fecha y hora de ahora\n",
    "\n",
    "| PostgreSQL | `SELECT NOW();` |\n",
    "|---|---|\n",
    "| MySQL | `SELECT NOW();` |\n",
    "| SQL Server | `SELECT GETDATE();` |\n",
    "| SQLite | `SELECT datetime('now');` |\n",
    "\n",
    "Y ojo: CURRENT_TIMESTAMP funciona en los cuatro. Cuando exista una forma estándar, esa es la que conviene usar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si viene nulo, ponme otro valor\n",
    "\n",
    "| PostgreSQL | `SELECT COALESCE(monto, 0) FROM pedidos;` |\n",
    "|---|---|\n",
    "| MySQL | `SELECT IFNULL(monto, 0) FROM pedidos;` |\n",
    "| SQL Server | `SELECT ISNULL(monto, 0) FROM pedidos;` |\n",
    "| SQLite | `SELECT IFNULL(monto, 0) FROM pedidos;` |\n",
    "\n",
    "COALESCE funciona en los cuatro y además acepta varios valores de repuesto. Es la que yo uso siempre."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Ves el patrón? **Casi siempre hay una forma que funciona en los\n",
    "cuatro**, y las diferencias están en los atajos que cada motor inventó\n",
    "por su cuenta. Si te acostumbras a la forma estándar, tu SQL viaja 💜\n",
    "\n",
    "Las otras diferencias que vas a encontrar, y que veremos cada una en su\n",
    "capítulo:\n",
    "\n",
    "- 🔤 **Las comillas.** El texto va entre comillas simples en los\n",
    "cuatro. Los nombres de tabla y columna con espacios van entre comillas dobles en\n",
    "PostgreSQL y SQLite, entre acentos graves en MySQL y entre corchetes en SQL\n",
    "Server.\n",
    "\n",
    "- 🔢 **Los tipos de dato.** Capítulo 5.\n",
    "\n",
    "- 🆔 **El id que se autoincrementa.** Capítulo 11, y ahí los\n",
    "cuatro se escriben distinto.\n",
    "\n",
    "- 📅 **Las funciones de fecha.** Capítulo 6, y es donde más se\n",
    "pelean.\n",
    "\n",
    "- 🔠 **Las mayúsculas.** En MySQL sobre Windows,\n",
    "`'lima'` y `'Lima'` suelen ser lo mismo. En PostgreSQL no.\n",
    "Esa te puede cambiar un conteo sin que te enteres."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Con cuál practicamos, y por qué"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Con **SQLite**, y no porque sea el más fácil sino por tres cosas\n",
    "muy concretas:\n",
    "\n",
    "- 💾 **No hay que instalar un servidor.** La base entera es un\n",
    "archivo. Lo descargas y ya.\n",
    "\n",
    "- 🐍 **Python lo trae de fábrica.** Cero configuración.\n",
    "\n",
    "- 📱 **Lo usas todos los días sin saberlo.** Tu celular tiene\n",
    "decenas de bases SQLite dentro: WhatsApp, el navegador, la galería.\n",
    "\n",
    "Y lo importante: **lo que aprendas aquí se escribe igual en los\n",
    "otros tres**, salvo lo que este libro te va marcando en su tabla.\n",
    "\n",
    "Cuando quieras probar los demás, sin instalar nada:\n",
    "\n",
    "- 🐘 [DB Fiddle](https://www.db-fiddle.com/) para PostgreSQL y MySQL en el navegador.\n",
    "\n",
    "- 🪟 [dbfiddle.uk](https://dbfiddle.uk/), que además tiene SQL Server."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La base de este libro"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Todo corre sobre `tienda.db`, una distribuidora con cinco tablas y\n",
    "relaciones de verdad. Vamos a mirarla:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa consulta es de SQLite y no funciona igual en los otros. Es un buen\n",
    "ejemplo de algo que *parece* SQL normal y es específico del motor:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Listar las tablas que hay\n",
    "\n",
    "| PostgreSQL | `\\dt   (o consultar information_schema.tables)` |\n",
    "|---|---|\n",
    "| MySQL | `SHOW TABLES;` |\n",
    "| SQL Server | `SELECT name FROM sys.tables;` |\n",
    "| SQLite | `SELECT name FROM sqlite_master WHERE type = 'table';` |\n",
    "\n",
    "Los cuatro guardan su catálogo en un sitio distinto. PostgreSQL, MySQL y SQL Server sí tienen information_schema.tables, que es lo estándar y lo que conviene usar si quieres una sola consulta para los tres."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y cuántas filas tiene cada una:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT 'clientes' AS tabla, COUNT(*) AS filas FROM clientes\n",
    "UNION ALL SELECT 'direcciones', COUNT(*) FROM direcciones\n",
    "UNION ALL SELECT 'productos', COUNT(*) FROM productos\n",
    "UNION ALL SELECT 'pedidos', COUNT(*) FROM pedidos\n",
    "UNION ALL SELECT 'detalle', COUNT(*) FROM detalle;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa sí es SQL estándar y corre igual en los cuatro motores 🌟\n",
    "\n",
    "Fíjate en la forma de las tablas, que dice mucho: 120 clientes hicieron 900\n",
    "pedidos, y esos 900 pedidos tienen 2.682 líneas de detalle. O sea que cada\n",
    "cliente compró varias veces y cada compra tenía varios productos. Eso es una\n",
    "base relacional de verdad y por eso los JOIN del capítulo 7 van a tener sentido."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tu primera consulta"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT nombre, ciudad, segmento\n",
    "FROM clientes\n",
    "WHERE ciudad = 'Lima'\n",
    "ORDER BY nombre\n",
    "LIMIT 5;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Eso ya es una consulta completa: qué columnas quieres, de dónde, con qué\n",
    "filtro, en qué orden y cuántas 💜\n",
    "\n",
    "Y salvo el `LIMIT`, esa consulta corre tal cual en los cuatro\n",
    "motores. En SQL Server sería `SELECT TOP 5 nombre, ciudad, segmento`\n",
    "y el resto igual."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Le dices qué quieres, no cómo conseguirlo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esta es la idea que hace que SQL se aprenda rápido, y la que más cuesta si\n",
    "vienes de otro lenguaje 🧠\n",
    "\n",
    "En Python, para sacar las ventas de más de mil soles ordenadas de mayor a\n",
    "menor, escribes **los pasos**: recorre la lista, compara cada\n",
    "elemento, guarda los que pasen, ordena el resultado. Le explicas a la máquina\n",
    "cómo hacerlo.\n",
    "\n",
    "En SQL escribes **qué quieres**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT id, monto\n",
    "FROM pedidos\n",
    "WHERE monto > 1000\n",
    "ORDER BY monto DESC\n",
    "LIMIT 3;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí no hay ningún bucle. No dijiste por dónde empezar a recorrer, ni con qué\n",
    "algoritmo ordenar, ni si conviene mirar primero el filtro o el orden 🤷‍♀️\n",
    "\n",
    "Eso lo decide el motor, y lo decide **cada vez**, mirando cuántas\n",
    "filas hay y qué índices existen. La misma consulta puede resolverse de dos\n",
    "maneras distintas hoy y dentro de un mes, y las dos te dan el mismo resultado.\n",
    "\n",
    "Se llama lenguaje declarativo, y tiene tres consecuencias prácticas:\n",
    "\n",
    "- 🚀 **Escribes mucho menos.** Cinco líneas contra quince.\n",
    "\n",
    "- 🧩 **No puedes optimizar a mano como en un bucle.** Lo que sí\n",
    "puedes es darle mejores herramientas, y de eso va el capítulo\n",
    "14.\n",
    "\n",
    "- 🕵️ **Cuando algo va lento, la pregunta no es \"qué hice mal\" sino \"qué\n",
    "plan eligió el motor\"**. Y eso se puede consultar.\n",
    "\n",
    "Si vienes de Excel, el salto es más corto de lo que parece: una tabla\n",
    "dinámica también es declarativa. Arrastras un campo a filas y otro a valores, y\n",
    "no le explicas a Excel cómo agrupar 📊"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Saber en qué motor estás, en un segundo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Te sientas en una máquina que no es la tuya, abres un cliente que no\n",
    "conoces y no tienes ni idea de qué hay detrás. Antes de escribir nada, esto 🔍"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT sqlite_version() AS version;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y en cada motor se pregunta distinto, que ya es la primera lección del libro\n",
    "en acción:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En qué motor y en qué versión estoy\n",
    "\n",
    "| PostgreSQL | `SELECT version();` |\n",
    "|---|---|\n",
    "| MySQL | `SELECT VERSION();` |\n",
    "| SQL Server | `SELECT @@VERSION;` |\n",
    "| SQLite | `SELECT sqlite_version();` |\n",
    "\n",
    "Cuatro formas de preguntar lo mismo, y ninguna se parece a las otras. Es el resumen del libro en una tabla."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Parece un detalle de curiosidad y no lo es: **la versión decide qué\n",
    "puedes escribir**. Las funciones de ventana, que son media carrera de\n",
    "analista, llegaron a MySQL en la 8.0 y a SQLite en la 3.25. Si la máquina tiene\n",
    "una anterior, el capítulo 9 entero no te va a\n",
    "funcionar, y el error que te sale no dice \"tu versión es vieja\": dice que hay un\n",
    "problema de sintaxis 🙃\n",
    "\n",
    "Por eso esta consulta es lo primero que escribo en una máquina nueva. Cuesta\n",
    "dos segundos y te ahorra media hora de buscar por qué algo que sabes que\n",
    "funciona no funciona."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que este libro no te va a enseñar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y lo digo aquí, al principio, para que no lo descubras a mitad del libro 🌸\n",
    "\n",
    "**Administrar el servidor.** Copias de seguridad, usuarios,\n",
    "permisos, replicación. Eso es trabajo de administración de bases de datos, es\n",
    "una carrera entera y no es esta. Aquí vas a saber consultar y modificar datos,\n",
    "que es lo que hace alguien de datos.\n",
    "\n",
    "**Diseñar el modelo desde cero.** Vas a ver cómo se crean tablas\n",
    "y qué son las claves, lo suficiente para entender la base con la que trabajas y\n",
    "para montarte una propia. Pero decidir la estructura de un sistema entero, con\n",
    "sus formas normales, es otro tema y da para otro libro.\n",
    "\n",
    "**Los extras de cada motor.** PostgreSQL tiene tipos JSON y\n",
    "búsqueda de texto completo, SQL Server tiene su lenguaje de procedimientos, y\n",
    "cada uno tiene sus juguetes. Aquí está el 80% que es igual en los cuatro, más\n",
    "las diferencias que aparecen el primer día. Los juguetes se aprenden después, y\n",
    "se aprenden mejor sabiendo lo de aquí.\n",
    "\n",
    "Lo que sí te llevas: entrar a una base que no conociste nunca, entender qué\n",
    "hay dentro, sacar el número que te pidieron, y saber cuándo el número que\n",
    "sacaste está mal 💜"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Cuántos clientes hay\n",
    "\n",
    "Cuenta cuántos clientes tiene la tabla."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT COUNT(*) AS clientes FROM clientes;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "clientes\n",
    "--------\n",
    "120\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`COUNT(*)` es estándar y funciona igual en los cuatro motores."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Las ciudades sin repetir\n",
    "\n",
    "Lista las ciudades distintas que hay, ordenadas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT DISTINCT ciudad FROM clientes ORDER BY ciudad;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ciudad\n",
    "--------\n",
    "Arequipa\n",
    "Chiclayo\n",
    "Cusco\n",
    "Lima\n",
    "Piura\n",
    "Trujillo\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Los cinco productos más caros\n",
    "\n",
    "Trae nombre y precio de los cinco productos con el precio\n",
    "más alto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT nombre, precio\n",
    "FROM productos\n",
    "ORDER BY precio DESC\n",
    "LIMIT 5;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "nombre       precio\n",
    "-----------  ------\n",
    "Producto 27  89.35\n",
    "Producto 37  86.1\n",
    "Producto 16  83.42\n",
    "Producto 36  82.97\n",
    "Producto 12  81.85\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En SQL Server esto sería `SELECT TOP 5 nombre, precio FROM productos\n",
    "ORDER BY precio DESC;`. Todo lo demás igual."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Escríbelo para los cuatro motores\n",
    "\n",
    "Sin ejecutar nada: escribe \"los 3 pedidos más caros\" en los\n",
    "cuatro dialectos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "-- PostgreSQL, MySQL y SQLite\n",
    "SELECT id, monto FROM pedidos ORDER BY monto DESC LIMIT 3;\n",
    "\n",
    "-- SQL Server\n",
    "SELECT TOP 3 id, monto FROM pedidos ORDER BY monto DESC;\n",
    "\n",
    "-- Y el estándar ANSI, que funciona en PostgreSQL y en SQL Server modernos\n",
    "SELECT id, monto FROM pedidos ORDER BY monto DESC\n",
    "FETCH FIRST 3 ROWS ONLY;\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa tercera forma, `FETCH FIRST`, es la del estándar. Es más larga\n",
    "y casi nadie la usa, pero saber que existe te salva el día que veas una consulta\n",
    "escrita así y no entiendas de dónde salió 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. El filtro con texto\n",
    "\n",
    "Trae los clientes del segmento Mayorista que sean de\n",
    "Arequipa."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT nombre, ciudad, segmento\n",
    "FROM clientes\n",
    "WHERE segmento = 'Mayorista' AND ciudad = 'Arequipa';\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "nombre                 ciudad    segmento\n",
    "---------------------  --------  ---------\n",
    "Cafe del Puerto 060    Arequipa  Mayorista\n",
    "Bodega San Martin 103  Arequipa  Mayorista\n",
    "Minimarket El Sol 109  Arequipa  Mayorista\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las comillas simples para el texto son estándar. Si usas comillas dobles,\n",
    "PostgreSQL va a creer que le hablas de una columna llamada Mayorista y te va a\n",
    "dar un error raro."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. La consulta que falla\n",
    "\n",
    "Pide una columna que no existe y lee el error."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT nombre, telefono FROM clientes LIMIT 3;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "OperationalError: no such column: telefono\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los cuatro motores dicen lo mismo con otras palabras: PostgreSQL dice\n",
    "`column \"telefono\" does not exist`, MySQL `Unknown column\n",
    "'telefono'` y SQL Server `Invalid column name 'telefono'`.\n",
    "Distinto texto, mismo problema 🙃"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La división que se come el medio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de cerrar, una de las pocas cosas de SQL que se parecen a español y no lo son. Mírala ahora, porque la vas a escribir sin pensar 🧮"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Sacas el ticket medio de los pedidos dividiendo la suma entre el número de pedidos. Es una división, no hay forma de equivocarse.\n",
    "\n",
    "```\n",
    "SELECT 7/2;\n",
    "-- 3\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Tres 😳 SQL divide enteros entre enteros y devuelve un entero, igual que hacía Python 2 y que todavía hace Java. No redondea: **corta**. El 0,5 no se pierde por poco, se tira entero.\n",
    "\n",
    "Y esto no avisa nunca, porque 3 es un número perfectamente razonable. En un reporte de unidades por pedido, o de días de entrega, o de cualquier cosa que sea \"algo entre algo\", te va a dar un número que se ve bien y que está bajo.\n",
    "\n",
    "Se arregla haciendo que un lado sea decimal: `7 * 1.0 / 2`, o `CAST(7 AS REAL) / 2`. Yo escribo el `1.0` por costumbre en cuanto veo una división, y no me he arrepentido nunca."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Aprendiste con SQLite y en tu primer trabajo hay SQL Server. Escribes SELECT * FROM ventas LIMIT 10 y te sale un error de sintaxis. ¿Qué pasó?\n",
    "\n",
    "a) LIMIT no existe en SQL Server, ahí se escribe SELECT TOP 10\n",
    "\n",
    "b) La tabla ventas no existe en esa base\n",
    "\n",
    "c) Falta un punto y coma al final\n",
    "\n",
    "d) SQL Server no deja usar el asterisco\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* El error es de sintaxis, no de tabla no encontrada. Los dos mensajes son distintos y conviene distinguirlos.\n",
    "\n",
    "*c)* SQL Server no necesita punto y coma para una sola consulta.\n",
    "\n",
    "*d)* El asterisco funciona en los cuatro motores. Lo que cambia está al final de la consulta, no al principio.\n",
    "\n",
    "El 80% de SQL es igual en los cuatro motores. El 20% que cambia es justo el que aparece el primer día."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 🗣️ SQL es el idioma; el motor es quien lo habla. Aprendes el idioma.\n",
    "\n",
    "- 🤝 El 80% se escribe igual en PostgreSQL, MySQL, SQL Server y SQLite.\n",
    "\n",
    "- ⚠️ El 20% que cambia aparece el primer día: limitar filas, juntar textos,\n",
    "fechas y nulos.\n",
    "\n",
    "- 🧭 Cuando exista una forma estándar (`COALESCE`,\n",
    "`CONCAT`, `CURRENT_TIMESTAMP`), esa es la que conviene.\n",
    "\n",
    "- 💾 Practicamos con SQLite porque es un archivo, y lo aprendido viaja.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aprendes SQL, no un motor. Lo que cambia entre los cuatro cabe en una\n",
    "tabla al lado."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y si todavía estás decidiendo si este camino es para ti, te dejo lo que\n",
    "hace de verdad una analista de datos en el día a día:\n",
    "[ser analista de datos](https://missyera.com/blog/ser-analista-de-datos/) 💛\n",
    "\n",
    "En el capítulo 2 instalamos lo que haga falta y conectamos con la base, que\n",
    "es donde se atasca la gente antes de escribir su primer SELECT.\n",
    "\n",
    "Que tengas un hermoso día! 🌟"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Qué es SQL?El idioma para pedirle datos a una base. Se le dice qué quieres, no cómo buscarlo, y de eso se encarga la base.\n",
    "\n",
    "¿Para qué sirve SQL?Para consultar, filtrar, cruzar y resumir datos que están guardados en tablas. Es lo que hay debajo de casi todo tablero y de casi todo reporte de empresa.\n",
    "\n",
    "¿SQL es un lenguaje de programación?Es un lenguaje, y no se parece a Python ni a Java: no tiene bucles ni condicionales al uso. Se declara lo que quieres y punto.\n",
    "\n",
    "¿Cuál es la diferencia entre SQL y MySQL?SQL es el idioma; MySQL es uno de los motores que lo hablan, como PostgreSQL, SQL Server y SQLite. El 80% del idioma es igual en los cuatro, y el 20% que cambia es el que te encuentras el primer día de trabajo.\n",
    "\n",
    "¿Cuánto se tarda en aprender SQL?Para escribir consultas útiles, unas semanas. Lo que toma más es saber si el número que devolvió está bien, y eso es la mitad de este libro."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 1 de **SQL desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/sql-desde-cero/que-es-sql/\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
