Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Phe

Campo descrição não corresponde ao Maior Preço

Recommended Posts

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

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

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. 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:

 

consultaGeral.png

 

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:

 

consultaEspecifica.png

 

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:

 

tb_miniaturas.png

 

E essa é a tabela categorias:

 

tb_categorias.png

 

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

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

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

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

É 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

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

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

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.