La importancia de protegerse de la inyección de sql
Acceso a datos con PDO
Conectar al servidor:
$server = "localhost";
$user = "root";
$password = "";
$db = "sakila";
try {
$conn = new PDO("mysql:host=$server;dbname=$db;charset=UTF8", $user, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (Exception $e) {
echo "Connection failed: " . $e->getMessage();
}
Ejecutar una consulta de acción (también funcionaría con query):
$sql = "update category set name='$nombre' where category_id=$category_id";
if ($conn->exec($sql) === true) {...}
Recorrer los registros:
$sql = "select * from category ";
$resul = $conn->query($sql);
if ($resul->rowCount()>0) {
while ($row = $resul->fetch()) {
...
}
}
Obtener todos los registros de una sola vez:
$sql = "select * from category "; $resul = $conn->query($sql); $rows=$resul->fetchAll()
Insertar y obtener el último id:
$sql = "insert into category (name) values ('" . addslashes($nombre) . "')";
if ($conn->query($sql) == true) {
$last_id = $conn->lastInsertId();
echo "Insertada categoría $nombre con id " . $last_id;
} else {
echo "Error " . $conn->error;
}
Enlazar resultados con variables:
$row = $conn->query('SELECT first_name,last_name from actor');
$row->bindColumn('first_name', $nombre);
$row->bindColumn(2, $apellido);
while ($row->fetch()) {
print "$nombre $apellido.
\n";
}
Preparar sentencias y luego pasar valores:
$st = $conn->prepare('INSERT INTO actor (first_name,last_name) VALUES (?,?)');
$st->execute(array('Vito','Corleone'));
$st->execute(array('Juan','Perez'));
$st = $conn->prepare('select * from actor where actor_id>?');
$st->execute(array(50));
print_r($st->fetchAll());
Pasar valores con nombre:
$st = $conn->prepare('select * from actor where first_name like :first_name');
$st->execute(array(':first_name'=>'%z%'));
Enlazar parámetros con variables:
$actores=['juan'=>'perez','ana'=>'pi','rosa'=>'buj'];
$st = $conn->prepare('insert into actor (first_name,last_name) values(:first_name,:last_name)');
$st->bindParam(':first_name', $first_name);
$st->bindparam(':last_name', $last_name);
foreach($actores as $first_name=>$last_name){
$st->execute();
}
Número de filas (Ojo, no funciona en todas las bases de datos):
$resul->rowCount()
Ejecutar con arrays:
$sentencia = $conn->prepare("INSERT INTO actor( first_name,last_name) VALUES (?,?)");
$sentencia->execute(array('asd','dsa'));
$sentencia = $conn->prepare("INSERT INTO actor( first_name,last_name) VALUES (:f,:l)");
$sentencia->execute(array('f'=>'asd','l'=>'dsa'));
Enlace manual php:
http://php.net/manual/es/pdo.prepared-statements.php
Un ejemplo:
<body>
<form action="#">
País:
<input type="text" name="pais">
<input type="submit">
</form>
<?php
$server = "localhost";
$user = "root";
$password = "";
$db = "sakila";
try {
$conn = new PDO("mysql:host=$server;dbname=$db;charset=UTF8", $user, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//Para insertar, recupero el pais de GET y si hay país lo inserto
$pais = filter_input(INPUT_GET, "pais", FILTER_SANITIZE_MAGIC_QUOTES);
if (!empty($pais)) {
$sql = "insert into country (country) values('$pais')";
if ($conn->exec($sql) > 0) {
echo "Registro insertado<br/>";
}
}
$sql = "select * from country ORDER BY COUNTRY_ID DESC";
$q = $conn->query($sql);
while ($row = $q->fetch()) {
echo $row['country_id'] . "-" . $row['country'] . "<br>";
}
} catch (Exception $e) {
echo "Connection failed: " . $e->getMessage();
}
?>
</body>
Examen BD 2019
Ejemplo de procedimientos almacenados
Parámetros entrada y salida:
CREATE PROCEDURE `test`(in id int, out p_actor varchar(100))
BEGIN
select concat_ws(' ',first_name,last_name) into p_actor from actor where actor_id=id;
END
call test(2, @actor);
CREATE DEFINER=`root`@`localhost` PROCEDURE `suma`(in a int, in b int, out suma int)
BEGIN
set suma=a+b;
END
call suma(8,9,@res);
Alta de registros con comprobación incluída:
CREATE DEFINER=`root`@`localhost` PROCEDURE `alta_actor`(in nombre varchar(100), in apellido varchar(100))
BEGIN
declare c int;
select count(*) into c from actor where first_name=nombre and last_name=apellido;
if c=0 and length(nombre)>1 and length(apellido)>1 then
insert into actor (first_name, last_name) values (nombre, apellido);
end if;
END
call alta_actor ('juan','pa');
call alta_actor ('juan','pa'); --No funcionará por repetido
call alta_actor ('juan','p'); -- no funcionará por longitud
Ejemplo de cursor:
CREATE PROCEDURE `cursor_ejemplo`(out total int) BEGIN DECLARE final int DEFAULT 0; declare nombre varchar(100); DECLARE mi_cursor CURSOR FOR SELECT first_name FROM actor; DECLARE CONTINUE HANDLER FOR NOT FOUND SET final = 1; set total=0; OPEN mi_cursor; while final=0 do fetch mi_cursor into nombre; if nombre like '%z%' then set total=total+1; end if; end while; CLOSE mi_cursor; END call cursor_ejemplo(@t);
Procedimientos almacenados
Ejemplos de procedimientos almacenados
CREATE DEFINER=`root`@`localhost` PROCEDURE film_in_stock (IN p_film_id INT, IN p_store_id INT, OUT p_film_count INT) READS SQL DATA BEGIN SELECT inventory_id FROM inventory WHERE film_id = p_film_id AND store_id = p_store_id AND inventory_in_stock(inventory_id); SELECT FOUND_ROWS() INTO p_film_count; END
call film_in_stock(123,1,@w); select @w
CREATE DEFINER=`root`@`localhost` PROCEDURE `actores_por_categoria` (in p_categoria varchar(50)) BEGIN select distinct concat(first_name,' ',last_name) actor from actor join film_actor using (actor_id) join film using (film_id) join film_category using (film_id) join category c using (category_id) where c.name=p_categoria order by actor; END
call actores_por_categoria('Animation')
Ejercicios vistas
Crear una vista que nos muestre el pais, la ciudad, la dirección y el nombre de los clientes. La podemos llamar clientes_direccion
Con esa vista creada será muy fácil mostrar los clientes de Argentina o Italia
Crear una vista que nos relacione la película con sus pagos. Que nos muestre el id de la película, el title, y todos los datos de payment.
Con esa vista sería muy fácil ver el total de pagos por película.
Creando vistas en mysql
Categorías, películas y actores:
create view pelistotal as select category_id, category.name nombrecategoria, film_id,title,actor_id, first_name,last_name from category join film_category using (category_id) join film using (film_id) join film_actor using (film_id) join actor using (actor_id)
La usamos como una tabla normal:
select name,count(actor_id) total from pelistotal group by category_id
select * from actor where actor_id not in ( SELECT actor_id FROM pelistotal where name='action');
Como hacer una consulta del tipo actores que no han trabajado en películas de acción si usar subconsultas. Primero creamos la vista en positivo:
create view actores_accion as select actor_id,first_name,last_name from category join film_category using (category_id) join film using (film_id) join film_actor using (film_id) join actor using (actor_id) where name='action'
Y después hacemos un left join y buscamos los nulos:
SELECT * FROM actor left join actores_accion using(actor_id) where actores_accion.first_name is null
Ejemplos Create view
create view peliculas_pais as select country, count(rental_id) total from country join city using (country_id) join address using(city_id) join customer using(address_id) join rental using(customer_id) group by country_id
CREATE VIEW peliculas_categoria AS select name, count(film_id) total from category join film_category using (category_id) group by category_id
