{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Qué es big data y cuándo no lo necesitas\n",
    "\n",
    "Las tres V sin marketing, dónde empieza de verdad, y por qué casi ninguna empresa peruana tiene ese problema.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 18 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/big-data/\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": [
    "Esta es la palabra que más te van a decir en una reunión y la que menos gente\n",
    "sabe definir 🎤\n",
    "\n",
    "Y no es culpa de nadie. Durante diez años se vendió como si fuera un producto,\n",
    "así que quedó significando \"muchos datos\", que es como definir \"casa\" diciendo\n",
    "\"muchos ladrillos\".\n",
    "\n",
    "Antes de arrancar, contéstate una: **¿cuántas filas tiene la tabla más\n",
    "grande que has abierto en tu trabajo?** Guarda ese número, que lo vamos a\n",
    "usar al final del capítulo para saber si lo tuyo es big data o no 📏"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La definición, sin marketing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Big data es cuando los datos ya no caben ni se procesan en una sola\n",
    "máquina.** Ya está. Esa es toda la idea.\n",
    "\n",
    "No es un número de filas ni un tamaño en gigas. Es un momento: aquel en el que\n",
    "tienes que repartir el trabajo entre varias computadoras porque una sola no\n",
    "llega. Y todas las herramientas que oíste nombrar existen para resolver ese\n",
    "reparto, no para otra cosa.\n",
    "\n",
    "Las famosas tres V son de 2001 y siguen sirviendo si se leen bien:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| La V | Qué pregunta de verdad | Cuándo te toca |\n",
    "|---|---|---|\n",
    "| **Volumen** | ¿Cabe en una máquina? | Cuando hablas de decenas de terabytes para arriba |\n",
    "| **Velocidad** | ¿Llegan más rápido de lo que puedes guardarlos? | Sensores, transacciones de un banco, clics de una web enorme |\n",
    "| **Variedad** | ¿Tienen forma de tabla? | Video, audio, texto libre, imágenes |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y fíjate en algo: **con que falle una sola ya tienes el problema**.\n",
    "Una empresa con datos chiquitos pero que llegan a mil por segundo tiene un\n",
    "problema de big data. Y una con muchos gigas de tablas ordenadas que se\n",
    "consultan una vez al día, no."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dónde está el listón, medido"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aquí es donde casi todos los artículos se ponen vagos y dicen \"grandes\n",
    "volúmenes\". Vamos a poner números.\n",
    "\n",
    "Primero, lo que pesa la base con la que llevas trece capítulos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT (SELECT * FROM pragma_page_count()) * (SELECT * FROM pragma_page_size())\n",
    "       AS bytes;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "139 KB. La base entera de este libro, con sus cinco tablas y sus casi cuatro\n",
    "mil filas, pesa menos que una foto del celular 📱\n",
    "\n",
    "Ahora vamos a hacerla grande de verdad. Estas cuatro líneas fabrican doscientas\n",
    "mil filas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "CREATE TABLE grande AS\n",
    "WITH RECURSIVE n(x) AS (\n",
    "    SELECT 1 UNION ALL SELECT x + 1 FROM n WHERE x < 200000\n",
    ")\n",
    "SELECT x AS id, (x % 6) AS ciudad, (x * 7919 % 200000) / 100.0 AS monto\n",
    "FROM n;\n",
    "\n",
    "SELECT COUNT(*) AS filas FROM grande;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Doscientas mil filas, creadas en una décima de segundo. Y ahora el resumen\n",
    "entero sobre todas ellas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT ciudad, ROUND(SUM(monto), 0) AS soles\n",
    "FROM grande\n",
    "GROUP BY ciudad\n",
    "ORDER BY ciudad;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Seis centésimas de segundo. Y la base pasó a pesar 4,5 MB 🚀\n",
    "\n",
    "Fuera de este libro lo llevé más lejos, en una laptop normal y con SQLite,\n",
    "que es la base más humilde que existe:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Con 5.000.000 de filas | Cuánto |\n",
    "|---|---|\n",
    "| Lo que ocupa en disco | 142 MB |\n",
    "| Lo que pesa cada fila | 28,5 bytes |\n",
    "| Insertarlas todas | 9,3 segundos |\n",
    "| Contarlas | 0,04 segundos |\n",
    "| Un GROUP BY sobre las cinco millones | 3,07 segundos |\n",
    "| La misma consulta filtrada, con índice | 0,03 segundos |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cinco millones de filas ocupan lo que tres canciones. Y esto no es un\n",
    "servidor: es una laptop 💻"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El número que te va a sorprender"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si cada fila pesa 28,5 bytes, para llenar **un solo terabyte**\n",
    "harían falta **35 mil millones de filas**.\n",
    "\n",
    "Ponlo en perspectiva con algo que conozcas. Si una empresa peruana mediana\n",
    "factura mil boletas al día, todos los días del año, sin parar, tarda\n",
    "**casi cien mil años** en llegar a ese terabyte.\n",
    "\n",
    "Y un terabyte todavía no es big data. Un terabyte cabe en un disco de 90\n",
    "soles 😅"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y Excel, ¿dónde se rinde?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En **1.048.576 filas**. Ese es el máximo de una hoja y no es\n",
    "negociable: son 2 elevado a 20, y está así desde 2007.\n",
    "\n",
    "O sea que el famoso \"se me colgó el Excel\" pasa **casi cinco veces\n",
    "antes** de que a SQLite se le empiece a notar el esfuerzo. Y ahí está el\n",
    "malentendido más caro de todos: mucha gente concluye \"tenemos big data\" cuando\n",
    "lo que tiene es un Excel donde no cabe algo que a una base de datos le sobra\n",
    "sitio 🙃"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Las herramientas, y qué problema resuelve cada una"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Te las nombro para que las reconozcas en una reunión, no para que las\n",
    "aprendas hoy:\n",
    "\n",
    "- 🐘 **Hadoop**. El abuelo. Guarda archivos repartidos entre\n",
    "muchas máquinas y procesa por lotes. Casi nadie empieza uno nuevo hoy.\n",
    "\n",
    "- ⚡ **Spark**. Lo mismo pero en memoria y mucho más rápido, y con\n",
    "una forma de escribir que se parece a pandas. Es el estándar cuando de verdad\n",
    "hace falta repartir.\n",
    "\n",
    "- ☁️ **BigQuery, Redshift, Snowflake**. Bases columnares en la\n",
    "nube. Le mandas SQL normal y ellas reparten por debajo sin que te enteres. Es\n",
    "por lejos el camino más común hoy.\n",
    "\n",
    "- 🌊 **Kafka**. No es para volumen, es para velocidad: datos que\n",
    "llegan sin parar y hay que ir atendiendo.\n",
    "\n",
    "Fíjate en lo de BigQuery, que es la buena noticia del capítulo: **le\n",
    "escribes SQL**. El mismo SELECT, el mismo GROUP BY, los mismos JOIN de\n",
    "este libro. Si algún día te toca big data de verdad, lo que aprendiste acá sigue\n",
    "valiendo entero 💜"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué hacer antes de llegar ahí"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El 99% de las veces que alguien dice \"necesitamos big data\", lo que necesita\n",
    "es una de estas cuatro, y todas están en este libro:\n",
    "\n",
    "- 🔍 **Un índice.** La consulta de cuarenta segundos que pasa a\n",
    "medio segundo, del capítulo 14.\n",
    "\n",
    "- 🧱 **Un diseño que no duplique.** La mitad de las tablas\n",
    "enormes son enormes porque repiten, del capítulo 10.\n",
    "\n",
    "- 📅 **Traer solo el periodo que miras.** Nadie necesita ocho años\n",
    "de historia para el reporte del mes.\n",
    "\n",
    "- 🧮 **Resumir una vez y guardar el resumen.** Una tabla de\n",
    "totales por día pesa mil veces menos que el detalle y contesta lo mismo.\n",
    "\n",
    "Y te lo digo con toda la honestidad: **montar Spark para diez millones\n",
    "de filas es más caro, más lento y más frágil** que ponerle un índice a una\n",
    "base normal. He visto proyectos hacerlo, y el resultado fue una consulta más\n",
    "lenta que antes y tres personas ocupadas manteniéndolo 😖"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los ejercicios son la mitad del libro. Intenta antes de abrir."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Mide tu propia base\n",
    "\n",
    "Averigua cuánto pesa la base del libro después de haberle\n",
    "metido las 200.000 filas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT ROUND((SELECT * FROM pragma_page_count()) *\n",
    "             (SELECT * FROM pragma_page_size()) / 1000000.0, 1) AS mb;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "mb\n",
    "---\n",
    "4.5\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "4,5 MB. Y le acabas de meter cincuenta veces más filas de las que traía 📦\n",
    "\n",
    "Todos los motores saben decirte lo que pesan, y es la primera pregunta antes\n",
    "de decidir nada sobre volumen 📏"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuánto pesa mi base\n",
    "\n",
    "| PostgreSQL | `SELECT pg_size_pretty(pg_database_size('mi_base'));` |\n",
    "|---|---|\n",
    "| MySQL | `SELECT table_schema, SUM(data_length + index_length) FROM information_schema.tables GROUP BY table_schema;` |\n",
    "| SQL Server | `EXEC sp_spaceused;` |\n",
    "| SQLite | `SELECT (SELECT * FROM pragma_page_count()) * (SELECT * FROM pragma_page_size());` |\n",
    "\n",
    "Cuatro formas de contestar la única pregunta que desarma una conversación sobre big data. Ninguna tarda más de un segundo, y casi nadie la hace antes de opinar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. La consulta del motor de al lado\n",
    "\n",
    "Prueba la de PostgreSQL aquí, en SQLite, a ver qué pasa."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT pg_size_pretty(pg_database_size('mi_base'));\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "OperationalError: no such function: pg_database_size\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*no such function* 🙅 Y es exactamente lo que tenía que pasar: esa\n",
    "función existe en PostgreSQL y no en SQLite.\n",
    "\n",
    "Te lo hago cometer a propósito porque es el error que más rabia da al empezar:\n",
    "copias algo de internet, no te corre, y crees que lo escribiste mal. Casi nunca\n",
    "lo escribiste mal. **Estaba escrito para otro motor**, que es de lo\n",
    "que va la tabla de dialectos de cada capítulo de este libro.\n",
    "\n",
    "La costumbre que te ahorra tardes: cuando busques ayuda, escribe el nombre de\n",
    "tu motor en la búsqueda. \"contar filas sql\" y \"contar filas postgresql\" te\n",
    "devuelven cosas distintas 🔍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Cuántas filas caben en un Excel\n",
    "\n",
    "La tabla `grande` tiene 200.000 filas. ¿Cuántas\n",
    "veces cabe en una hoja de Excel?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT 1048576 AS limite_excel,\n",
    "       (SELECT COUNT(*) FROM grande) AS mis_filas,\n",
    "       ROUND(1048576.0 / (SELECT COUNT(*) FROM grande), 1) AS veces;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "limite_excel  mis_filas  veces\n",
    "------------  ---------  -----\n",
    "1048576       200000     5.2\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cabe cinco veces. Y a la base ni le hizo cosquillas 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Resumir una vez y guardar el resumen\n",
    "\n",
    "Crea una tabla de totales por ciudad y compara cuántas\n",
    "filas tiene contra el detalle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "CREATE TABLE resumen AS\n",
    "SELECT ciudad, COUNT(*) AS operaciones, ROUND(SUM(monto), 0) AS soles\n",
    "FROM grande GROUP BY ciudad;\n",
    "\n",
    "SELECT (SELECT COUNT(*) FROM grande) AS detalle,\n",
    "       (SELECT COUNT(*) FROM resumen) AS resumen;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "detalle  resumen\n",
    "-------  -------\n",
    "200000   6\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Doscientas mil filas contra seis 🤯\n",
    "\n",
    "Esto es lo que hace un almacén de datos y es la respuesta a la mitad de los\n",
    "problemas de rendimiento: si la pregunta siempre es la misma, contéstala una vez\n",
    "al día y guarda la respuesta. El detalle sigue ahí para cuando alguien quiera\n",
    "bajar, pero el reporte no lo toca."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. El índice, otra vez, pero con volumen\n",
    "\n",
    "Pídele el plan a una consulta filtrada sobre las 200.000\n",
    "filas, antes y después del índice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "EXPLAIN QUERY PLAN SELECT COUNT(*) FROM grande WHERE ciudad = 3;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "id  parent  notused  detail\n",
    "--  ------  -------  -----------\n",
    "3   0       0        SCAN grande\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "CREATE INDEX ix_grande ON grande(ciudad);\n",
    "EXPLAIN QUERY PLAN SELECT COUNT(*) FROM grande WHERE ciudad = 3;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "id  parent  notused  detail\n",
    "--  ------  -------  -------------------------------------------------------\n",
    "3   0       0        SEARCH grande USING COVERING INDEX ix_grande (ciudad=?)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "De `SCAN` a `SEARCH` con una línea. Esa es la\n",
    "herramienta que hay que agotar antes de pensar en repartir nada entre\n",
    "máquinas ⚡"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Cuántos años tardarías en tener un terabyte\n",
    "\n",
    "Si cada fila pesa 28,5 bytes y emites mil boletas al día,\n",
    "¿cuántos años para llegar a un terabyte?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT ROUND(1000000000000.0 / 28.5, 0) AS filas_para_1_tb,\n",
    "       ROUND(1000000000000.0 / 28.5 / 1000 / 365, 0) AS anios;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "filas_para_1_tb  anios\n",
    "---------------  -------\n",
    "35087719298.0    96131.0\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Noventa y seis mil años 😂\n",
    "\n",
    "Obviamente una boleta real pesa más que 28 bytes, porque lleva líneas,\n",
    "nombres y direcciones. Pero aunque pesara cien veces más, seguirían siendo casi\n",
    "mil años. **El orden de magnitud es el argumento**, no el decimal."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. El error de creer que contar es caro\n",
    "\n",
    "Cuenta las 200.000 filas y después cuéntalas agrupadas.\n",
    "¿Cuál crees que tarda?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "SELECT COUNT(*) AS filas,\n",
    "       COUNT(DISTINCT ciudad) AS ciudades,\n",
    "       ROUND(AVG(monto), 2) AS ticket,\n",
    "       MIN(monto) AS minimo,\n",
    "       MAX(monto) AS maximo\n",
    "FROM grande;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "filas   ciudades  ticket  minimo  maximo\n",
    "------  --------  ------  ------  -------\n",
    "200000  6         1000.0  0.0     1999.99\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Las cinco cosas de golpe y ni te diste cuenta. Una base de datos está hecha\n",
    "exactamente para esto, y por eso es tan mal negocio bajarse los datos a un Excel\n",
    "para hacer lo mismo 📊"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. Y ahora tú: ¿lo tuyo es big data?\n",
    "\n",
    "Coge el número que guardaste al empezar el capítulo, el de\n",
    "tu tabla más grande, y contesta estas tres."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q(\"\"\"\n",
    "-- 1. ¿Cabe en una máquina? Multiplica tus filas por 500 bytes,\n",
    "--    que es una fila gorda de verdad. Si da menos de 100 GB, cabe.\n",
    "-- 2. ¿Llegan más rápido de lo que los guardas?\n",
    "-- 3. ¿Tienen forma de tabla, o son videos, audios y texto libre?\n",
    "\n",
    "SELECT 'si contestaste que si, que no y que si, no es big data' AS veredicto;\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "veredicto\n",
    "------------------------------------------------------\n",
    "si contestaste que si, que no y que si, no es big data\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y si no es big data, es una noticia buenísima 🎉 Significa que todo tu\n",
    "problema se arregla con lo que ya sabes: un índice, un diseño que no duplique y\n",
    "una consulta escrita con cabeza."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La tabla enorme que pesaba menos que una foto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Antes de cerrar, la trampa. Y esta no es de código: es de la conversación que\n",
    "vas a tener en una reunión 🎤"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "En la reunión alguien dice que la empresa ya tiene big data, porque el Excel de ventas se cuelga y son casi dos millones de registros. Nadie lo discute, y se abre un proyecto.\n",
    "\n",
    "```\n",
    "-- lo que se dijo\n",
    "-- \"son 1.800.000 registros, esto ya es big data\"\n",
    "\n",
    "-- lo que pesan, a 28,5 bytes por fila\n",
    "-- 1.800.000 x 28,5 = 51,3 MB\n",
    "\n",
    "-- lo que aguanta SQLite en una laptop\n",
    "-- 5.000.000 de filas = 142 MB, GROUP BY en 3,07 s\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Cincuenta y un megas 📎 Eso cabe en un correo. Y esa reunión acaba de abrir un proyecto de infraestructura para un archivo que pesa menos que la presentación donde se propuso.\n",
    "\n",
    "Lo que pasó es que se confundieron dos cosas distintas: **que a Excel se le acabe la hoja no dice nada del tamaño de tus datos**. Excel se planta en 1.048.576 filas por diseño, no por volumen, y a una base normal esas mismas filas le sobran cinco veces.\n",
    "\n",
    "El daño no es solo el dinero del proyecto. Es que el problema real, que casi siempre es un índice que falta o una tabla que duplica, se queda sin arreglar mientras todo el mundo mira hacia otro lado durante seis meses.\n",
    "\n",
    "La pregunta que desarma esta conversación en diez segundos, y la hago siempre: **¿cuánto pesa la tabla?** No cuántas filas tiene, cuánto pesa. Si nadie en la sala sabe contestarla, todavía no hay nada que decidir."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Una tienda guarda 40 millones de líneas de venta al año, ordenadas en tablas, y consulta el reporte una vez al día. ¿Es big data?\n",
    "\n",
    "a) No. Cabe de sobra en una base normal y se consulta una vez al día\n",
    "\n",
    "b) Sí, cuarenta millones de filas ya es big data\n",
    "\n",
    "c) Sí, porque son datos de varios años acumulados\n",
    "\n",
    "d) Depende de si usan Hadoop o Spark\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* El número de filas no define nada por sí solo. Cuarenta millones de filas de ese tamaño rondan el gigabyte, y un gigabyte cabe en cualquier sitio.\n",
    "\n",
    "*c)* La antigüedad tampoco. Lo que decide es si cabe y si se puede procesar en una máquina.\n",
    "\n",
    "*d)* Al revés: la herramienta se elige después de saber qué problema hay. Elegirla primero es lo que produce los proyectos que no terminan.\n",
    "\n",
    "Y si esa misma tienda recibiera esas 40 millones de líneas en tiempo real y tuviera que reaccionar al instante, entonces sí, aunque los datos fueran los mismos. La V que falla no siempre es el volumen."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 📦 Big data es cuando los datos no caben ni se procesan en una máquina. No es\n",
    "un número de filas.\n",
    "\n",
    "- 🔢 Con que falle una de las tres V ya tienes el problema. Y muchas veces la\n",
    "que falla es la velocidad, no el volumen.\n",
    "\n",
    "- 💻 Cinco millones de filas son 142 MB y un GROUP BY de tres segundos, en una\n",
    "laptop y con la base más humilde que existe.\n",
    "\n",
    "- 📄 Excel se rinde en 1.048.576 filas, casi cinco veces antes que eso.\n",
    "\n",
    "- 🗓️ Para llenar un terabyte a 28,5 bytes por fila harían falta 35 mil\n",
    "millones de filas.\n",
    "\n",
    "- ☁️ A BigQuery le escribes SQL normal. Lo de este libro sigue valiendo si\n",
    "algún día llegas ahí.\n",
    "\n",
    "- 🔧 Antes de repartir entre máquinas: un índice, un diseño que no duplique,\n",
    "traer solo el periodo, y guardar el resumen.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Casi nadie tiene un problema\n",
    "de volumen. Casi todos tienen una consulta sin arreglar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y si lo que te interesa no es guardar muchos datos sino predecir con ellos,\n",
    "ese es otro camino y empieza en el\n",
    "[libro de machine learning](https://missyera.com/guias/machine-learning-desde-cero/), que\n",
    "además usa el mismo archivo de práctica 🤖\n",
    "\n",
    "En el capítulo 21 volvemos a lo que vive\n",
    "dentro de la base: vistas, disparadores y lo que SQLite decidió no tener.\n",
    "\n",
    "Que tengas un hermoso día! 🌟"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Qué es big data?Datos que ya no caben o no se procesan en una sola máquina. La definición no es un número de gigas: es que tu herramienta de siempre dejó de alcanzar.\n",
    "\n",
    "¿Cuál es la definición de big data?Se explica con tres uves: volumen, velocidad y variedad. Sirven para entenderlo y no para decidir, porque casi ninguna empresa cruza el listón de ninguna de las tres.\n",
    "\n",
    "¿Qué ejemplos hay de big data?Los clics de un sitio grande, los registros de una red de sensores, las transacciones de un banco nacional. Lo que suele NO serlo: la tabla de ventas de una empresa mediana, que cabe entera en una laptop.\n",
    "\n",
    "¿Para qué sirve el big data?Para lo mismo que los datos normales: decidir mejor. Lo que cambia son las herramientas, y cambiarlas antes de necesitarlas es la forma más cara de no resolver nada.\n",
    "\n",
    "¿Cuándo hace falta Spark?Cuando el trabajo no entra en la máquina más grande que puedes alquilar. Antes de eso, SQL sobre una base o DuckDB sobre un archivo resuelven lo mismo sin montar un clúster."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 18 de **SQL desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/sql-desde-cero/big-data/\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
}
