Ir para conteúdo
landerbadi

Código pho não executa em banco de dados grande

Recommended Posts

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?

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Reescreva sua consulta para minimizar o número de operações e junções complexas. Vou fornecer uma versão otimizada da sua consulta:

 

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
WHERE 
    P.id IN (
        SELECT PI.productID
        FROM product_item PI
        JOIN item I ON I.id = PI.itemID
        WHERE I.name = 'casa'
    )
    AND P.id NOT IN (
        SELECT PI.productID
        FROM product_item PI
        JOIN item I ON I.id = PI.itemID
        WHERE I.active IS NULL OR I.active != 'S'
    )
GROUP BY 
    P.id, P.name;

utilização de subconsultas pode ser custosa, especialmente em grandes bancos de dados. Tente minimizar subconsultas complexas. Vou tentar reescrever a consulta para evitar subconsultas:

 

SELECT 
    P.id, 
    P.name, 
    GROUP_CONCAT(I.name ORDER BY I.name) AS items
FROM 
    product P
JOIN 
    product_item PI1 ON P.id = PI1.productID
JOIN 
    item I1 ON I1.id = PI1.itemID
WHERE 
    I1.name = 'casa'
    AND NOT EXISTS (
        SELECT 1
        FROM product_item PI2
        JOIN item I2 ON I2.id = PI2.itemID
        WHERE 
            PI2.productID = P.id
            AND (I2.active IS NULL OR I2.active != 'S')
    )
GROUP BY 
    P.id, P.name;

Execute comandos de análise de índices para garantir que o banco de dados está utilizando os índices de forma eficiente:

 

ANALYZE TABLE item;
ANALYZE TABLE product;
ANALYZE TABLE product_item;

A principal ideia é reduzir a complexidade da consulta e garantir que os índices adequados estão sendo utilizados pelo banco de dados. Além disso, certifique-se de testar cada otimização individualmente para verificar se há melhorias no desempenho.

Compartilhar este post


Link para o post
Compartilhar em outros sites
19 horas atrás, Jack Oliveira disse:

Reescreva sua consulta para minimizar o número de operações e junções complexas. Vou fornecer uma versão otimizada da sua consulta:

 


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
WHERE 
    P.id IN (
        SELECT PI.productID
        FROM product_item PI
        JOIN item I ON I.id = PI.itemID
        WHERE I.name = 'casa'
    )
    AND P.id NOT IN (
        SELECT PI.productID
        FROM product_item PI
        JOIN item I ON I.id = PI.itemID
        WHERE I.active IS NULL OR I.active != 'S'
    )
GROUP BY 
    P.id, P.name;

utilização de subconsultas pode ser custosa, especialmente em grandes bancos de dados. Tente minimizar subconsultas complexas. Vou tentar reescrever a consulta para evitar subconsultas:

 


SELECT 
    P.id, 
    P.name, 
    GROUP_CONCAT(I.name ORDER BY I.name) AS items
FROM 
    product P
JOIN 
    product_item PI1 ON P.id = PI1.productID
JOIN 
    item I1 ON I1.id = PI1.itemID
WHERE 
    I1.name = 'casa'
    AND NOT EXISTS (
        SELECT 1
        FROM product_item PI2
        JOIN item I2 ON I2.id = PI2.itemID
        WHERE 
            PI2.productID = P.id
            AND (I2.active IS NULL OR I2.active != 'S')
    )
GROUP BY 
    P.id, P.name;

Execute comandos de análise de índices para garantir que o banco de dados está utilizando os índices de forma eficiente:

 


ANALYZE TABLE item;
ANALYZE TABLE product;
ANALYZE TABLE product_item;

A principal ideia é reduzir a complexidade da consulta e garantir que os índices adequados estão sendo utilizados pelo banco de dados. Além disso, certifique-se de testar cada otimização individualmente para verificar se há melhorias no desempenho.

Muito obrigado. Testei aqui e funcionou. Demora cerca de 30 segundos para fazer a listagem mais isso não incomoda.

 

Estou com uma outra dúvida. Nesta parte do código:

SELECT PI.productID
        FROM product_item PI
        JOIN item I ON I.id = PI.itemID
        WHERE I.name = 'casa'

Ele busca pela palavra "casa". Tem como fazer com que ele liste também um outro item? Por exemplo: Listar todos os registros que contenham o item "casa" e também os registros que contenham o item "apartamento"?

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Se os nomes dos itens forem exatamente "casa" e "apartamento" pode usar IN

SELECT PI.productID
FROM product_item PI
JOIN item I ON I.id = PI.itemID
WHERE I.name IN ('casa', 'apartamento')

Com php

 

<?php
// Configuração da conexão
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "database";

// Cria a conexão
$conn = new mysqli($servername, $username, $password, $dbname);

// Verifica a conexão
if ($conn->connect_error) {
    die("Falha na conexão: " . $conn->connect_error);
}

// Define os itens que você deseja buscar
$items = ['casa', 'apartamento'];

// Cria a consulta
$sql = "SELECT PI.productID
        FROM product_item PI
        JOIN item I ON I.id = PI.itemID
        WHERE I.name IN ('" . implode("','", array_map([$conn, 'real_escape_string'], $items)) . "')";

// Executa a consulta
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    // Saída dos dados de cada linha
    while ($row = $result->fetch_assoc()) {
        echo "productID: " . $row["productID"]. "<br>";
    }
} else {
    echo "0 resultados";
}

// Fecha a conexão
$conn->close();
?>

 

Se você precisar buscar por correspondências parciais, como "casa%" ou "apartamento%":

 

Pode usar OR com Like

 

SELECT PI.productID
FROM product_item PI
JOIN item I ON I.id = PI.itemID
WHERE I.name LIKE 'casa%' OR I.name LIKE 'apartamento%'

Php

 

<?php
// Configuração da conexão
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "database";

// Cria a conexão
$conn = new mysqli($servername, $username, $password, $dbname);

// Verifica a conexão
if ($conn->connect_error) {
    die("Falha na conexão: " . $conn->connect_error);
}

// Define os itens que você deseja buscar
$items = ['casa', 'apartamento'];

// Cria a consulta
$sql = "SELECT PI.productID
        FROM product_item PI
        JOIN item I ON I.id = PI.itemID
        WHERE ";
$whereClauses = [];
foreach ($items as $item) {
    $escapedItem = $conn->real_escape_string($item);
    $whereClauses[] = "I.name LIKE '%$escapedItem%'";
}
$sql .= implode(' OR ', $whereClauses);

// Executa a consulta
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    // Saída dos dados de cada linha
    while ($row = $result->fetch_assoc()) {
        echo "productID: " . $row["productID"]. "<br>";
    }
} else {
    echo "0 resultados";
}

// Fecha a conexão
$conn->close();
?>

 

Agora se você estiver buscando pelos nomes exatos e preferir a sintaxe com OR

 

SELECT PI.productID
FROM product_item PI
JOIN item I ON I.id = PI.itemID
WHERE I.name = 'casa' OR I.name = 'apartamento'
<?php
// Configuração da conexão
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "database";

// Cria a conexão
$conn = new mysqli($servername, $username, $password, $dbname);

// Verifica a conexão
if ($conn->connect_error) {
    die("Falha na conexão: " . $conn->connect_error);
}

// Define os itens que você deseja buscar
$items = ['casa', 'apartamento'];

// Cria a consulta
$sql = "SELECT PI.productID
        FROM product_item PI
        JOIN item I ON I.id = PI.itemID
        WHERE ";
$whereClauses = [];
foreach ($items as $item) {
    $escapedItem = $conn->real_escape_string($item);
    $whereClauses[] = "I.name = '$escapedItem'";
}
$sql .= implode(' OR ', $whereClauses);

// Executa a consulta
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    // Saída dos dados de cada linha
    while ($row = $result->fetch_assoc()) {
        echo "productID: " . $row["productID"]. "<br>";
    }
} else {
    echo "0 resultados";
}

// Fecha a conexão
$conn->close();
?>

 

 

Se você precisar de correspondências exatas, utilize IN ou OR com igualdade. Se precisar de correspondências parciais, utilize LIKE com OR.

 

 

O php é só uma base de como poderia esta fazendo blz..

 

Ajuste a sua maneira do seu projeto

 

Compartilhar este post


Link para o post
Compartilhar em outros sites
1 hora atrás, landerbadi disse:

Muito obrigado. Testei aqui e funcionou. Demora cerca de 30 segundos para fazer a listagem mais isso não incomoda

otimizar sua consulta e melhorar a performance, a ideia principal é evitar subconsultas complexas e garantir que os índices corretos estão sendo utilizados.

 

SELECT 
    P.id, 
    P.name, 
    GROUP_CONCAT(I1.name ORDER BY I1.name) AS items
FROM 
    product P
JOIN 
    product_item PI1 ON P.id = PI1.productID
JOIN 
    item I1 ON I1.id = PI1.itemID
LEFT JOIN 
    (SELECT PI2.productID
     FROM product_item PI2
     JOIN item I2 ON I2.id = PI2.itemID
     WHERE I2.active IS NULL OR I2.active != 'S'
     GROUP BY PI2.productID) AS InactiveItems ON P.id = InactiveItems.productID
WHERE 
    I1.name = 'casa'
    AND InactiveItems.productID IS NULL
GROUP BY 
    P.id, P.name;

os índices apropriados estão configurados para as tabelas envolvidas. Os seguintes índices são recomendados:

 

CREATE INDEX idx_product_item_productID ON product_item(productID);
CREATE INDEX idx_product_item_itemID ON product_item(itemID);
CREATE INDEX idx_item_id ON item(id);
CREATE INDEX idx_item_name ON item(name);
CREATE INDEX idx_item_active ON item(active);

Php

 

<?php
// Configuração da conexão
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "database";

// Cria a conexão
$conn = new mysqli($servername, $username, $password, $dbname);

// Verifica a conexão
if ($conn->connect_error) {
    die("Falha na conexão: " . $conn->connect_error);
}

// Cria a consulta
$sql = "
SELECT 
    P.id, 
    P.name, 
    GROUP_CONCAT(I1.name ORDER BY I1.name) AS items
FROM 
    product P
JOIN 
    product_item PI1 ON P.id = PI1.productID
JOIN 
    item I1 ON I1.id = PI1.itemID
LEFT JOIN 
    (SELECT PI2.productID
     FROM product_item PI2
     JOIN item I2 ON I2.id = PI2.itemID
     WHERE I2.active IS NULL OR I2.active != 'S'
     GROUP BY PI2.productID) AS InactiveItems ON P.id = InactiveItems.productID
WHERE 
    I1.name = 'casa'
    AND InactiveItems.productID IS NULL
GROUP BY 
    P.id, P.name;
";

// Executa a consulta
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    // Saída dos dados de cada linha
    while ($row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"] . " - Nome: " . $row["name"] . " - Itens: " . $row["items"] . "<br>";
    }
} else {
    echo "0 resultados";
}

// Fecha a conexão
$conn->close();
?>

Veja se sql assim ajudar melhor o tempo da consulta 

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 daemon
      Boa tarde,
       
      tenho o domío na raiz: teste.com.br
      Gostaria de criar uma barra /categoria.
       
      No caso fica teste.com.br/categoria/
      Tem como eu fazer este url sem criar a pasta categoria ?
    • Por daemon
      Estou com uma dúvida,
       
      Eu tenho uma rotina que faz uma leitura do arquivo .xml de vários sites.

      Eu consigo pegar o tópico e a descrição, porém gostara de mostrar a imagem que esta na pagina do link.
       
      É possível eu colocar na minha index o url, e aparecer uma preview da imagem que esta no link ? 
       
      Igual o google: https://news.google.com/topics/CAAqKggKIiRDQkFTRlFvSUwyMHZNRGx1YlY4U0JYQjBMVUpTR2dKQ1VpZ0FQAQ?hl=pt-BR&amp;gl=BR&amp;ceid=BR%3Apt-419
       
      No caso eu importo o tópico e descrição, porém não consigo deixar essa imagem na pagina principal.
    • Por violin101
      Caros amigos, saudações.

      Estou com uma dúvida:
      Como consigo fazer uma rotina no sistema onde possa fazer de forma automática trazer o resultado de entrada de produtos a cada 3 meses, por exemplo:

      Produtos --- Entrada ---  Meses
      Prod_A -------- 100 ------ 01/2024
      Prod_A -------- 120 ------ 03/2024
      Prod_A -------- 105 ------ 04/2024
      =========================
      Prod_B -------- 105 ------ 02/2024
      Prod_B -------- 120 ------ 03/2024
      Prod_B -------- 130 ------ 04/2024
      =========================
      ETC
       
      Como consigo mostrar para o usuário esse movimento de entrada a cada 3 meses ?

      Grato,
       
      Cesar
    • Por violin101
      Caros amigos, saudações.
       
      Peço desculpa em recorrer a ajuda dos amigos, pois está sendo um mistério para mim, configurar o meu sistema para Impressão de XML e NFe.

      Quando acrescento as linhas no início do Controller:
      use NFePHP\NFe\Make;
      use NFePHP\NFe\Tools;
      use NFePHP\NFe\Danfe;
      etc, etc.
       
       e chamo a libraries assim em alguma função:  $nfe = new Make(); e etc. <== o sistema acusa que não consegue achar 
       
      fiz as alterações necessárias no autoload
      /* |-------------------------------------------------------------------------- | Composer auto-loading |-------------------------------------------------------------------------- | | Enabling this setting will tell CodeIgniter to look for a Composer | package auto-loader script in application/vendor/autoload.php. | | $config['composer_autoload'] = TRUE; | | Or if you have your vendor/ directory located somewhere else, you | can opt to set a specific path as well: | | $config['composer_autoload'] = '/path/to/vendor/autoload.php'; | | For more information about Composer, please visit http://getcomposer.org/ | | Note: This will NOT disable or override the CodeIgniter-specific | autoloading (application/config/autoload.php) */ $config['composer_autoload'] = './vendor/autoload.php';  
      o meu composer.json está assim:
      { "description": "The CodeIgniter framework", "name": "codeigniter/framework", "type": "project", "homepage": "https://codeigniter.com", "license": "MIT", "support": { "forum": "http://forum.codeigniter.com/", "wiki": "https://github.com/bcit-ci/CodeIgniter/wiki", "irc": "irc://irc.freenode.net/codeigniter", "source": "https://github.com/bcit-ci/CodeIgniter" }, "require": { "php": ">=5.3.7", "mpdf/mpdf": "^7.1", "nfephp-org/sped-nfe": "^5.0", "nfephp-org/sped-da": "^1.0" }, "suggest": { "paragonie/random_compat": "Provides better randomness in PHP 5.x" }, "autoload": { "psr-4": { "Libraries\\": "application/libraries" } } }  
      após instalar a NFePhp através do composer, o caminho da pasta NFePhp está assim:
      nome_do_meu_sistema
      |____ application
      |______vendor
      |________nfephp-org
      |__________sped-common
      |__________sped-da
      |__________sped-gtin
      |__________sped-nfe

      Como consigo trazer os comando/libraries da NFe para poder utilizar no projeto.
       
      Grato,
       
      Cesar
    • Por violin101
      Caros amigos, saudações.
       
      Estou com uma dúvida e não estou conseguindo resolver.
       
      Tenho uma tabela onde o usuário lança a Entrada de Produtos <=== até aqui tudo bem.

      O problema que estou encontrando é: 
      - após lançar os itens e se por alguma necessidade precisar fazer devolução, gostaria de alterar o STATUS do lançamento do PRODUTO de 1 para 2, ou seja:
       
      lançou:
      101 - Caneta ------- 10 x 1.50 = 15,00 status = 1
      102 - Lápis --------- 10 x 1.50 = 15,00 status = 1
      103 - Borracha ---- 10 x 1.50 = 15,00 status = 1
      104 - Apontador - 10 x 1.50 = 15,00 status = 1
       
      Se precisar DEVOLVER os itens 102 e 103 o Status de 1 passa para 2, ou seja:
      101 - Caneta ------- 10 x 1.50 = 15,00 status = 1
      102 - Lápis --------- 10 x 1.50 = 15,00 status = 2
      103 - Borracha ---- 10 x 1.50 = 15,00 status = 2
      104 - Apontador - 10 x 1.50 = 15,00 status = 1
       
      Obs.: como consigo filtrar o CódigoProduto + NúmeroLançamento, para não ocorrer o erro de alterar todos.
       
      O problema que estou enfrentando, quando dou o Comando para Alterar, o Sistema Altera todos os produtos lançados que estiver na base de dados, ou seja, onde tiver 102 e 103, o sistema altera.
       
      o meu código:
      CONTROLLER
      /*Salva o Detalhe do Lançamento*/ protected function save_detalhe($prods,$qtde,$vlrunit,$vlrtotal,$notaLct,$numlcto,$codslct){ for ($i=0; $i < count($prods); $i++) { $data = array( 'cod_prod' => $prods[$i], 'qtd_prod' => $qtde[$i], 'vlr_unit' => $vlrunit[$i], 'vlr_total' => $vlrtotal[$i], 'status' => 2, 'devitens' => 2, 'n_nf' => $notaLct, 'lancamentos_idlcto'=> $numlcto, ); //Função para Salvar os Detalhes do lançamento $this->devolucao_model->save_detalhe($data); //Função para Atualizar a Quantidade de Produto no Estoque $this->update_produto($prods[$i],$qtde[$i],$vlrunit[$i],$vlrtotal[$i]); //Função para Alterar Devolução do Item $this->upd_itens($prods[$i],$notaLct[$i],$codslct[$i]); } } /*<=== até aqui tudo certo, como deve ser feito*/ /* * O Problema está aqui * para não ocorrer duplicidade de Lançamento, * o sistema altera o Status do 1º. lançamento */ protected function upd_itens($idprds,$notaLct,$codslct){ $data = array( 'devitens' => 2, ); $this->devolucao_model->update_itens($idprds,$data); }  
      MODEL:
      //Altera o Campo Devolução do Item Lançado //Altera todos os código de produto que encontrar public function update_itens($idprds,$data){ $this->db->where("cod_prod",$idprds); return $this->db->update("lctos_itens",$data); }  
      Grato,
       
      Cesar






       
       
       
       
       
       
×

Informação importante

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