epic

Pro
Se incorporó
11 Febrero 2007
Mensajes
846
Hola a todos nuevamente ;)

Tengo una duda... tengo la siguiente consulta:

Código:
<?php
    require 'conexion.php';
    $campo= "";
    if(!empty($_POST))
    {
    $valor = $_POST['rut'];
    if(!empty($valor)){
    $campo= "WHERE pacientes.rut LIKE '%$valor'";
    }
}
$sql = "SELECT pacientes.rut, pacientes.nombres, pacientes.apellidos, centros.centro, insumos.cant_lancetas, insumos.cant_cintas, altas.estatus
        FROM pacientes
        LEFT OUTER JOIN centros ON pacientes.id_centro = centros.id_centro
        LEFT OUTER JOIN insumos ON pacientes.rut=insumos.rut
        LEFT OUTER JOIN altas ON pacientes.rut=altas.rut
        $campo GROUP BY pacientes.rut";
$resultado = $mysqli->query($sql) or trigger_error($mysqli->error);
?>

Tengo mas abajo 2 botones , uno que le pone una flag de "Alta" al registro y el otro boton crea otro registro con el mismo rut, pero ahora con flag de "Activo" y asi van esos 2 botones, creando registros (con fecha y el usuario que lo hace) asociados a ese rut pero cambiando de "Alta" a Activo" o de "Activo" a "Alta".

En la BD se almacenan bien los registros, pero al momento de mostrar con esa consulta SQL de mas arriba, me fije que en ocaciones no traia el valor real del registro, hacia Altas, Activaciones, Altas, Activaciones y todo bien, pero en un momento ya me atraia por ejemplo siempre "Alta" aunque en la BD el ULTIMO registro fuese "Activo"(si, necesito que siempre se muestre el ultimo "estatus" de ese paciente.

La tabla pacientes con la tabla Altas estan asociadas con el rut del paciente.
 

frosstatx

AMD EX-NV Y LINUX FANBOY
Se incorporó
27 Junio 2008
Mensajes
482
Te recomiendo hacer un procedimiento almacenado, asi tendras mas "juego de piernas" y en caso de modifcacion no tendras que meterte al codigo de php , se me ocurre un monton de formas para tener el ultimo.... por ejemplo usando el having para filtrar por la ultima fecha fecha
 
Upvote 0

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
No he testeado, asi que puede haber errores, pero esto te deberia dar un poco mas seguridad. Ahi tu lo adaptas.
PHP:
<?php
    require 'conexion.php';
    if(!empty($_POST))
    {
    $valor = trim($_POST['rut']);
    if(!empty($valor)){
        if(preg_match('^(\d{1,2}(?:[\.]?\d{3}){2}-[\dkK])$',$valor)){
            $rut = "%".$valor;
        }else{
            die('rut invalido');
        }
    }
}else{
die('rut vacio');
}


$stmt = $conn->prepare("SELECT pacientes.rut, pacientes.nombres, pacientes.apellidos, centros.centro, insumos.cant_lancetas, insumos.cant_cintas, altas.estatus
FROM pacientes
LEFT OUTER JOIN centros ON pacientes.id_centro = centros.id_centro
LEFT OUTER JOIN insumos ON pacientes.rut=insumos.rut
LEFT OUTER JOIN altas ON pacientes.rut=altas.rut
WHERE pacientes.rut LIKE ?
GROUP BY pacientes.rut");
$stmt->bind_param("s", $rut);
$stmt->execute();
$resultado = $stmt->get_result();

Lo que intente hacer:
- Usar regex para ver que el rut, sea un rut.
- usar prepared statements para dificultar la inyeccion en tu BD
-trim() para borrar espacion en blanco
 
Última modificación:
Upvote 0

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
aunque tenga antes un login y cada pagina este con sesiones ?
Es buena practica hacerlo, te servirá para hoy y para tus futuros desarrollos.
Incluso el formulario de login podrías pegarle una mirada que este con bastante seguridad.
 
Upvote 0

epic

Pro
Se incorporó
11 Febrero 2007
Mensajes
846
Es buena practica hacerlo, te servirá para hoy y para tus futuros desarrollos.
Incluso el formulario de login podrías pegarle una mirada que este con bastante seguridad.

La primera es un login y esta con https, todo dentro de un contenedor aislado detrás de un proxy.:

Código:
<?php
session_start();
if(isset($_SESSION['nombredelusuario']))
{
    header('location: index.php');
}
if(isset($_POST['btningresar']))
{
######## PRODUCCIÓN ########  
    $dbhost="xxxx";
    $dbuser="xxxx";
    $dbpass="xxxx";
    $dbname="xxxx";
    $conn=mysqli_connect($dbhost,$dbuser,$dbpass,$dbname);
    if(!$conn)
    {
        die("No hay conexión: ".mysqli_connect_error());
    }
  
    $nombre=$_POST['txtusuario'];
    $pass=$_POST['txtpassword'];
  
    $query=mysqli_query($conn,"Select * from usuarios where usuario = '".$nombre."' and password = '".$pass."'");
    $nr=mysqli_num_rows($query);
  
    if(!isset($_SESSION['nombredelusuario']))
    {
    if($nr == 1)
    {
        $_SESSION['nombredelusuario']=$nombre;
        header("location: index.php");
    }
    else if ($nr == 0)
    {
        echo "<script>alert('Usuario no existe');window.location= 'login.php' </script>";
    }
    }
}
?>
<html>
    <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="css/login.css">
    </head>
    <body>
        <div>
            <form method="POST">
            <table>
                <tr><td colspan="2" style="background-color:#009fae;padding-bottom:10px;"><label>INICIAR SESIÓN</label></td></tr>
                <tr><td rowspan="6"><img src="logo.png"></td><td><label>Usuario</label></td></tr>
                <tr><td><input type="text" name="txtusuario" placeholder="Ingresar usuario" required /> </td></tr>
                <tr><td><label>Contraseña</label></td></tr>
                <tr><td><input type="password" name="txtpassword" placeholder="Ingresar password" required /> </td></tr>
                <tr><td><input type="submit" name="btningresar" value="Ingresar"/></td></tr>
                <tr><td>Version 1.0</td></tr>
                <tr><td align="right" style="color:#000000;font-size:80%;">© 2022 Sistema </td></tr>
            </table>
            </form>
        </div>
    </body>
</html>

[CODE]


Se ve decente???
 
Upvote 0

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
La primera es un login y esta con https, todo dentro de un contenedor aislado detrás de un proxy.:

Código:
<?php
session_start();
if(isset($_SESSION['nombredelusuario']))
{
    header('location: index.php');
}
if(isset($_POST['btningresar']))
{
######## PRODUCCIÓN ########
    $dbhost="xxxx";
    $dbuser="xxxx";
    $dbpass="xxxx";
    $dbname="xxxx";
    $conn=mysqli_connect($dbhost,$dbuser,$dbpass,$dbname);
    if(!$conn)
    {
        die("No hay conexión: ".mysqli_connect_error());
    }
 
    $nombre=$_POST['txtusuario'];
    $pass=$_POST['txtpassword'];
 
    $query=mysqli_query($conn,"Select * from usuarios where usuario = '".$nombre."' and password = '".$pass."'");
    $nr=mysqli_num_rows($query);
 
    if(!isset($_SESSION['nombredelusuario']))
    {
    if($nr == 1)
    {
        $_SESSION['nombredelusuario']=$nombre;
        header("location: index.php");
    }
    else if ($nr == 0)
    {
        echo "<script>alert('Usuario no existe');window.location= 'login.php' </script>";
    }
    }
}
?>
<html>
    <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="css/login.css">
    </head>
    <body>
        <div>
            <form method="POST">
            <table>
                <tr><td colspan="2" style="background-color:#009fae;padding-bottom:10px;"><label>INICIAR SESIÓN</label></td></tr>
                <tr><td rowspan="6"><img src="logo.png"></td><td><label>Usuario</label></td></tr>
                <tr><td><input type="text" name="txtusuario" placeholder="Ingresar usuario" required /> </td></tr>
                <tr><td><label>Contraseña</label></td></tr>
                <tr><td><input type="password" name="txtpassword" placeholder="Ingresar password" required /> </td></tr>
                <tr><td><input type="submit" name="btningresar" value="Ingresar"/></td></tr>
                <tr><td>Version 1.0</td></tr>
                <tr><td align="right" style="color:#000000;font-size:80%;">© 2022 Sistema </td></tr>
            </table>
            </form>
        </div>
    </body>
</html>

[CODE]


Se ve decente???
En general estas bien, aunque aun asi haria algunos cambios
- Usar prepared statements de ahora en adelante cada vez que puedas. Una vez que le agarras la costumbre es muy util.
Cual es la gracia? pues que tu puedes definir previamente que valores van a pasar a la consulta, por ejemplo: solo pasaran numeros, solo pasaran strings, etc. Muy util contra inyeccion sql.

- Almacenar passwords encriptados. Al parecer usas texto plano no? esto es muy importante que lo arregles.
Las funciones relevantes son password_hash y password_verify

-Usar recaptcha para evitar que pruebe millones de password por segundo. Esto es lo menos importante ahora

Hice un pequeño script en el que te peudes basar y mejorar. Requiere que tengas los passwords encriptados previamente.
PHP:
if (!empty($_POST['email']) && !empty($_POST['password'])) {
    $records = $conexion->prepare('SELECT id, email, password FROM users WHERE email = ?');
    $records->bind_param("s", $_POST['email']);
    $records->execute();
    $results = $records->get_result();
    $results = $results->fetch_all(MYSQLI_ASSOC);
    if ($results) {
        if (count($results) > 0 && password_verify($_POST['password'], $results[0]['password'])) {
            $_SESSION['user_id'] = $results[0]['id'];
            $_SESSION['email'] = $results[0]['email'];
            header("Location: index.php");
        } else {
            die('Usuario o password incorrecto.');
        }
    } else {
        die('Usuario o password incorrecto.');
    }
}
Le peudes hacer mas cosas incluso, como validar el email antes de pasarlo a sql usando
 
Última modificación:
Upvote 0

epic

Pro
Se incorporó
11 Febrero 2007
Mensajes
846
Gracias por tu tiempo @ricm

Si, los pasword están en texto plano, ya que en un comienzo estos pequeños sistemas eran internamente para los usuarios de un deptos. especifico en la empresa(pero es algo que quería de igual forma hacer), aunque los que se conectan desde fuera son de unos centros y están filtrados por la ip en el proxy(mas todo lo que comente arriba), osea nadie que no se conecte desde esas ip le carga el login ni nada.

Hago muchas preguntas, ya que en realidad soy de infra , pero por cosas de la vida en estos momentos me toco ayudar en php :S , veré primero lo de la encriptacion de las claves.

Pero me urge un poco saber lo de mi primer post, porque en ocasiones esa consulta no me devuelve el ultimo dato del estatus del paciente (Alta/Activo)
 
Upvote 0

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
No entendí bien cual es el problema inicial.
Cual es el valor "real"? Es el último para un rut?
Probaste la consulta desde la consola sql?
Quizá pudieras pegar un ejemplo del resultado deseado y el resultado obtenido.

Estoy imaginando que el problema es que un mismo rut te dice activo cuando debería decir alta por ejemplo. Sospecho que eso puede ser un problema causado por group by.
 
Upvote 0

epic

Pro
Se incorporó
11 Febrero 2007
Mensajes
846
Aqui varias soluciones a su pregunta: https://learnsql.com/blog/sql-join-only-first-row/

Con respecto al resto de cosas comentadas.. estoy de acuerdo, hay varias cosas que mejorar con respecto a seguridad ahí.
gracia de ahi saque gran parte de lo que necesitaba, pero no logro sacar la suma de unos item:

SQL:
SELECT pacientes.rut, pacientes.nombres, pacientes.apellidos, last_altas.estatus, centros.centro, insumos.cant_lancetas
FROM pacientes
LEFT OUTER JOIN insumos ON (insumos.rut=pacientes.rut)

LEFT JOIN (
     SELECT *
     FROM altas
     WHERE id_alta IN (
        SELECT MAX(id_alta)
        FROM altas
        GROUP BY rut
)
) AS last_altas
ON pacientes.rut = last_altas.rut
   
JOIN (
     SELECT *
     FROM centros
     WHERE id_centro IN (
        SELECT MAX(id_centro)
        FROM centros
        GROUP BY id_centro
)
) AS centros
ON pacientes.id_centro = centros.id_centro

WHERE pacientes.rut LIKE "11111111-1"
ORDER BY pacientes.rut;


Si dejo eso me muestra 3 registros ya que hay 3 inserciones de "lancetas",
1666376130144.png


pero si le meto el SUM a la variable insumos.cant_lancetas, osea si la dejo SUM( insumos.cant_lancetas) me arroja error:
In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'araucania.pacientes.rut'; this is incompatible with sql_mode=only_full_group_by
1666376210539.png


Si le elimino la linea del "LEFT OUTER JOIN insumos ON (insumos.rut=pacientes.rut)" y la variable SUM( insumos.cant_lancetas), me arroja el resultado bien, me arroja el ultimo estatus del paciente buscado.

1666376245486.png



Esta es la tabla de Insumos:
1666376300127.png



Y este es el modelo entidad relacion:
1666376370746.png
 
Upvote 0

Mesita

Capo
Se incorporó
3 Mayo 2007
Mensajes
100
gracia de ahi saque gran parte de lo que necesitaba, pero no logro sacar la suma de unos item:

SQL:
SELECT pacientes.rut, pacientes.nombres, pacientes.apellidos, last_altas.estatus, centros.centro, insumos.cant_lancetas
FROM pacientes
LEFT OUTER JOIN insumos ON (insumos.rut=pacientes.rut)

LEFT JOIN (
     SELECT *
     FROM altas
     WHERE id_alta IN (
        SELECT MAX(id_alta)
        FROM altas
        GROUP BY rut
)
) AS last_altas
ON pacientes.rut = last_altas.rut
  
JOIN (
     SELECT *
     FROM centros
     WHERE id_centro IN (
        SELECT MAX(id_centro)
        FROM centros
        GROUP BY id_centro
)
) AS centros
ON pacientes.id_centro = centros.id_centro

WHERE pacientes.rut LIKE "11111111-1"
ORDER BY pacientes.rut;


Si dejo eso me muestra 3 registros ya que hay 3 inserciones de "lancetas",
Ver adjunto 26908

pero si le meto el SUM a la variable insumos.cant_lancetas, osea si la dejo SUM( insumos.cant_lancetas) me arroja error:
In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'araucania.pacientes.rut'; this is incompatible with sql_mode=only_full_group_by
Ver adjunto 26909

Si le elimino la linea del "LEFT OUTER JOIN insumos ON (insumos.rut=pacientes.rut)" y la variable SUM( insumos.cant_lancetas), me arroja el resultado bien, me arroja el ultimo estatus del paciente buscado.

Ver adjunto 26910


Esta es la tabla de Insumos:
Ver adjunto 26911


Y este es el modelo entidad relacion:
Ver adjunto 26912
Para que funcione el SUM tienes que agregar el GROUP BY como parte de tu query y agregar las columnas que no son agregaciones como condiciones del GROUP BY.
Mirando así a la rápida veo que también puedes hacer una subquery dentro del JOIN de insumos y ahi hacer un solo GROUP BY que te devuelva la suma.

De todas maneras, no es un problema que insumos solamente se relacione con el rut de un paciente ? Es decir, esto te va a devolver la suma total de insumos (lancetas) asociadas a ese RUT independiente de si fueron usadas en distintas atenciones.

La verdad me cuesta un poco recomendar cosas sin tener contexto de lo que realmente estas tratando de lograr.
 
Upvote 0

ricm

Se incorporó
28 Agosto 2005
Mensajes
7.590
Para que funcione el SUM tienes que agregar el GROUP BY como parte de tu query y agregar las columnas que no son agregaciones como condiciones del GROUP BY.
Mirando así a la rápida veo que también puedes hacer una subquery dentro del JOIN de insumos y ahi hacer un solo GROUP BY que te devuelva la suma.

De todas maneras, no es un problema que insumos solamente se relacione con el rut de un paciente ? Es decir, esto te va a devolver la suma total de insumos (lancetas) asociadas a ese RUT independiente de si fueron usadas en distintas atenciones.

La verdad me cuesta un poco recomendar cosas sin tener contexto de lo que realmente estas tratando de lograr.
Cual es el problema inicial que tiene? Yo no logré captar la pregunta 🤪
 
Upvote 0

epic

Pro
Se incorporó
11 Febrero 2007
Mensajes
846
Para que funcione el SUM tienes que agregar el GROUP BY como parte de tu query y agregar las columnas que no son agregaciones como condiciones del GROUP BY.
Mirando así a la rápida veo que también puedes hacer una subquery dentro del JOIN de insumos y ahi hacer un solo GROUP BY que te devuelva la suma.

De todas maneras, no es un problema que insumos solamente se relacione con el rut de un paciente ? Es decir, esto te va a devolver la suma total de insumos (lancetas) asociadas a ese RUT independiente de si fueron usadas en distintas atenciones.

La verdad me cuesta un poco recomendar cosas sin tener contexto de lo que realmente estas tratando de lograr.


Osea esta bien que el insumo se relaciones con el paciente y luego sume el total independiente si fue en una sola atención o en las posibles 50.

SQL:
SELECT pacientes.rut, pacientes.nombres, pacientes.apellidos, last_altas.estatus, centros.centro, total_insumos.cant_lancetas
FROM pacientes
LEFT JOIN (
    SELECT *
    FROM insumos
    WHERE cant_lancetas, cant_cintas IN (
       SELECT SUM(cant_lancetas)
       FROM insumos
       GROUP BY rut
)
) AS total_insumos
ON pacientes.rut = total_insumos.rut
...
..
.

no hace la suma :S
 
Upvote 0

epic

Pro
Se incorporó
11 Febrero 2007
Mensajes
846
Cual es el problema inicial que tiene? Yo no logré captar la pregunta 🤪


jajajaja que no me esta sumando la cantidad de lancetas e idealmente tambien la cantidad de cintas:

SQL:
SELECT pacientes.rut, pacientes.nombres, pacientes.apellidos, last_altas.estatus, centros.centro, insumos.cant_lancetas
FROM pacientes
LEFT OUTER JOIN insumos ON (insumos.rut=pacientes.rut)

LEFT JOIN (
     SELECT *
     FROM altas
     WHERE id_alta IN (
        SELECT MAX(id_alta)
        FROM altas
        GROUP BY rut
)
) AS last_altas
ON pacientes.rut = last_altas.rut
 
JOIN (
     SELECT *
     FROM centros
     WHERE id_centro IN (
        SELECT MAX(id_centro)
        FROM centros
        GROUP BY id_centro
)
) AS centros
ON pacientes.id_centro = centros.id_centro

WHERE pacientes.rut LIKE "11111111-1"
ORDER BY pacientes.rut;



Le cambie el join de insumos por este otro cod:

SQL:
LEFT JOIN (
    SELECT *
    FROM insumos
    WHERE cant_lancetas, cant_cintas IN (
       SELECT SUM(cant_lancetas)
       FROM insumos
       GROUP BY rut
)
) AS total_insumos
ON pacientes.rut = total_insumos.rut

pero no me suma :S
 
Upvote 0

Mesita

Capo
Se incorporó
3 Mayo 2007
Mensajes
100
en realidad creo que basta con hacer algo asi:
SQL:
LEFT JOIN (
    SELECT insumos.rut, SUM(cant_lancetas) AS sum_lancetas, SUM(cant_cintas) AS sum_cintas
    FROM insumos
    GROUP BY insumos.rut
) AS total_insumos
ON pacientes.rut = total_insumos.rut

luego en el select deberias poder seleccionas las columnas definidas como sum_lancetas y sum_cintas sin problema
 
Upvote 0
Subir