¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Revisar el SQL que escribe un modelo

Después de esta lección: Puedo leer la consulta de un modelo y decir si responde a la pregunta que se hizo.

Lección de 65 min6 comandosLaboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

¿Responde la consulta del modelo a la pregunta?

Revisar el SQL que escribe un modelo es comparar la consulta con la pregunta antes de fiarte de ella. Una consulta puede funcionar sin error y responder a otra pregunta.

El módulo 4 enseña uniones y agrupaciones. Esta lección revisa el SQL que un modelo escribió para una pregunta y busca sus tres fallos más frecuentes.

Al final de esta lección

Nota

Puedo leer la consulta de un modelo y decir si responde a la pregunta que se hizo.

Palabras clave

PalabraQué significaEjemplo
Cláusulauna parte de una consulta, como WHERE
Filtrouna prueba que deja pasar solo algunas filasreturned = 0
Duplicadoun mismo valor contado más de una vez
DISTINCTdeja cada valor una sola vezCOUNT(DISTINCT member_id)
Columna de uniónla columna por la que se emparejan dos tablas

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Lee la pregunta antes que la consulta

Primero apunta lo que pide la pregunta. ¿Qué filas cuentan? ¿Se espera un número, una lista o un solo valor? ¿Hay que contar cada cosa una sola vez?

Después lee la consulta cláusula a cláusula. FROM y JOIN dicen de dónde salen las filas, WHERE se queda con una parte y SELECT dice qué se devuelve.

Ejemplo resuelto: trozos de la pregunta y cláusulas

La pregunta diceLa consulta necesitaCláusula
en el mes 3month = 3WHERE
socios distintosDISTINCT member_idSELECT
cuántosCOUNTSELECT
títulosuna unión de loans con books por book_idFROM y JOIN

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Tres fallos frecuentes de un modelo

Contar duplicados: COUNT(member_id) cuenta a Alba dos veces si tomó prestado dos veces. Un filtro olvidado: la consulta cuenta todos los préstamos, no solo los que siguen fuera.

La unión equivocada: loans.id = books.id empareja el préstamo 3 con el libro 3 solo porque llevan el mismo número. La unión tiene que pasar por loans.book_id.

Ejemplo resuelto: mal y bien

PreguntaTrozo erróneoTrozo correcto
Socios distintos en el mes 3COUNT(member_id)COUNT(DISTINCT member_id)
Libros que siguen prestadosSin WHEREWHERE returned = 0
Títulos que tomó el socio 2ON l.id = b.idON l.book_id = b.id

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Un error frecuente

Advertencia

Mucha gente cree que una consulta que funciona sin error responde a la pregunta. Un error solo dice que el SQL está roto. Una unión equivocada o un filtro olvidado funcionan en silencio y dan una respuesta falsa.

Escribe el ayudante del modelo, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Los tokens de una respuesta, o null si no trae ninguno.' 'function count(data, name) {' '  return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' '// Una respuesta puede envolver la consulta en un bloque delimitado: nos quedamos con el interior.' 'function sqlOf(text) {' '  const parts = text.split(String.fromCharCode(96).repeat(3));' '  const inner = (parts.length > 2 ? parts[1] : text).trim();' '  return inner.toLowerCase().startsWith(\"sql\") ? inner.slice(3).trim() : inner;' '}' > model.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe el ayudante del modelo, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"estado \" + res.status);' '    const d = await res.json();' '    return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"completion_tokens\"), recorded: false };' '  } catch (error) {' '    console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' '    return { text: sample, inTokens: null, outTokens: null, recorded: true };' '  }' '}' 'module.exports = { ask, sqlOf };' >> model.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe la revisión de consultas, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask, sqlOf } = require(\"./model\");' 'const questions = [' '  { q: \"¿Cuántos socios distintos tomaron prestado un libro en el mes 3?\", needs: [\"DISTINCT\", \"month = 3\"],' '    a: \"SELECT COUNT(member_id) FROM loans WHERE month = 3\",' '    b: \"SELECT COUNT(DISTINCT member_id) FROM loans WHERE month = 3\" },' '  { q: \"¿Cuántos libros siguen prestados?\", needs: [\"returned = 0\"],' '    a: \"SELECT COUNT(*) FROM loans\",' '    b: \"SELECT COUNT(*) FROM loans WHERE returned = 0\" },' '  { q: \"¿Qué títulos ha tomado prestados el socio 2?\", needs: [\"book_id\", \"member_id = 2\"],' '    a: \"SELECT b.title FROM loans l JOIN books b ON l.id = b.id WHERE l.member_id = 2\",' '    b: \"SELECT b.title FROM loans l JOIN books b ON l.book_id = b.id WHERE l.member_id = 2\" },' '];' > review.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe la revisión de consultas, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function squash(sql) { return sql.toUpperCase().split(/\s+/).join(\" \").trim(); }' 'function review(sql, needs) {' '  const text = squash(sql);' '  const problems = needs.filter(function (n) { return text.indexOf(squash(n)) < 0; })' '    .map(function (n) { return \"falta \" + n; });' '  if (/ON \w+\.ID = \w+\.ID\b/.test(text)) problems.push(\"une id con id\");' '  return problems.length ? problems.join(\", \") : \"parece correcta\";' '}' >> review.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe la revisión de consultas, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  questions.forEach(function (x, i) {' '    console.log((i + 1) + \" \" + x.q);' '    console.log(\"  A: \" + review(x.a, x.needs));' '    console.log(\"  B: \" + review(x.b, x.needs));' '  });' '  const schema = \"loans(id, book_id, member_id, month, returned)\nmembers(id, name)\nloans.member_id -> members.id\";' '  const system = \"Responde con una sola consulta SELECT de SQLite y nada más.\";' '  const sample = \"SELECT COUNT(DISTINCT member_id) FROM loans WHERE month = 3;\";' '  const reply = await ask([{ role: \"system\", content: system },' '    { role: \"user\", content: schema + \"\nPregunta: \" + questions[0].q }], sample, 120);' '  const sql = sqlOf(reply.text);' '  console.log(\"el modelo escribió: \" + sql);' '  console.log(\"  revisión: \" + review(sql, questions[0].needs));' '}' 'main();' >> review.js"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta la revisión

Comando
docker exec -w /home/node/project agent node review.js

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Lo que imprime el Run

LíneaQué muestra
A: falta DISTINCTPregunta 1, la candidata A cuenta duplicados
A: falta returned = 0Pregunta 2, la candidata A no tiene filtro
A: falta book_id, une id con idPregunta 3, la candidata A une por la columna equivocada
B: parece correctaCada candidata B pasa la revisión
el modelo escribió: ...La consulta del modelo para la pregunta 1, revisada igual, o una muestra grabada

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Sobre este laboratorio y su código: Laboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.