Phe 1 Denunciar post Postado Janeiro 21, 2013 Tenho um problema e não tenho a menor ideia de como resolver. São duas tabelas: uma chamada miniaturas e outras chamada categorias. Da tabela miniaturas quero exibir os seguintes campos: Descrição(na tabela o campo é nome), categoria, e o preço (sendo que o preço deve ser retornado com o maior valor); E da tabela categorias, devo retornar o nome da categoria. É um sistema de miniaturas de automóveis. Então o nome da categoria seria automóvel, avião, motocicleta, enfim. O código ficou assim: <?php include "conexao.inc"; //a tabela miniaturas fazendo ligação com a categorias, estão relacionadas $sql = "SELECT id_categoria, nome, MAX(preco) AS maiorPreco, categorias.cat_nome"; $sql = $sql . " FROM miniaturas"; $sql = $sql . " INNER JOIN categorias"; $sql = $sql . " ON miniaturas.id_categoria = categorias.id"; $sql = $sql . " GROUP By cat_nome"; $sql = $sql . " ORDER BY preco desc"; $rs = mysql_query($sql) or die(mysql_error()); $total_registros = mysql_num_rows($rs); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <title>Documento sem título</title> <style type="text/css"> body { font: 80% verdana; } table { font: 90% verdana; border-collapse: collapse; border: 1px solid #000; margin: 0 auto; } th, td { padding: 3px; border: 1px solid #000; } th { padding: 3px; background: #ccc; } </style> </head> <body> <p> Sentença sql para este exercício: <strong><?php echo $sql; ?></strong><br /> Total de registros retornados pela consulta: <strong><?php echo $total_registros; ?></strong> </p> <table> <thead> <tr> <th>Descrição</th> <th>Categoria</th> <th>Nome Categoria</th> <th>Miniatura com Maior Preço</th> </tr> </thead> <?php while ($reg = mysql_fetch_array($rs)) { //exibindo as informações dos campos $descricao = $reg['nome']; $id_categoria = $reg['id_categoria']; $categoria = $reg['cat_nome']; $maiorPreco = $reg['maiorPreco']; ?> <tr> <td><?php echo $descricao; ?></td> <td><?php echo $id_categoria; ?></td> <td><?php echo $categoria; ?></td> <td><?php echo $maiorPreco; ?></td> </tr> <?php } ?> </table> </body> </html> <?php mysql_free_result($rs); mysql_close($conexao); ?> O resultado foi exibido da seguinte forma: Descrição Categoria Nome Categoria Miniatura com Maior Preço Embraer ERJ 135 6 Aviões 312 American LaFrance Fire Pumper 4 Caminhões 169 Marcopolo Paradiso 1200 1 Ônibus 248 Volvo S40 2 Automóveis 310 JDH 5 Motocicletas 85 Retroescavadeira 416E 3 Máquinas pesadas 295 Dodge Military Power Wagon 7 Militares 110 Parece que deu certo. Mas resolvi verificar se a descrição (que na tabela se chama nome) batia com o valor. É para retornar o maior preço da miniatura de cada categoria. Se eu tirar a coluna descrição, fica tudo certo. Só que eu quero que seja exibido a descrição. Embraer ERJ 135 não é o mais caro da categoria Aviões. A consulta retornou a primeira descrição da categoria e não a descrição correta que seria um Boeing 747-Cargo, como mostra a tabela abaixo usando como exemplo a categoria Aviões: Descrição Categoria Preço Boeing 747-Cargo 6 312 Nesta tabela, não é exibido o nome da categoria. O que esta errado na consulta SQL. Eu realmente não sei porque não retornou a descrição correta da categoria. Acabei de ver que o preço deveria esta em ordem descendente. Na minha concepção, depois do 312 deveria vir 310. Não é isso. Resumindo, é pego o primeiro nome da categoria da tabela miniaturas, mas o maior preço da categoria. Não entendi o porque disso e nem como resolver. Alguém pode ajudar a entender? Compartilhar este post Link para o post Compartilhar em outros sites
Massaki 47 Denunciar post Postado Janeiro 21, 2013 Fiz uma pequena modificação. Veja se resolve: $sql = "SELECT miniaturas.id_categoria, miniaturas.nome, MAX(miniaturas.preco) AS maiorPreco, categorias.cat_nome"; $sql .= " FROM miniaturas"; $sql .= " INNER JOIN categorias"; $sql .= " ON miniaturas.id_categoria = categorias.id"; $sql .= " GROUP By categorias.cat_nome"; $sql .= " ORDER BY maiorPreco DESC"; Compartilhar este post Link para o post Compartilhar em outros sites
Phe 1 Denunciar post Postado Janeiro 22, 2013 A ordenação ficou correta, agradeço. Mas a descrição ainda não. É retornado a primeira descrição da categoria consultada. Ou seja, o valor do maior preço não corresponde com a descrição. Olá colegas programadores. Vi que este post teve visitas. Mais uma vez eu pergunto: Alguém saberia o porque deste erro? Não continuei tentando porque não sei o que fazer. Logicamente não consigo entender. Não programo em php a anos mas se houver uns destes fico grato. Obrigado. Compartilhar este post Link para o post Compartilhar em outros sites
Matheus Tavares 167 Denunciar post Postado Janeiro 23, 2013 Olá amigo. Poste suas colunas com os registros de exemplo, por favor. []'s Compartilhar este post Link para o post Compartilhar em outros sites
Phe 1 Denunciar post Postado Janeiro 27, 2013 Matheus. Em primeiro lugar agradeço por responder e se dispor a ajudar. E peço desculpas por responder agora pois, estava com problemas na internet e servidor de hospedagem. Essa é a imagem da tabela de miniaturas em uma consulta geral: O retângulo vermelho é para mostrar que o avião mais caro da categoria avião é este Boeing 747-Cargo - categoria 6 aviões - no valor de R$ 312,00 Essa é a consulta geral. Na consulta específica que tem que trazer a miniatura mais cara de cada categoria mostra os seguintes resultados: Novamente, no retângulo vermelho mostra a miniatura mais cara da categoria aviões mas, a descrição não esta correta. Como disse anteriormente, a consulta retorna a primeira descrição da categoria. Isso eu não entendo. Por isso não sei como resolver. Essa é a imagem da tabela miniaturas com seus respectivos campos no mysql: E essa é a tabela categorias: Novamente, este é o código para a consulta específica: <?php include "conexao.inc"; $sql = "SELECT miniaturas.id_categoria, miniaturas.nome, MAX(miniaturas.preco) AS maiorPreco, categorias.cat_nome"; $sql .= " FROM miniaturas"; $sql .= " INNER JOIN categorias"; $sql .= " ON miniaturas.id_categoria = categorias.id"; $sql .= " GROUP By categorias.cat_nome"; $sql .= " ORDER BY maiorPreco DESC"; $rs = mysql_query($sql) or die(mysql_error()); $total_registros = mysql_num_rows($rs); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <title>Documento sem título</title> <style type="text/css"> body { font: 80% verdana; } table { font: 90% verdana; border-collapse: collapse; border: 1px solid #000; margin: 0 auto; } th, td { padding: 3px; border: 1px solid #000; } th { padding: 3px; background: #ccc; } </style> </head> <body> <p> Sentença sql para este exercício: <strong><?php echo $sql; ?></strong><br /> Total de registros retornados pela consulta: <strong><?php echo $total_registros; ?></strong> </p> <table> <thead> <tr> <th>Descrição</th> <th>Categoria</th> <th>Nome Categoria</th> <th>Miniatura com Maior Preço</th> </tr> </thead> <?php while ($reg = mysql_fetch_array($rs)) { $descricao = $reg['nome']; $id_categoria = $reg['id_categoria']; $categoria = $reg['cat_nome']; $maiorPreco = $reg['maiorPreco']; ?> <tr> <td><?php echo $descricao; ?></td> <td><?php echo $id_categoria; ?></td> <td><?php echo $categoria; ?></td> <td><?php echo $maiorPreco; ?></td> </tr> <?php } ?> </table> </body> </html> <?php mysql_free_result($rs); mysql_close($conexao); ?> Se você ou alguém mais experiente do que eu na linguagem e na área puder ajudar e explicar a lógica por detrás disso, fico agradecido. Compartilhar este post Link para o post Compartilhar em outros sites
Matheus Tavares 167 Denunciar post Postado Janeiro 27, 2013 Ah sim, entendi o que você precisa e creio ter a sua solução, mas peço que você exporte seu db e cole aqui para que eu possa importar e fazer alguns testes. []'s Compartilhar este post Link para o post Compartilhar em outros sites
Phe 1 Denunciar post Postado Janeiro 28, 2013 Mais uma vez obrigado Matheus por se dispor a ajudar. Esse é o link para o banco de dados. O nome do banco é dp_php5. http://www.fernandopl.webatu.com/Database.zip Ah. Peço desculpas a todos. Eu agradeci ao Matheus mas se alguém mais tiver alguma ideia e souber resolver ou dar uma luz ficarei grato. Estou tentando solucionar isso a mais de duas semanas. Embora só tenha postado na semana passada. Ou seja, desespero. Exportado e atualizado. Último link http://www.fernandopl.webatu.com/ Compartilhar este post Link para o post Compartilhar em outros sites
Evandro Oliveira 331 Denunciar post Postado Janeiro 28, 2013 Você precisa relacionar as colunas - que não fazem parte da mesma tabela - numa subconsulta select * from (select miniaturas.nome, miniaturas.preco, categorias.cat_nome as categoria from miniaturas inner join categorias on categorias.id = miniaturas.id_categoria order by preco desc ) as temp group by temp.categoria order by temp.preco desc; A propósito, muito bom o seu empenho em fornecer os dados necessários para reproduzir o problema. Seria muito mais fácil ajudar as pessoas se todos agissem assim. Compartilhar este post Link para o post Compartilhar em outros sites
Phe 1 Denunciar post Postado Janeiro 28, 2013 Evandro, muito obrigado pela a sua ajuda. Adaptei o código para o php e só inserir mais uma coluna. $sql = "SELECT * FROM ("; $sql .= " SELECT miniaturas.nome, miniaturas.id_categoria,"; $sql .= " miniaturas.preco, categorias.cat_nome AS categorias"; $sql .= " FROM miniaturas"; $sql .= " INNER JOIN categorias ON categorias.id = miniaturas.id_categoria"; $sql .= " ORDER BY preco desc"; $sql .= " ) AS TEMP"; $sql .= " GROUP BY temp.categorias"; $sql .= " ORDER BY temp.preco desc"; A coluna miniaturas.id_categorias. Confesso que por não ver na consulta o argumento MAX, achei que tivesse esquecido. Pois bem, deu tudo certo e fico muito feliz e grato. Afinal, quando um código dá certo é para comemorar. Depois de duas semanas. Bom, ainda preciso entender algumas coisas e uma delas é: Por quê você não usou o MAX? Eu tentei usar e não deu certo. Não consigo entender a lógica. 1º - Comecemos pelo primeiro código usando o MAX nos exemplos acima. Pelo pouco de entendo de array - e é bem pouco - já deveria trazer todas as informações. Se a consulta acha o maior preço, a categoria, como e por quê e pego a descrição incorreta se tudo esta na mesma linha. Entendeu a dúvida. Por quê o mysql pega a primeira descrição da categoria mas retorna o id da categoria e o preço correto? 2º - Por quê na consulta que você fez não foi necessário usar o argumento MAX. Como que o mysql sabe o maior preço de acordo com os parâmetros que você passou? 3º - Aproveitando o embalo, consulta com sub-consulta. Nem sabia que era possível. O que significa o AS TEMP? Mais uma vez obrigado. Compartilhar este post Link para o post Compartilhar em outros sites
Evandro Oliveira 331 Denunciar post Postado Janeiro 29, 2013 É necessário entender alguns conceitos. Infelizmente, não encontrei nenhuma literatura decente para referenciar. Vou ter que explicar com base em minha própria vivência/experiência. A explicação, também, sai do escopo deste fórum. Portanto, pode ser que algum moderador "quebre" este tópico em duas partes e encaixe o conteúdo que achar apropriado na sessão de bancos de dados. resultset é o conjunto de dados de uma consulta que retorna saída (select, show, explain, describe, ...) operação vertical ou referência vertical indica que estamos falando das colunas de um resultset, não necessariamente da tabela. operação horizontal ou referência horizontal, como você já deve ter deduzido, remete às linhas do resultset. Todas as funções de agrupamento fazem referência vertical. Isso significa que elas simplesmente ignoram as outras colunas. Tendo [inline]select nome, preco from miniaturas[/inline], ao contrário do que seria óbvio deduzir, utilizar MAX não retorna a linha cujo preço seja o maior. Faça o teste: [inline]select nome, MAX(preco) from miniaturas[/inline]. Veja que o nome permanece inalterado. Por este motivo, descartamos o uso de MAX. Não nos é útil neste cenário. Outra confusão é assumir que GROUP BY faça o agrupamento após a ordenação. GROUP BY vai pegar a primeira referência horizontal, aplicar as funções de agrupamento informadas na consulta e descartar o restante. Depois disso é feita a ordenação. veja que [inline]select nome, preco from miniaturas group by id_categoria[/inline] e [inline]select nome, preco from miniaturas group by id_categoria order by preco[/inline] são o mesmo resultset, ordenado conforme os nossos critérios. Dessa forma, precisamos colocar a referência horizontal de maior preço como primeira linha antes da ordenação. A melhor forma de se fazer isso, no seu caso, é criando uma view create view maior_preco as select miniaturas.nome as descricao, miniaturas.id_categoria, miniaturas.preco, categorias.cat_nome as categorias from miniaturas inner join categorias on categorias.id = miniaturas.id_categoria order by preco desc A consulta ficaria ligeiramente reduzida: [inline]select * from maior_preco group by id_categoria[/inline] A segunda forma, que foi passada no post anterior, que é um pouco menos otimizada/performática, consiste em utilizar subconsultas. É como se eu criasse uma view que é instantaneamente descartada. AS TEMP. A palavra-chave [inline]AS[/inline] indica um alias ou apelido. Sua principal utilidade é evitar colisões de nomes. create table cidade( id smallint (2) unsigned not null primary key auto_increment, nome varchar (20) not null unique ); insert into cidade values (null, 'São Paulo'), (null, 'Brasília'); create table pessoa( id smallint (2) unsigned not null primary key auto_increment, nome varchar (30) not null unique ); insert into pessoa values (null, 'José'), (null, 'João'), (null, 'Maria'); create table morador( id tinyint (4) unsigned not null primary key auto_increment, pessoa smallint (2) unsigned not null unique, cidade smallint (2) unsigned not null, foreign key morador(pessoa) references pessoa(id), foreign key cidade(cidade) references cidade(id) ); insert into morador values (null, 1, 1), (null, 2, 1), (null, 3, 2); Veja que tanto cidade quanto pessoa possuem uma coluna cujo nome é nome. Uma forma de diferenciá-los quando ambos estiverem num mesmo resultset é utilizando apelidos. select pessoa.nome, cidade.nome as cidade from morador inner join pessoa on pessoa.id = morador.pessoa inner join cidade on cidade.id = morador.cidade; Outro uso comum é quando temos muitos join, ou critérios de join, para diminuir o tamanho das consultas. Definimos como apelido a primeira letra ou um pedaço do nome da tabela select p.nome, c.nome as cidade from morador as m inner join pessoa as p on p.id = m.pessoa inner join cidade as c on c.id = m.cidade; É uma prática que eu não aconselho. As consultas ficam menos verbosas e difíceis de depurar. Outro caso é quando elas são obrigatórias. Uma subconsulta precisa de um nome. Uma view precisa de uma consulta. Tanto o nome da subconsulta quanto a consulta da view são indicadas pelo uso de AS. A opção por [inline]AS TEMP[/inline] foi apenas para indicar que eu estava selecionando os campos de um resultset temporário. Compartilhar este post Link para o post Compartilhar em outros sites
Henrique Barcelos 290 Denunciar post Postado Janeiro 29, 2013 Ao que parece, o problema não é com o PHP. Movendo para área correta. Compartilhar este post Link para o post Compartilhar em outros sites
Phe 1 Denunciar post Postado Janeiro 29, 2013 Evandro, mais uma vez obrigado pela ajuda e por ter respondido. Mais uma vez, gratidão. Vou estudar mais isso. Se eu falar - digitar - que entendi estarei mentindo. Parei de estudar mysql e estou tendo que retomar novamente. Enfim... Mas, o que você passou já dá para fazer algumas buscas. Agora pelo menos tenho outros parâmetros. Esta dúvida, é de um livro que comprei chamado "Faça um site orientado por projeto com php e mysql - comércio eletrônico". O exercício só pedia para exibir a categoria. Foi minha iniciativa incrementar. Ainda bem que fiz isso senão não teria essas dúvidas. Quanto ao fato de "não parecer se um problema com PHP", analisando melhor, é verdade. Mas não estava manipulando o banco diretamente, por isso não postei em banco de dados. Peço desculpas. Compartilhar este post Link para o post Compartilhar em outros sites
Motta 645 Denunciar post Postado Janeiro 30, 2013 Maiores preços por "catiguria" SELECT m1.* FROM miniaturas M1 WHERE M1.PRECO = (SELECT MAX(M2.PRECO) WHERE M2.ID_CATEGORIA = M1.CATEGORIA) Compartilhar este post Link para o post Compartilhar em outros sites