Ir para conteúdo
arthursanno

MySQL pegar o primeiro registro sem 2 filhoa (parent)

Recommended Posts

Possuo a seguinte tabela

CREATE TABLE IF NOT EXISTS `pool` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `idPai` int(11) NOT NULL,
  `nome` varchar(100) NOT NULL,
  PRIMARY KEY (`idPool`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;

INSERT INTO `pool` (`id`, `idPai`, `nome`) VALUES
(1, 0, 'José'),
(2, 1, 'Maria'),
(3, 1, 'Carlos'),
(4, 2, 'Renta'),
(5, 2, 'Pedro'),
(6, 3, 'Gustavo');

No caso desse diagrama:

1
:: 2
:::: 4
:::: 5
:: 3
:::: 6

Quero fazer uma consulta SQL nessa tabela cujo me retorne o primeiro registros que NÃO possui 2 filhos. (no caso do diagrama seria o registro 3... e ao acrescentar mais um filho de 3, a consulta ia retornar o registro 4 agora)

 

Não sei se tenho que fazer consultas dentro de consultas. Preciso de orientação pra continuar o caminho do estudo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá!

 

Caso você precise consultar quando não possui nenhum filho, no caso os IDS 4, 5 e 6, você poderia fazer assim:

SELECT id
FROM pool p1
WHERE NOT EXISTS (
  SELECT idPai FROM pool p2 WHERE p1.id = p2.idPai
)

Indo nessa lógica, para restringirmos para os registros que não possuem 2 filhos para casar com o ID 3, poderíamos fazer assim:

SELECT id
FROM pool p1
WHERE (
  SELECT COUNT(1) FROM pool p2 WHERE p1.id = p2.idPai
) <= 1
LIMIT 1

 

Mas eu não acho que resolver esse tipo de questão diretamente no banco seja a melhor solução. Depende muito do que você precisa fazer.

 

Outro detalhe é que do jeito que seu banco está estruturado nesse momento, problemas de performance futuros seriam inevitáveis. Para contornar isso você deve criar um índice/chave na coluna idPai. Há um exemplo de SQL aqui:

https://forum.imasters.com.br/topic/562468-resolvido-estrutura-de-um-projeto-de-marketing-multinível-com-php-e-mysql/

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá Matheus. Obrigado por me responder.

 

Eu havia construido essa consulta, verifique:

SELECT *
FROM pool a
WHERE a.id NOT IN (select idPai from pool GROUP BY idPai ORDER BY idPai ASC)
OR a.id IN (SELECT idPai
				FROM pool
				WHERE id <> 1
				GROUP BY idPai
				HAVING COUNT(*) < 2);

O resultado seria a lista dos pais sem filhos e do pai com 1 filho.

Mas como a inserção é automática, ou seja, ele já faz a inserção no primeiro pai livre, então mudei para.

SELECT *
FROM pool a
WHERE a.id > (select idPai from pool ORDER BY idPai DESC LIMIT 1)
OR a.id IN (SELECT idPai
				FROM pool
				WHERE id <> 1
				GROUP BY idPai
				HAVING COUNT(*) < 2)
ORDER BY id ASC LIMIT 1;

Me sugere algo?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pelo que entendi você está solicitando ao banco os pais com menos de 2 filhos (através da sua segunda subquery) e caso não existam, o último pai criado no sistema (sua primeira subquery), é isso?

Dessa forma você teoricamente teria o pai apropriado para a inserção do próximo filho na árvore.

 

Mas você ainda não está livre de cair na situação de todos os pais terem 2 filhos, inclusive o último pai (sua primeira subquery). Caso sua intenção seja de não permitir mais que 2 filhos, aqui haveria uma exceção, que provavelmente seria criar um pai virtual, por exemplo.

 

Além disso, não tenho certeza, mas acredito que dessa forma sua consulta seria mais performática:

SELECT *
FROM pool p1
WHERE
  (SELECT COUNT(1) FROM pool p2 WHERE p1.id = p2.idPai) <= 1
  OR
  id > (select idPai from pool ORDER BY idPai DESC LIMIT 1)

ORDER BY id ASC
LIMIT 1

 

Outra coisa que poderia ser feita é manter em cache a lista dos pais que ainda não possuem 2 filhos. Imagine essa sua segunda SUBQUERY:

SELECT idPai
FROM pool
WHERE id <> 1
GROUP BY idPai
HAVING COUNT(*) < 2

Basicamente ela faz isso: para todos os pais que não possuam ID 1, agrupe-os e remova os que possuirem 2 filhos ou mais.

Perceba que essa consulta será repetida múltiplas vezes, sempre que você precisar consultar o banco.

 

Sua consulta ficaria assim, se estes registros estivessem em cache:

SELECT *
FROM pool a
WHERE a.id > (select idPai from pool ORDER BY idPai DESC LIMIT 1)
OR a.id IN ( 3 )
ORDER BY id ASC LIMIT 1;

Eu apenas copiei sua consulta e substitui a segunda SUBQUERY pelo id 3, que é o resultado da consulta quando executada.

 

Isso você poderia fazer com MATERIALIZED VIEW ou cache a nível de aplicação (APCu por exemplo, se estivermos falando de PHP).

 

De resto, acho que está tudo ok, mas iria sugerir de você popular melhor sua tabela para testar melhor sua consulta. Algo como uns 20 a 30 registros, pelo menos. Isso iria prover uma maior confiabilidade no desenvolvimento.

Compartilhar este post


Link para o post
Compartilhar em outros sites
Citar

Pelo que entendi você está solicitando ao banco os pais com menos de 2 filhos (através da sua segunda subquery) e caso não existam, o último pai criado no sistema (sua primeira subquery), é isso?

Dessa forma você teoricamente teria o pai apropriado para a inserção do próximo filho na árvore.

A inserção do novo registros será sempre no primeiro pai disponível.

 

Ex:

INSERT INTO `pool` (`id`, `idPai`, `nome`) VALUES
(1, 0, 'José'),
(2, 1, 'Maria'),
(3, 1, 'Carlos'),
(4, 2, 'Renta'),
(5, 2, 'Pedro'),
(6, 3, 'Gustavo');

Nesse caso o primeiro pai disponível é o 3 (Carlos), porque ele não possui 2 filhos (somente 1). Ao acrescentar mais um filho nele, o primeiro pai disponível é o 4 (Renata), cujo não possui nenhum filho, ao acrescentar mais um filho, Renata ainda será o primeiro pai disponível pois ainda não tem 2 filhos...

INSERT INTO `pool` (`id`, `idPai`, `nome`) VALUES
(1, 0, 'José'),
(2, 1, 'Maria'),
(3, 1, 'Carlos'),
(4, 2, 'Renta'),
(5, 2, 'Pedro'),
(6, 3, 'Gustavo'),
// Acrescentando novo filho em 3 pq ele só tem 1 filho é o primeiro pai disponível
(7, 3, 'Igor'),
// Acrescentando novo filho agora em 4, pq 3 já possui 3 filhos e 4 nenhum filho 
(8, 4, 'Jerry'),
// Acrescentando novo filho, ainda em 4 pq 4 não possui 2 filhos
(9, 4, 'Bárbara'),
// Acrescentando novo filho, agora em 5 pq 4 já possui 2 filhos
(10, 5, 'Rebeca');
...

 

 

Nessa query que montei, ele me retorna sempre o PRIMEIRO PAI DISPONÍVEL

SELECT *
FROM pool a
WHERE a.id > (select idPai from pool ORDER BY idPai DESC LIMIT 1)
OR a.id IN (SELECT idPai
				FROM pool
				WHERE id <> 1
				GROUP BY idPai
				HAVING COUNT(*) < 2)
ORDER BY id ASC LIMIT 1;

Uso esse para pegar o ID do primeiro pai livre e adiciono automaticamente o novo registro nesse pai.

 

http://www.sqlfiddle.com/#!9/67fdf2/1

 

 

Sim, estarei usando PHP.

Compartilhar este post


Link para o post
Compartilhar em outros sites
37 minutos atrás, arthursanno disse:

Nessa query que montei, ele me retorna sempre o PRIMEIRO PAI DISPONÍVEL 

Aí que tá. Na primeira query que postei é exatamente isso que é feito também.

 

Então, ao menos que essa query não lhe atenda por alguma razão ou esteja funcionando incorretamente, acredito que ela seja a melhor opção para você, levando em consideração que possui menos código e performa um pouco melhor (no seu fiddle dá para notar a diferença em ms na execução):

SELECT *
FROM pool p1
WHERE (
  SELECT COUNT(1) FROM pool p2 WHERE p1.id = p2.idPai
) <= 1
LIMIT 1

Aqui retorna: #5 | 2 | "Pedro", assim como as demais consultas.

 

 

O mesmo serve para a consulta que você elaborou. Ela é uma boa opção se retirarmos a primeira condição do WHERE, que eu acredito que esteja ali sem necessidade.

Nesse caso teríamos isso, que é muito parecido com o que eu fiz, mas utilizando o IN/GROUP/HAVING como alternativas ao depenent subquery:

SELECT *
FROM pool a
WHERE a.id IN (
    SELECT idPai
    FROM pool
    WHERE id <> 1
    GROUP BY idPai
    HAVING COUNT(*) < 2)
ORDER BY id ASC LIMIT 1;

Essa consulta gera o mesmo resultado também. Tudo certo.

 

Entende onde eu quero chegar? Ao menos que eu não tenha entendido alguma coisa, acredito que essas consultas fazem o que você precisa, mas esse pequeno trecho é desnecessário e pode ser removido. Estou me referindo a isso aqui:

[...] a.id > (select idPai from pool ORDER BY idPai DESC LIMIT 1) OR [...]

 

Mas que bom que está resolvido. Você mesmo solucionou a questão :)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora

  • Conteúdo Similar

    • Por douglas79
      Bom dia,

      Há alguns dias que venho instalar o apache, o php, mysql e o phpmyadmin manualmente e sem obter sucesso. Até consegui rodar o php, porém, quando vou baixar a úitima versão do MYSQL, não tem todos os pacotes nele instalados, inclusive no completo, só encontro o Router.
      Alguém pode me dizer o porquê que isso está ocorrendo?
      Desde já agradeço a ajuda de vocês, que será bem vinda!
      No aguardo!

      Uso a versão 8.3.9 do PHP
      Meu SO é o Windows 10 32 bits
    • Por landerbadi
      Tenho uma tabela chamada "item" com os seguintes campos: id, name, active. Nela tem cadastrado vários itens. No campo "active" eu coloco a letra "S" para informar que este item está ativo no sistema. Por exemplo: 1, casa, S 2, mesa, S 3, cama, S 4, moto S 5, rádio O quinto registro "radio" não está ativo no sistema pois não tem um "S" no campo active. E outra tabela chamada "product" com os seguintes campos (id, name) com os seguintes registros: 1, Produto A 2, Produto B 3, Produto C E uma terceira tabela chamada "product_item" com os seguintes campos (productID, itemID). No campo productID eu coloco o id de um produto da tabela "product" e no campo "itemID" eu coloco o id do produto da tabela "item". Exemplo: 1, 1 1, 3 1, 4 2, 3 2, 4 Sendo assim o produto A da tabela 'product" comtem os itens casa, cama e moto. Eu preciso fazer uma busca da seguinte maneira:  Eu escolho um registro da tabela "item", por exemplo "casa". Preciso fazer com que o php me liste todos os registros da tabela "product" que contenham a palavra "casa" e que os demais itens estejam ativos no siste. Ou seja, que contenham um "S" no campo "active"  Eu consegui fazer isso da seguinte maneira: SELECT P.id, P.name, GROUP_CONCAT(I.name ORDER BY I.name) AS items FROM product P JOIN product_item PI ON P.id = PI.productID JOIN item I ON I.id = PI.itemID AND I.active = 'S' WHERE P.id NOT IN ( SELECT PI.productID FROM product_item PI JOIN item I ON I.id = PI.itemID WHERE I.active IS NULL ) AND P.id IN ( SELECT PI.productID FROM product_item PI JOIN item I ON I.id = PI.itemID WHERE I.name = 'mesa' ) GROUP BY P.id, P.name; O problema que eu estou tendo é o seguinte:
      Quando eu jogo este código para o banco de dados onde eu já tenho os registros cadastrado o php fica lendo uma eternidade e não lista os produtos.
       
      Usando código no banco de dados que eu fiz para testes ele funciona perfeitamente pois nele tem poucos registros.
       
      No banco de dados principal a tabela "item" tem 11.196 registros. A tabela "product" tem 88.214 registros e a tabela "product_item" tem 518.378 registros. 
       
      Eu acredito que, devido o banco de dados ser muito grande, ele não consegue listar.
       
      Alguém sabe de algum meio de resolver isso?
       
       
    • Por landerbadi
      Boa tarde pessoal. Estou tentado fazer uma consulta no banco de dados porém estou tendo dificuldades. Tenho uma tabela chamada "itens" com os seguintes campos: id, item, ativo. Nela tem cadastrado vários itens. No campo ativo eu coloco a letra "S" para informar que este item está ativo no sistema. Por exemplo: 1, casa, S 2, mesa, S 3, cama, S 4, moto S 5, rádio O quinto registro "radio" não está ativo no sistema pois não tem um "S" no campo ativo. E outra tabela chamada "produtos" com os seguintes campos (id, item1, item2, item3) com os seguintes registros: 1, casa, mesa, moto 2, mesa, casa, cama 3, rádio, cama, mesa Eu preciso fazer uma busca na tabela produtos da seguinte maneira: Eu escolho um registro na tabela "itens", por exemplo "mesa". Preciso fazer com que o php me liste todos os registros da tabela "produtos" que contenham a palavra "mesa". Até aqui tudo bem eu consigo listar. Estou fazendo assim: <?php $item = "mesa" $sql = mysqli_query($conn, "SELECT * FROM produtos WHERE item1 LIKE '$item' OR item2 LIKE '$item' OR item3 LIKE '$item' LIMIT 10"); while($aux = mysqli_fetch_assoc($sql)) { $id = $aux["id"]; $item1 = $aux["item1"]; $item2 = $aux["item2"]; $item3 = $aux["item3"]; echo $id . " - " . $item1 . ", " . $item2 . ", " $item3 . "<br>"; } ?> O problema é que está listando todos os registros que contém o item mesa. Eu preciso que o php verifique os demais item e me liste somente os registro em que todos os registros estejam ativos no sistema. No exemplo acima ele não deveria listar o registro 3. pois nesse registro contém o item "radio" e este item não está ativo no sistema. Ou seja, o registro "radio" na tabela itens não possui um "S" na coluna "ativo". Alguém sabe como resolver isso?
    • Por First
      Olá a todos!
       
      Eu estou criando um sistema do zero mas estou encontnrando algumas dificuldades e não estou sabendo resolver, então vim recorrer ajuda de vocês.
      Aqui está todo o meu código: https://github.com/PauloJagata/aprendizado/
       
      Eu fiz um sistema de rotas mas só mostra o conteúdo da '/' não sei porque, quando eu tento acessar o register nada muda.
      E eu também quero que se não estiver liberado na rota mostra o erro de 404, mas quando eu tento acessar um link inválido, nada acontece.
      Alguém pode me ajudar com isso? E se tiver algumas sugestão para melhoria do código também estou aceitando.
       
       
      Desde já, obrigado.
    • Por landerbadi
      Olá pessoal, boa tarde
       
      Tenho uma tabela chamada "produtos" com os seguintes campos (id, produto) e outra tabela chamada "itens" com os seguintes campos (id, prod_01, prod_02, prod_03, prod_04).
       
      Na tabela produtos eu tenho cadastrado os seguintes produtos: laranja, maçã, uva, goiaba, arroz, feijão, macarrão, etc.
       
      Na tabela itens eu tenho cadastrado os itens da seguinte maneira:
       
      1, laranja, uva, arroz, feijão;
      2, maçã, macarrão, goiaba, uva;
      3, arroz, feijão, maçã, azeite
       
      Meu problema é o seguinte: 
      Eu escolho um produto da tabela "produtos", por exemplo "uva".  Preciso fazer uma consulta na tabela "itens" para ser listado todos os registros que contenham o produto "uva" e que todos os demais produtos estejam cadastrados na tabela "produtos".
       
      No exemplo acima seria listado apenas dois registros, pois o terceiro registro não contém o produto "uva". 
       
      Alguém pode me ajudar? Pois estou quebrando a cabeça a vários dias e não consigo achar uma solução.
×

Informação importante

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