¿PHP y SQL funcionan juntos?

Ejecutar Código SQL en PHP: Guía Completa

Valoración: 4.72 (3200 votos)

La interacción entre una aplicación web escrita en PHP y una base de datos es fundamental para crear sitios dinámicos y funcionales. Permite almacenar, recuperar, modificar y eliminar información crucial para la operación del sitio. Ejecutar código SQL desde PHP es el puente que conecta tu lógica de negocio con tus datos.

Históricamente, PHP ha ofrecido varias formas de interactuar con bases de datos. Algunas de las interfaces más antiguas, como la extensión mysql_*, han quedado obsoletas y no deben usarse debido a problemas de seguridad y falta de características modernas. Hoy en día, las opciones recomendadas y seguras son principalmente dos: PDO (PHP Data Objects) y MySQLi (MySQL Improved).

¿Puedo usar PHP con MySQL?
Con PHP, puedes conectarte a bases de datos y manipularlas. MySQL es el sistema de bases de datos más popular para PHP .
Índice de Contenido

¿Por Qué es Crucial Aprender a Ejecutar SQL en PHP?

Casi cualquier aplicación web que vaya más allá de mostrar contenido estático necesita una base de datos. Desde un simple blog que guarda comentarios hasta una compleja plataforma de comercio electrónico con inventario, usuarios y pedidos, la persistencia de datos es clave. PHP, siendo uno de los lenguajes más populares para el desarrollo web, necesita una forma robusta y segura de comunicarse con el gestor de base de datos.

Dominar la ejecución de consultas SQL desde PHP te permite:

  • Recuperar datos para mostrar en páginas web (por ejemplo, una lista de productos).
  • Guardar información enviada por los usuarios (por ejemplo, un registro de nuevo usuario).
  • Actualizar datos existentes (por ejemplo, cambiar la dirección de envío de un cliente).
  • Eliminar información (por ejemplo, borrar un artículo de un carrito de compra).
  • Implementar lógica de negocio compleja que reside en la base de datos (por ejemplo, a través de procedimientos almacenados).

Conectando PHP a la Base de Datos

Antes de ejecutar cualquier consulta SQL, lo primero que necesitas es establecer una conexión con la base de datos. Esto implica proporcionar credenciales (servidor, nombre de usuario, contraseña) y especificar la base de datos a la que quieres acceder.

Conexión Usando PDO

PDO es una capa de abstracción de acceso a datos que proporciona una interfaz uniforme para interactuar con diferentes tipos de bases de datos (MySQL, PostgreSQL, SQLite, SQL Server, Oracle, etc.). Esto significa que si cambias de gestor de base de datos, el código PHP para la conexión y las consultas básicas apenas necesita modificaciones.

Para conectar usando PDO, creas una nueva instancia de la clase PDO. El constructor requiere un DSN (Data Source Name), que es una cadena que especifica el tipo de base de datos y los detalles de conexión, y opcionalmente el nombre de usuario y la contraseña.

<?php $dsn = 'mysql:host=localhost;dbname=nombre_bd;charset=utf8mb4'; $usuario = 'tu_usuario'; $contrasena = 'tu_contrasena'; try { $pdo = new PDO($dsn, $usuario, $contrasena); // Configura PDO para lanzar excepciones en caso de error $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo "Conexión exitosa usando PDO"; } catch (PDOException $e) { // Manejo del error echo "Error de conexión: " . $e->getMessage(); // En un entorno de producción, no muestres el error crudo al usuario // logea el error y muestra un mensaje genérico // error_log("Error de conexión PDO: " . $e->getMessage()); // die("Ocurrió un error al conectar con la base de datos."); } ?> 

El uso de bloques try...catch es fundamental para manejar errores de conexión de manera elegante.

Conexión Usando MySQLi

MySQLi (MySQL Improved) es una extensión diseñada específicamente para trabajar con bases de datos MySQL. Ofrece una interfaz tanto procedural como orientada a objetos. Es más rápida que PDO cuando se usa con MySQL porque está optimizada para este gestor, pero carece de la flexibilidad de PDO para cambiar de base de datos.

Conexión usando la interfaz orientada a objetos:

<?php $servidor = 'localhost'; $usuario = 'tu_usuario'; $contrasena = 'tu_contrasena'; $base_datos = 'nombre_bd'; $mysqli = new mysqli($servidor, $usuario, $contrasena, $base_datos); // Verificar la conexión if ($mysqli->connect_error) { // Manejo del error echo "Error de conexión: " . $mysqli->connect_error; // En un entorno de producción... // error_log("Error de conexión MySQLi: " . $mysqli->connect_error); // die("Ocurrió un error al conectar con la base de datos."); } else { echo "Conexión exitosa usando MySQLi"; } // Opcional: establecer el charset (importante para caracteres especiales) $mysqli->set_charset("utf8mb4"); ?> 

Conexión usando la interfaz procedural:

<?php $servidor = 'localhost'; $usuario = 'tu_usuario'; $contrasena = 'tu_contrasena'; $base_datos = 'nombre_bd'; $conexion = mysqli_connect($servidor, $usuario, $contrasena, $base_datos); // Verificar la conexión if (!$conexion) { // Manejo del error echo "Error de conexión: " . mysqli_connect_error(); // En un entorno de producción... // error_log("Error de conexión MySQLi (procedural): " . mysqli_connect_error()); // die("Ocurrió un error al conectar con la base de datos."); } else { echo "Conexión exitosa usando MySQLi (procedural)"; } // Opcional: establecer el charset mysqli_set_charset($conexion, "utf8mb4"); ?> 

Ejecutando Consultas SQL

Una vez establecida la conexión, puedes ejecutar consultas SQL. Existen dos métodos principales: ejecutar consultas simples y ejecutar sentencias preparadas.

Ejecutando Consultas Simples (No Recomendado para Entrada de Usuario)

Este método es adecuado para consultas estáticas que no incluyen datos proporcionados por el usuario. Usarlo con entrada de usuario es un riesgo de inyección SQL.

Con PDO

Para consultas que no retornan conjuntos de resultados (INSERT, UPDATE, DELETE), puedes usar exec(). Retorna el número de filas afectadas.

<?php // Suponiendo que $pdo es tu objeto de conexión PDO try { $sql = "INSERT INTO usuarios (nombre, email) VALUES ('Juan', '[email protected]')"; $filas_afectadas = $pdo->exec($sql); echo "Filas insertadas: " . $filas_afectadas; } catch (PDOException $e) { echo "Error al ejecutar INSERT: " . $e->getMessage(); } ?> 

Para consultas que retornan conjuntos de resultados (SELECT), usas query(). Retorna un objeto PDOStatement que puedes iterar.

<?php // Suponiendo que $pdo es tu objeto de conexión PDO try { $sql = "SELECT id, nombre, email FROM usuarios"; $resultado = $pdo->query($sql); echo "<h3>Lista de Usuarios:</h3><ul>"; while ($fila = $resultado->fetch(PDO::FETCH_ASSOC)) { echo "<li>ID: " . $fila['id'] . ", Nombre: " . $fila['nombre'] . ", Email: " . $fila['email'] . "</li>"; } echo "</ul>"; } catch (PDOException $e) { echo "Error al ejecutar SELECT: " . $e->getMessage(); } ?> 

Con MySQLi

Tanto para consultas que modifican datos como para SELECT, usas el método query() (orientado a objetos) o la función mysqli_query() (procedural). Para SELECT, retorna un objeto mysqli_result; para otras consultas, retorna true o false.

Orientado a Objetos:

<?php // Suponiendo que $mysqli es tu objeto de conexión MySQLi // INSERT $sql_insert = "INSERT INTO usuarios (nombre, email) VALUES ('Pedro', '[email protected]')"; if ($mysqli->query($sql_insert) === TRUE) { echo "Nuevo registro insertado exitosamente"; } else { echo "Error: " . $sql_insert . "<br>" . $mysqli->error; } // SELECT $sql_select = "SELECT id, nombre, email FROM usuarios"; $resultado = $mysqli->query($sql_select); if ($resultado->num_rows > 0) { echo "<h3>Lista de Usuarios:</h3><ul>"; // Recorrer los resultados while($fila = $resultado->fetch_assoc()) { echo "<li>ID: " . $fila['id'] . ", Nombre: " . $fila['nombre'] . ", Email: " . $fila['email'] . "</li>"; } echo "</ul>"; } else { echo "0 resultados"; } // Liberar el conjunto de resultados $resultado->free(); ?> 

Procedural:

<?php // Suponiendo que $conexion es tu conexión MySQLi procedural // INSERT $sql_insert = "INSERT INTO usuarios (nombre, email) VALUES ('Ana', '[email protected]')"; if (mysqli_query($conexion, $sql_insert)) { echo "Nuevo registro insertado exitosamente"; } else { echo "Error: " . $sql_insert . "<br>" . mysqli_error($conexion); } // SELECT $sql_select = "SELECT id, nombre, email FROM usuarios"; $resultado = mysqli_query($conexion, $sql_select); if (mysqli_num_rows($resultado) > 0) { echo "<h3>Lista de Usuarios:</h3><ul>"; // Recorrer los resultados while($fila = mysqli_fetch_assoc($resultado)) { echo "<li>ID: " . $fila['id'] . ", Nombre: " . $fila['nombre'] . ", Email: " . $fila['email'] . "</li>"; } echo "</ul>"; } else { echo "0 resultados"; } // Liberar el conjunto de resultados mysqli_free_result($resultado); ?> 

El Poder de las Sentencias Preparadas (Prepared Statements)

Las sentencias preparadas son la forma recomendada y más segura de ejecutar consultas SQL, especialmente aquellas que incluyen datos proporcionados por el usuario. Ayudan a prevenir ataques de inyección SQL, que ocurren cuando un atacante inserta código SQL malicioso a través de la entrada de usuario para manipular la consulta.

El proceso general es:

  1. Preparas la plantilla de la consulta con marcadores de posición para los valores.
  2. El gestor de base de datos analiza (parsea) la plantilla.
  3. Vinculas los valores reales a los marcadores de posición.
  4. Ejecutas la consulta.

Los valores vinculados se envían al servidor de base de datos por separado de la consulta SQL, lo que garantiza que no puedan ser interpretados como parte de la estructura de la consulta SQL.

Sentencias Preparadas con PDO

PDO utiliza marcadores de posición con signo de interrogación (?) o marcadores con nombre (:nombre).

Usando marcadores con signo de interrogación:

<?php // Suponiendo que $pdo es tu objeto de conexión PDO $email_buscado = '[email protected]'; $sql = "SELECT id, nombre FROM usuarios WHERE email = ?"; try { // 1. Preparar la sentencia $stmt = $pdo->prepare($sql); // 2. Vincular el valor (el tipo de dato es opcional en bindParam/bindValue pero buena práctica) $stmt->bindParam(1, $email_buscado, PDO::PARAM_STR); // bindParam() vincula una variable por referencia. Si el valor de $email_buscado cambia antes de execute(), se usará el nuevo valor. // bindValue() vincula un valor por valor. Se usa el valor de $email_buscado en el momento de bindValue(). // 3. Ejecutar la sentencia $stmt->execute(); // 4. Obtener resultados $usuario = $stmt->fetch(PDO::FETCH_ASSOC); if ($usuario) { echo "Usuario encontrado: ID = " . $usuario['id'] . ", Nombre = " . $usuario['nombre']; } else { echo "Usuario no encontrado"; } } catch (PDOException $e) { echo "Error al ejecutar sentencia preparada (PDO ?): " . $e->getMessage(); } ?> 

Usando marcadores con nombre:

<?php // Suponiendo que $pdo es tu objeto de conexión PDO $email_buscado = '[email protected]'; $sql = "SELECT id, nombre FROM usuarios WHERE email = :email"; try { // 1. Preparar la sentencia $stmt = $pdo->prepare($sql); // 2. Vincular el valor $stmt->bindParam(':email', $email_buscado, PDO::PARAM_STR); // 3. Ejecutar la sentencia $stmt->execute(); // 4. Obtener resultados $usuario = $stmt->fetch(PDO::FETCH_ASSOC); if ($usuario) { echo "Usuario encontrado: ID = " . $usuario['id'] . ", Nombre = " . $usuario['nombre']; } else { echo "Usuario no encontrado"; } } catch (PDOException $e) { echo "Error al ejecutar sentencia preparada (PDO :nombre): " . $e->getMessage(); } ?> 

También puedes pasar un array de valores directamente a execute() sin usar bindParam/bindValue:

<?php // Suponiendo que $pdo es tu objeto de conexión PDO $email_buscado = '[email protected]'; $nombre_buscado = 'Ana'; $sql = "SELECT id FROM usuarios WHERE email = ? AND nombre = ?"; try { // 1. Preparar la sentencia $stmt = $pdo->prepare($sql); // 2 & 3. Ejecutar la sentencia pasando los valores en un array $stmt->execute([$email_buscado, $nombre_buscado]); // 4. Obtener resultados $usuario = $stmt->fetch(PDO::FETCH_ASSOC); if ($usuario) { echo "Usuario encontrado por email y nombre. ID = " . $usuario['id']; } else { echo "Usuario no encontrado por email y nombre"; } } catch (PDOException $e) { echo "Error al ejecutar sentencia preparada (PDO array): " . $e->getMessage(); } ?> 

Sentencias Preparadas con MySQLi

MySQLi también soporta sentencias preparadas, usando solo marcadores de posición con signo de interrogación (?).

Orientado a Objetos:

<?php // Suponiendo que $mysqli es tu objeto de conexión MySQLi $email_buscado = '[email protected]'; $sql = "SELECT id, nombre FROM usuarios WHERE email = ?"; // 1. Preparar la sentencia $stmt = $mysqli->prepare($sql); if ($stmt === FALSE) { echo "Error al preparar la sentencia: " . $mysqli->error; } else { // 2. Vincular el parámetro // El primer argumento de bind_param es una cadena que especifica los tipos de datos // 's' para string, 'i' para integer, 'd' para double, 'b' para blob $stmt->bind_param("s", $email_buscado); // 3. Ejecutar la sentencia $stmt->execute(); // 4. Vincular resultados a variables (opcional, pero útil) $stmt->bind_result($id, $nombre); // 5. Obtener resultados (fetch) if ($stmt->fetch()) { echo "Usuario encontrado: ID = " . $id . ", Nombre = " . $nombre; } else { echo "Usuario no encontrado"; } // Cerrar la sentencia $stmt->close(); } ?> 

Procedural:

<?php // Suponiendo que $conexion es tu conexión MySQLi procedural $email_buscado = '[email protected]'; $sql = "SELECT id, nombre FROM usuarios WHERE email = ?"; // 1. Preparar la sentencia $stmt = mysqli_prepare($conexion, $sql); if ($stmt === FALSE) { echo "Error al preparar la sentencia: " . mysqli_error($conexion); } else { // 2. Vincular el parámetro mysqli_stmt_bind_param($stmt, "s", $email_buscado); // 3. Ejecutar la sentencia mysqli_stmt_execute($stmt); // 4. Vincular resultados a variables (opcional) mysqli_stmt_bind_result($stmt, $id, $nombre); // 5. Obtener resultados (fetch) if (mysqli_stmt_fetch($stmt)) { echo "Usuario encontrado: ID = " . $id . ", Nombre = " . $nombre; } else { echo "Usuario no encontrado"; } // Cerrar la sentencia mysqli_stmt_close($stmt); } ?> 

Es importante destacar que con MySQLi, después de ejecutar un SELECT preparado, necesitas usar fetch() en el objeto statement y, a menudo, store_result() si esperas muchos resultados o necesitas saber el número de filas antes de fetchear.

Manejo de Errores

Un manejo adecuado de errores es vital. No quieres que tu aplicación muestre mensajes de error técnicos de la base de datos a los usuarios. Configura tu conexión para que lance excepciones (PDO) o verifica los códigos de error después de cada operación (MySQLi).

Con PDO, al configurar PDO::ATTR_ERRMODE a PDO::ERRMODE_EXCEPTION, cualquier error de SQL lanzará una excepción PDOException que puedes capturar con try...catch.

Con MySQLi, debes verificar el valor de retorno de las funciones (por ejemplo, query(), prepare(), execute()) y usar las propiedades $mysqli->error o $mysqli->connect_error (orientado a objetos) o las funciones mysqli_error() o mysqli_connect_error() (procedural) para obtener detalles del error.

Transacciones

Para operaciones que involucran múltiples consultas que deben completarse juntas o fallar juntas (atomicidad), se utilizan transacciones. Esto es crucial en escenarios como transferencias de dinero (debitar una cuenta, acreditar otra).

Con PDO:

<?php // Suponiendo que $pdo es tu objeto de conexión PDO try { $pdo->beginTransaction(); // Primera operación $sql1 = "UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1"; $pdo->exec($sql1); // Segunda operación $sql2 = "UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2"; $pdo->exec($sql2); // Si todo fue bien, confirmar la transacción $pdo->commit(); echo "Transacción completada exitosamente."; } catch (PDOException $e) { // Si algo falló, revertir los cambios $pdo->rollBack(); echo "Error en la transacción: " . $e->getMessage(); } ?> 

Con MySQLi (Orientado a Objetos):

<?php // Suponiendo que $mysqli es tu objeto de conexión MySQLi // Desactivar autocommit $mysqli->autocommit(FALSE); $todo_bien = TRUE; // Primera operación $sql1 = "UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1"; if ($mysqli->query($sql1) === FALSE) { $todo_bien = FALSE; } // Segunda operación $sql2 = "UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2"; if ($mysqli->query($sql2) === FALSE) { $todo_bien = FALSE; } if ($todo_bien) { // Confirmar la transacción $mysqli->commit(); echo "Transacción completada exitosamente."; } else { // Revertir los cambios $mysqli->rollback(); echo "Transacción fallida."; // Opcional: registrar el error: error_log("MySQLi Transaction Error: " . $mysqli->error); } // Reactivar autocommit (opcional) $mysqli->autocommit(TRUE); ?> 

Cerrando la Conexión

Aunque PHP cierra automáticamente la conexión a la base de datos cuando el script termina, es una buena práctica cerrarla explícitamente cuando ya no se necesita, especialmente en scripts largos o si manejas múltiples conexiones.

Con PDO, simplemente asigna null al objeto de conexión:

<?php // ... operaciones de base de datos ... $pdo = null; ?> 

Con MySQLi, usa el método close() (orientado a objetos) o la función mysqli_close() (procedural):

<?php // ... operaciones de base de datos ... // Orientado a Objetos $mysqli->close(); // Procedural mysqli_close($conexion); ?> 

PDO vs. MySQLi: ¿Cuál Elegir?

Ambas son opciones válidas y seguras para interactuar con MySQL. La elección depende a menudo de tus necesidades y preferencias.

CaracterísticaPDOMySQLi
Bases de Datos SoportadasMúltiples (MySQL, PostgreSQL, SQLite, etc.)Principalmente MySQL
Interfaces DisponiblesOrientada a ObjetosOrientada a Objetos y Procedural
Sentencias PreparadasSí (marcadores ? y :nombre)Sí (marcadores ?)
Rendimiento (con MySQL)Muy buenoLigeramente superior (optimizado para MySQL)
Facilidad de UsoUniforme para diferentes BDDos estilos (OO/procedural)
Manejo de ErroresExcepciones (recomendado)Códigos de error y propiedades/funciones de error

Si trabajas o planeas trabajar con diferentes tipos de bases de datos, PDO es la opción más flexible. Si solo vas a usar MySQL y prefieres tener una interfaz procedural además de la orientada a objetos, MySQLi es una excelente elección.

Preguntas Frecuentes

¿Qué es la inyección SQL y cómo la evito?

La inyección SQL es una vulnerabilidad de seguridad donde un atacante manipula una consulta SQL a través de la entrada de usuario. Se evita principalmente utilizando sentencias preparadas y vinculando correctamente los parámetros. Nunca concatenes directamente la entrada de usuario en tus consultas SQL.

¿Necesito sanitizar la entrada de usuario si uso sentencias preparadas?

Las sentencias preparadas se encargan de separar los datos de la lógica SQL, previniendo la inyección SQL en la propia consulta. Sin embargo, aún es una buena práctica validar y, si es necesario, sanitizar la entrada de usuario para otros fines (por ejemplo, asegurar que un email tenga formato válido, que un número sea realmente un número), pero no para proteger la consulta SQL de la inyección.

¿Es mejor PDO o MySQLi?

Ambas son buenas opciones modernas. PDO es más versátil para múltiples tipos de bases de datos, mientras que MySQLi está optimizado para MySQL y ofrece una interfaz procedural. La elección depende de tus necesidades específicas. Lo importante es usar sentencias preparadas con cualquiera de ellas.

¿Cómo manejo grandes conjuntos de resultados?

Fetchear todos los resultados a la vez en la memoria puede ser problemático con conjuntos muy grandes. Tanto PDO como MySQLi permiten fetchear filas una por una dentro de un bucle (como se mostró en los ejemplos con fetch() o fetch_assoc()), lo cual es más eficiente en memoria. Para casos extremos, podrías necesitar técnicas específicas del gestor de base de datos o paginación en tu aplicación.

¿Debo cerrar la conexión a la base de datos?

PHP cierra la conexión automáticamente al finalizar el script. Cerrarla explícitamente es opcional pero puede ser útil en scripts largos o para liberar recursos antes si la base de datos ya no se usará.

Conclusión

Ejecutar código SQL en PHP es una habilidad esencial para cualquier desarrollador web. Utilizar las extensiones modernas como PDO o MySQLi y, sobre todo, dominar el uso de sentencias preparadas, te permitirá interactuar con tus bases de datos de forma segura, eficiente y mantenible. Recuerda siempre priorizar la seguridad para proteger tu aplicación y los datos de tus usuarios.

Si quieres conocer otros artículos parecidos a Ejecutar Código SQL en PHP: Guía Completa puedes visitar la categoría Bases de datos.

Ivan

Soy un entusiasta de la tecnología con especialización en bases de datos, particularmente en MySQL. A través de mis tutoriales detallados, busco desmitificar los conceptos complejos y proporcionar soluciones prácticas a los desafíos cotidianos relacionados con la gestión de datos

Aprende mas sobre MySQL

Subir