Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Boa noite, galera, tudo tranquilo?
Sou novo na área de programação, e principalmente em Php/MySQL rsrs.
Há alguns meses, me meti a fazer um sistema de chamadas para o Cursinho do qual sou coordenador. Ele funciona, mas venho tendo alguns problemas de performance e velocidade do registro dos dados no banco de dados, e se vocês pudessem me auxiliar a dar uma acelerada nisso eu ficaria muito grato.
Antes de qualquer coisa, vou explicar por cima como funciona:
Existe uma tabela principal, com o nome de chamadas, que guarda o registros de todas as chamadas realizadas no cursinho. Dentro dessa tabela, estão contidos o ID único de cada chamada, o horário, o professor que a realizou e sala. São quatro salas, e cada número (1, 2, 3 ou 4) no campo 'sala' da chamada faz que o sistema redirecione para a tabela de presenças da sala, onde cada sala tem uma tabela, para o registro das presenças dos alunos.
Na tabela de presenças, em que cada sala tem uma, o sistema preenche cada linha com o ID da chamada, o RA do aluno e a presença (0 para falta e 1 para presença). Desse modo, nessa tabela, muitas linhas são inseridas por chamada realizada (por volta de 40 a 60 linhas, a depender da sala).
Acontece que, quando testo com uma internet de qualidade, ou mesmo no meu localhost, a inserção desses resultados funciona muito bem, e não obtenho erro algum. Só que, na escola onde as chamadas são realizadas, a internet é apenas "navegável", e os professores têm tido alguns problemas de erros na página enquanto tentam realizar o procedimento.
Dentro do script Php, as coisas funcionam mais ou menos assim: existe um IF que verifica, antes de tudo, se os dados da chamada conseguiram ser inseridos na tabela principal. Aí, caso a condição seja verdadeira, um laço while começa a inserção dos registros na tabela de presenças. E eu tenho tido alguns erros justamente nesse ponto... Em muitas chamadas realizadas na escola, o script chega a registrar a chamada na tabela principal mas não lança as presenças na outra tabela... E em outras vezes, registra os dados na tabela principal apenas de forma parcial, ou seja, preenche ID, data, e deixa o resto em branco, parando o processo todo ali... E tudo era para ser integrado, eu realmente não sei o que acontece. No meu raciocínio, primeiro o browser recolhia os dados e enviava ao servidor, e depois o servidor lançaria esses dados de uma vez, mas não parece que é isso que está acontecendo.
Gostaria de saber se: é muito grave a lógica que eu utilizei? O laço while para inserir as presenças repete a query por aluno, ou seja, roda de 40 a 60 vezes executando uma query do MySql. Seria melhor eu unir essas presenças em um array e depois lançá-las com um query só na tabela de presenças?
Mais uma coisa: haveria como assegurar que, se o MySql for inserir os dados na tabela principal, tem que necessariamente inserir as presenças na outra tabela? Ou seja, ou insere tudo ou insere nada... Meu maior problema não tem sido a impossibilidade de realizar essas chamadas em sala, já que os professores têm seus logins e senha e podem lançá-las para o sistema em casa, mas sim os erros que muitas vezes acontecem nas tentativas do processo e deformam o registro como foi citado acima...
Bem, pessoal, eu espero que tenha ficado claro. Tô realmente engatinhando nessa área, e muitas vezes as soluções que tenho em mente, por mais que funcionem, não são as mais viáveis . Muito obrigado, e mais uma vez boa noite!
Ah, sim! Perdão pela inconveniência rsrs. A mágica acontece mais ou menos aqui:
$insere_chamada = mysqli_query($connect, "INSERT INTO chamadas (RP,data,hora,sala,aula) VALUES ('$user','$data','$hora','$sala','$aula')");
if($insere_chamada)
{
$lista_query = mysqli_query($connect, "SELECT * FROM $table");
$consulta_ID = mysqli_query($connect, "SELECT ID FROM chamadas WHERE data='$data' AND hora='$hora' AND RP='$user'");
$rowID = mysqli_fetch_array($consulta_ID);
while($row = mysqli_fetch_array($lista_query, MYSQL_NUM))
{
$p = $_POST["campo$row[1]"];
$insere_presenca = mysqli_query($connect, "INSERT INTO $table2 (ID,RA,presenca) VALUES ('$rowID[0]','$row[1]','$p')");
}
if($insere_presenca)
{
echo"<script language='javascript' type='text/javascript'>alert('A sua chamada foi salva com sucesso, tenha uma boa aula!');window.location.href='index.php';</script>";
}
else
{
echo"<script language='javascript' type='text/javascript'>alert('A chamada não pôde ser salva.');window.location.href='index.php';</script>";
}
}
else
{
echo"<script language='javascript' type='text/javascript'>alert('A chamada não pôde ser salva.');window.location.href='index.php';</script>";
}
O script inteiro tem 500 linhas rsrs, porque acabei colocando alguma forma de conseguir editar as chamadas já feitas, e ficou bem grande. Por isso, coloquei só uma parte, a que julgo mais importante.
Vou explicar (ou tentar):
em $insere_chamada, ele simplesmente coloca na tabela principal os dados que já vinham se arrastando anteriormente pelo script, como o RP (registro do professor), data, hora, sala e o número da aula (primeira, segunda, terceira...)
Depois, em $lista_query, ele consulta a tabela de alunos da sala em questão, que só possui a coluna RA.
Em $consulta_ID ele verifica se a chamada, que foi registrada com todos os dados de professor, data, hora.. foi realmente registrada na tabela principal.
E, por último, naquele laço while, o script sai pegando RA por RA da tabela de alunos da sala e registra a cada iteração a presença daquele aluno na tabela de presenças.
Bem... é basicamente isso, é justamente nessa etapa que tenho encontrado problemas, já que nas etapas de recolhimento dos dados, formulário, tudo funciona OK. A minha maior dúvida é que não sei se do jeito que tá estruturado assim seria "PROGRAMAVELMENTE SAUDÁVEL". Eu teria desempenho máximo desse jeito ou teria um modo menos burro de fazer? rsrsrs. E outra coisa, haveria como garantir a execução de todos os INSERTS ou de nenhum? Obrigado e boa noite Não entendi muito bem, mas vamos lá:
Você colocou uma listagem por checkbox com todos alunos para o professor marcar se o mesmo está presente na sala de aula?
Após o mesmo selecionar todos ele clica e o script percorre todos os dados enviados via POST, recebendo RM + presente(0-1)?
Caso tenha 300 alunos, ele vai ficar rodando até terminar os 300?
Me ajuda a entender, se for dessa forma posso te dá uma solução talvez e você implementa ao seu caso.
Isso mesmo, cara! Acontece EXATAMENTE isso que você disse. Perdão o nível de confusão que fiz ao tentar explicar. Esse código precisa ser atualizado, quando fiz meu conhecimento sobre a área era muito menor rsrs. Pra ter ideia, usei o $rowID pra pegar o ID da última inserção, ao invés de usar o comando feito pra isso . Valeu, cara!
>
3 horas atrás, FerrariR disse:
Isso mesmo, cara! Acontece EXATAMENTE isso que você disse. Perdão o nível de confusão que fiz ao tentar explicar. Esse código precisa ser atualizado, quando fiz meu conhecimento sobre a área era muito menor rsrs. Pra ter ideia, usei o $rowID pra pegar o ID da última inserção, ao invés de usar o comando feito pra isso . Valeu, cara!
Ao em vez de você fazer esse processo todo, você pode usar ajax.
Pensamos, cada chamada é feita em um dia (2017-04-08)
Quando o professor clicar em "PRESENTE / NÃO PRESENTE" ele vai enviar uma requisição ao servidor, mandando inserir ou atualizar caso já exista um registro naquele dia (caso o professor marcou errado).
Ou seja, o servidor não vai sobrecarregar, conseguindo exercer o que é para ser feito.
var dadosUsuario;
function tratarValores(num) {
var checkbox = document.getElementById("aluno-presenca" + num);
var aluno = document.getElementById("aluno" + num);
dadosUsuario = "presenca=" + encodeURIComponent(checkbox) + "&aluno=" + encodeURIComponent(aluno);
iniciarAjax("POST", "adicionar/presenca/", true, num);
}
function iniciarAjax(type, url, assinc, num) {
var ajax;
if (window.XMLHttpRequest)
{
ajax = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
ajax = new ActiveXObject("Msxml2.XMLHTTP");
if (!ajax)
{
ajax = new ActiveXObject("Microsoft.XMLHTTP");
}
}
if (ajax)
{
ajax.onreadystatechange = function() {
if (ajax.readyState == 4)
{
if (ajax.status == 200)
{
document.getElementById("response" + num).innerHTML = ajax.responseText;
}
}
}
ajax.open(type, url, assinc);
ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
ajax.send(dadosUsuario);
}
}
Não sou nenhum mestre de js. Mas acho que dá para se espelhar e adaptar ao que você quer, adicionando disciplina, turma etc.Quando eu trabalhei em uma instituição de ensino e realizamos o desenvolvimento do portal do professor para a questão das chamadas, utilizamos a lógica inversa para reduzir a quantidade de overhead gerado na transação. Vou tentar explicar.
Durante o levantamento de requisitos, localizamos algumas questões da "lógica" das chamadas:
-
Em geral, a maioria dos alunos estão presentes;
-
No caderno físico, professores não costumam marcar presenças, apenas faltas;
-
Alunos que não estão vinculados a turma, possuem um vínculo diferenciado;
-
Fórmula de cálculo de presença.
Esse último, é um pouco mais simples de se explicar com um exemplo (que, você, como professor, deve estar "careca" de saber). Basicamente, se são 20 aulas, cada aula corresponde a 5% de frequência. No término da primeira aula, o aluno que esteve presente possui 100% de frequência e o aluno que faltou possui 95% de frequência. No caso de "acúmulo de presença", eles possuiriam, respectivamente, 5% e 0% de frequência.
Logo, modelamos da seguinte forma (vou colocar de forma simplista, até, porque, essa modelagem já está próximo de completar uma década e eu não me lembro de 100%):
-
aluno: tabela correspondente a cada aluno;
-
turma: tabela correspondente a turma. Essa tabela possui:
-
professor;
-
data/horário (inclui-se qual dia da semana, qual horário de início/término, turno, etc...);
-
disciplina;
-
sala.
-
aluno_turma: tabela N:M vinculando os alunos a uma turma (cada aluno poderia ter mais de uma turma).
-
aula: indicava o dia e a sala que uma turma teve aula. Essa tabela possui:
-
turma;
-
sala (a aula poderia ocorrer em uma sala diferente da que a turma deveria estar alocada);
-
professor (o mesmo que a sala, em caso de um imprevisto);
-
data/horário (também poderia ocorrer em dia diferente, mas era raro, mas poderia...).
-
aluno_aula_falta: vinculava o aluno que faltou àquela disciplina.
Na hora de realizar os relatórios, vinculávamos os alunos que estavam na turma e as suas faltas. Como alunos que não eram da turma e participaram de alguma aula (horar complementares, monitoria, etc...) eles eram vinculados com outro finalidade, e não entravam no quesito de presença/falta.
É importante salientar que cada aula contemplava 4 presenças/faltas nesse caso (devido ao período de carga horária). Nesse caso, em uma turma de 40 alunos , caso 5 faltassem seriam inseridos 20 registros. No caso inverso, 35 teriam presença e seriam inseridos 140 registros.
Ferrari, você tem muitos problemas nesse seu sistema, vou me concentrar apenas onde suspeito que esta o "fogo"
Aparentemente o problema de lentidão esta no loop que faz o insert, então vamos trocar "N" inserts por apenas um
por exemplo:
-- Para que fazer três inserts com três chamadas ao banco:
INSERT INTO usuario(nome,senha) VALUES ('joao', '1234');
INSERT INTO usuario(nome,senha) VALUES ('jose', '1111');
INSERT INTO usuario(nome,senha) VALUES ('rafael', '1212);
-- Se eu posso fazer apenas uma:
INSERT INTO usuario(nome,senha) VALUES ('joao', '1234'), ('jose', '11111'), ('rafael', '1212');
# Arrumando o seu código, ficara assim:
while($result = mysqli_fetch_array($lista_query) )
{
$row[] = $result[1];
}
$values = '';
foreach ($row as $value) {
$values .= sprintf("('$rowID[0]', '%s', '$p'),", $value);
}
$values = substr($values, 0, -1) . ";";
$sql = "INSERT INTO $table2 (ID,RA,presenca) VALUES " . $values;
$insere_presenca = mysqli_query($connect, $sql); # Com apenas um insert!
Veja esse post do Beraldo, vai ajudar:
http://rberaldo.com.br/inserindo-multiplos-registros-em-tabela-de-banco-de-dados/
>
29 minutos atrás, jamesbond disse:
Ao em vez de você fazer esse processo todo, você pode usar ajax.
Pensamos, cada chamada é feita em um dia (2017-04-08)
Quando o professor clicar em "PRESENTE / NÃO PRESENTE" ele vai enviar uma requisição ao servidor, mandando inserir ou atualizar caso já exista um registro naquele dia (caso o professor marcou errado).
Ou seja, o servidor não vai sobrecarregar, conseguindo exercer o que é para ser feito.
Nossa, esse sistema me parece sensacional haha. Mas, ao mesmo tempo, um pouco complexo, já que não sei utilizar Ajax .
>
17 minutos atrás, Gabriel Heming disse:
Quando eu trabalhei em uma instituição de ensino e realizamos o desenvolvimento do portal do professor para a questão das chamadas, utilizamos a lógica inversa para reduzir a quantidade de overhead gerado na transação. Vou tentar explicar.
Durante o levantamento de requisitos, localizamos algumas questões da "lógica" das chamadas:
-
Em geral, a maioria dos alunos estão presentes;
-
No caderno físico, professores não costumam marcar presenças, apenas faltas;
-
Alunos que não estão vinculados a turma, possuem um vínculo diferenciado;
-
Fórmula de cálculo de presença.
Esse último, é um pouco mais simples de se explicar com um exemplo (que, você, como professor, deve estar "careca" de saber). Basicamente, se são 20 aulas, cada aula corresponde a 5% de frequência. No término da primeira aula, o aluno que esteve presente possui 100% de frequência e o aluno que faltou possui 95% de frequência. No caso de "acúmulo de presença", eles possuiriam, respectivamente, 5% e 0% de frequência.
Logo, modelamos da seguinte forma (vou colocar de forma simplista, até, porque, essa modelagem já está próximo de completar uma década e eu não me lembro de 100%):
-
aluno: tabela correspondente a cada aluno;
-
turma: tabela correspondente a turma. Essa tabela possui:
-
professor;
-
data/horário (inclui-se qual dia da semana, qual horário de início/término, turno, etc...);
-
disciplina;
-
sala.
-
aluno_turma: tabela N:M vinculando os alunos a uma turma (cada aluno poderia ter mais de uma turma).
-
aula: indicava o dia e a sala que uma turma teve aula. Essa tabela possui:
-
turma;
-
sala (a aula poderia ocorrer em uma sala diferente da que a turma deveria estar alocada);
-
professor (o mesmo que a sala, em caso de um imprevisto);
-
data/horário (também poderia ocorrer em dia diferente, mas era raro, mas poderia...).
-
aluno_aula_falta: vinculava o aluno que faltou àquela disciplina.
Na hora de realizar os relatórios, vinculávamos os alunos que estavam na turma e as suas faltas. Como alunos que não eram da turma e participaram de alguma aula (horar complementares, monitoria, etc...) eles eram vinculados com outro finalidade, e não entravam no quesito de presença/falta.
É importante salientar que cada aula contemplava 4 presenças/faltas nesse caso (devido ao período de carga horária). Nesse caso, em uma turma de 40 alunos , caso 5 faltassem seriam inseridos 20 registros. No caso inverso, 35 teriam presença e seriam inseridos 140 registros.
Gostei bastante da sua lógica do banco de dados. Muito mesmo. Imagino que aceleraria muito a velocidade de processamento, vou pensar sobre e tentar adaptar ao que já tenho.
Voltando ao problema de melhora de desempenho, galera, eu tentei substituir todas as QUERYS que estão dentro do laço WHILE por apenas uma que insere um ARRAY formado por todas as presenças. Agora, acontece que o tempo de processamento caiu da média de 1,5 segundo para 0,2! Fiquei muito feliz, não sabia que a repetição da QUERY poderia atrasar tanto o INSERT em massa. Agora, continuo com a seguinte dúvida: teria como assegurar que ou se insere nas duas tabelas ou se insere em nenhuma? Obrigado!
>
2 minutos atrás, FerrariR disse:
Nossa, esse sistema me parece sensacional haha. Mas, ao mesmo tempo, um pouco complexo, já que não sei utilizar Ajax .
Gostei bastante da sua lógica do banco de dados. Muito mesmo. Imagino que aceleraria muito a velocidade de processamento, vou pensar sobre e tentar adaptar ao que já tenho.
Voltando ao problema de melhora de desempenho, galera, eu tentei substituir todas as QUERYS que estão dentro do laço WHILE por apenas uma que insere um ARRAY formado por todas as presenças. Agora, acontece que o tempo de processamento caiu da média de 1,5 segundo para 0,2! Fiquei muito feliz, não sabia que a repetição da QUERY poderia atrasar tanto o INSERT em massa. Agora, continuo com a seguinte dúvida: teria como assegurar que ou se insere nas duas tabelas ou se insere em nenhuma? Obrigado!
Você só vai saber se está ocorrendo como espera caso analise seu banco de dados...
>
1 minuto atrás, EdCesar disse:
Ferrari, você tem muitos problemas nesse seu sistema, vou me concentrar apenas onde suspeito que esta o "fogo"
Aparentemente o problema de lentidão esta no loop que faz o insert, então vamos trocar "N" inserts por apenas um
por exemplo:
-- Para que fazer três inserts com três chamada ao banco:
INSERT INTO usuario(nome,senha) VALUES ('joao', '1234');
INSERT INTO usuario(nome,senha) VALUES ('jose', '1111');
INSERT INTO usuario(nome,senha) VALUES ('rafael', '1212);
-- Se eu posso fazer apenas uma:
INSERT INTO usuario(nome,senha) VALUES ('joao', '1234'), ('jose', '11111'), ('rafael', '1212');
# Arrumando o seu código, ficara assim:
while($result = mysqli_fetch_array($lista_query) )
{
$row[] = $result[1];
}
$values = '';
foreach ($row as $value) {
$values .= sprintf("('$rowID[0]', '%s', '$p'),", $value);
}
$values = substr($values, 0, -1) . ";";
$sql = "INSERT INTO $table2 (ID,RA,presenca) VALUES " . $values;
$insere_presenca = mysqli_query($connect, $sql); # Com apenas um insert!
Veja esse post do Beraldo, vai ajudar:
http://rberaldo.com.br/inserindo-multiplos-registros-em-tabela-de-banco-de-dados/
Caramba, cara, acabei de fazer justamente o que você sugeriu vendo um post no blog do Thiago Belém, e consegui inserir várias linhas com apenas um INSERT. O tempo de execução foi de 1,5 segundo pra 0,2, fiquei bastante feliz haha. Agora, fica a dúvida se dá pra impedir que os inserts sejam corrompidos... Valeu!
>
Agora, jamesbond disse:
Você só vai saber se está ocorrendo como espera caso analise seu banco de dados...
Entendi. Então terei que testar rsrs
Que bom que você já conseguiu implementar a solução!
>
19 minutos atrás, FerrariR disse:
Agora, fica a dúvida se dá pra impedir que os inserts sejam corrompidos... Valeu!
Sim, você teria que trabalhar com Mysql transaction
Quando você inicia um procedimento, você basicamente inicia uma sessão, e no final é feito um commit, ou seja, as alterações são salvas. Com transaction, você pode usar o rollback para cancelar tudo, caso algo de errado.
Esse é o conceito, da uma pesquisada no tema
Boa sorte!
No link abaixo há um exemplo em PDO, a lógica (e explicação) é a mesma:
Valeu, pessoal! Vocês ajudaram demaissss. Espero um dia ter metade do conhecimento que vocês têm para poder auxiliar aqui no fórum também. Abraços!
Boa noite, precisamos de códigos para lhe auxiliar.