menu
Fórum Adianti
menu Menu
Soma de valores no banco de dados (sum) via objeto e sem SQL Olá, bom dia, Disponibilizo aqui uma forma de trabalhar com a soma de valores no banco de dados via objeto, usando componentes do framework, espero que seja útil. A algum tempo tenho a necessidade de trabalhar com relatórios complexos, para tal uso views de acordo com o que já foi tratado pelo próprio Pablo no passado (disponível aqui https://www.adianti.com.br/forum/pt/view_879?relat...
MD
Soma de valores no banco de dados (sum) via objeto e sem SQL  
Olá, bom dia,

Disponibilizo aqui uma forma de trabalhar com a soma de valores no banco de dados via objeto, usando componentes do framework, espero que seja útil.

A algum tempo tenho a necessidade de trabalhar com relatórios complexos, para tal uso views de acordo com o que já foi tratado pelo próprio Pablo no passado (disponível aqui https://www.adianti.com.br/forum/pt/view_879?relatorios-com-queries-complexas-de).

Acontece que mais recentemente tive a necessidade de trabalhar com soma de valores ou mesmo a soma de colunas dos objetos, realizando isso através do TRepository e somando dentro da classe (através do PHP), isso funciona bem, mas o desempenho deixa a deseja quando se trabalha com alguns milhões de entradas no banco de dados.

Pensando nisso, e pensando em resolver meu problema de forma "elegante", evitando montar SQLs na mão para poder ter o sum e também podendo aproveitar objetos com TCriteria por exemplo, criei uma classe chamada TRepositorySum (baseada em TRepository tentei estender ela pois precisava de um único método, mas ela é uma classe final e não pode ser estendida), onde é possível somar colunas ou mesmo valores de mais de uma coluna. Abaixo segue a classe e alguns exemplos de uso:

  1. <?php
  2. /**
  3.  * Implements the Repository Pattern to deal with sum of Active Records
  4.  *
  5.  * @version    4.0
  6.  * @package    database
  7.  * @author     Pablo Dall'Oglio
  8.  * @author     Marco Driemeyer <marco@plenatech.com.br>
  9.  * @copyright  Copyright (c) 2006 Adianti Solutions Ltd. (http://www.adianti.com.br)
  10.  * @license    http://www.adianti.com.br/framework-license
  11.  */
  12. class TRepositorySum 
  13. {
  14.     private $class// Active Record class to be manipulated
  15.     private $criteria// buffered criteria to use with fluent interfaces
  16.     
  17.     
  18.     /**
  19.      * Class Constructor
  20.      * @param $class = Active Record class name
  21.      */
  22.     public function __construct($class)
  23.     {
  24.         if (class_exists($class))
  25.         {
  26.             if (is_subclass_of($class'TRecord'))
  27.             {
  28.                 $this->class $class;
  29.                 $this->criteria = new TCriteria;
  30.             }
  31.             else
  32.             {
  33.                 throw new Exception(AdiantiCoreTranslator::translate('The class ^1 was not accepted as argument. The class informed as parameter must be subclass of ^2.'$class'TRecord'));
  34.             }
  35.         }
  36.         else
  37.         {
  38.             throw new Exception(AdiantiCoreTranslator::translate('The class ^1 was not found. Check the class name or the file name. They must match'$class));
  39.         }
  40.     }
  41.     
  42.     
  43.     /**
  44.      * Returns the name of database entity
  45.      * @return A String containing the name of the entity
  46.      */
  47.     protected function getEntity()
  48.     {
  49.         return constant($this->class.'::TABLENAME');
  50.     }
  51.     /**
  52.      * Return the sum of columns of objects that satisfy a given criteria
  53.      * @param $criteria  An TCriteria object, specifiyng the filters
  54.      * @param $columns   An indexed array with the name and the column to sum
  55.      * @return           An stdClass with the named property storing the sum of values
  56.      */
  57.     public function sum(TCriteria $criteria NULL, array $columns)
  58.     {
  59.         if (!$criteria)
  60.         {
  61.             $criteria = isset($this->criteria) ? $this->criteria : new TCriteria;
  62.         }
  63.         // creates a SELECT statement
  64.         $sql = new TSqlSelect;
  65.         
  66.         // Interact with the array and add the columns to sum
  67.         foreach ($columns as $key => $column)
  68.         {
  69.             $sql->addColumn("sum ($column) as $key");
  70.         }
  71.         
  72.         $sql->setEntity($this->getEntity());
  73.         // assign the criteria to the SELECT statement
  74.         $sql->setCriteria($criteria);
  75.         
  76.         // get the connection of the active transaction
  77.         if ($conn TTransaction::get())
  78.         {
  79.             // register the operation in the LOG file
  80.             TTransaction::log($sql->getInstruction());
  81.             
  82.             $dbinfo TTransaction::getDatabaseInfo(); // get dbinfo
  83.             if (isset($dbinfo['prep']) AND $dbinfo['prep'] == '1'// prepared ON
  84.             {
  85.                 $result $conn-> prepare $sql->getInstructionTRUE ) , array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
  86.                 $result-> execute $criteria->getPreparedVars() );
  87.             }
  88.             else
  89.             {
  90.                 // executes the SELECT statement
  91.                 $result$conn-> query($sql->getInstruction());
  92.             }
  93.             
  94.             if ($result)
  95.             {
  96.                 $row $result->fetch();
  97.              
  98.                 // Initiate the stdClass and interact with the return of the sum 
  99.                 $stdClass = new stdClass();
  100.                 foreach ($columns as $key => $column)
  101.                 {
  102.                     if ($row["$key"])
  103.                         $stdClass->$key $row["$key"];
  104.                     else
  105.                         $stdClass->$key 0;
  106.                 }
  107.                 
  108.                 return $stdClass;
  109.             }
  110.         }
  111.         else
  112.         {
  113.             // if there's no active transaction opened
  114.             throw new Exception(AdiantiCoreTranslator::translate('No active transactions') . ': ' __METHOD__ .' '$this->getEntity());
  115.         }
  116.     }
  117.     
  118.     public function get(TCriteria $criteria NULL$callObjectLoad TRUE)
  119.     {
  120.         return $this->load($criteria$callObjectLoad);
  121.     }
  122. }
  123. ?>



Exemplos de uso:
  1. <?php
  2.     $reposum = new TRepositorySum('ModelToSum');
  3.     $criteria = new TCriteria();
  4.     $criteria->add(new TFilter('column1''>=''1'));
  5.     $criteria->add(new TFilter('column2''=''3'));
  6.                         
  7.     // TRepositorySum retorna a soma de uma ou mais colunas dentro de um objeto stdClass, as colunas a serem somadas são informadas no momento da chamada do metodo
  8.     // Para somar, basta chamar o metodo sum com um array indexado no qual informamos o nome da propriedade que deve receber a soma no objeto que será retornado e os campos a serem somados
  9.     // Realizamos testes em PostgreSQL e MySQL, mas possivelmente funcione em outros bancos, se não funcionar a lógica para implementar a soma nesses banco esta exposta e pode ser ajustada
  10.     $simpleSum $repo->sum($criteria, array('return_name' => 'column_to_sum'));
  11.     echo "$simpleSum->return_name"// retorna a soma da coluna column_to_sum realizada no banco de dados
  12.     $otherSum $repo->sum($criteria, array('return_name' => 'column_to_sum''return_sum' => 'column_to_sum_1 + solumn_to_sum_2'));
  13.     echo "$otherSum->return_name"// retorna a soma da coluna column_to_sum realizada no banco de dados
  14.     echo "$otherSum->return_sum";   // retorna a soma das colunas column_to_sum_1 + column_to_sum_2 realizada no banco de dados
  15. ?>


Espero ter me feito entender e espero que a classe seja útil. No futuro talvez esse método seja um método interessante para ser adicionado a classe TRepository.

Um abraço!

Curso completo Meu Negócio Pronto
Use para si, ou transforme em um negócio: Inclui aulas e códigos-fontes
Gestor de conteúdo (SITE) + Loja Virtual (E-Commerce) + Emissor de Notas para infoprodutos


Meu negócio pronto Quero me inscrever agora!

Comentários (7)


MD

Abaixo um exemplo do SQL executado no baco de dados:
SELECT sum (column_to_sum) as return_name, sum (column1 + column2) as return_sum FROM tabele WHERE (column1 >= 1 AND column2 = 3)
WP

Legal ter compartilhado, muito bom parabéns , na próxima coloque dentro de Contribuições, aqui logo vai se perder
FC

Gostei também.. testei funciona bem, um alerta é que a variável $reposum deve ser $repo senão vai dar erro no teste.


MD

Caros, agradeço o retorno, criei uma publicação dentro das contribuições referenciado esta e ajustando o erro, grato a vcs!
Abs
MS

Muito bacana a contribuição, show e bem documentada.
RX

Não sei se nos dias de hj já existe uma maneira melhor de dar um SUM numa tabela pelo Adianti.
A classe do Marco funciona perfeitamente, porém, acredito que por mudança nas versões mais atuais do Mysql, acaba gerando uma exceção dizendo que a função Sum não existe.
Para resolver, tire o espaço depois do "sum" na linha 71.

Troque
  1. <?php            $sql->addColumn("sum ($column) as $key"); ?>

por:
  1. <?php            $sql->addColumn("sum($column) as $key"); ?>

RX

Consegui fazendo extendendo de TRepository


  1. <?php
  2. use Adianti\Database\TRepository;
  3. /**
  4.  * Implements the Repository Pattern to deal with sum of Active Records
  5.  *
  6.  * @version    4.0
  7.  * @package    database
  8.  * @author     Pablo Dall'Oglio
  9.  * @author     Marco Driemeyer <marco@plenatech.com.br>
  10.  * @copyright  Copyright (c) 2006 Adianti Solutions Ltd. (http://www.adianti.com.br)
  11.  * @license    http://www.adianti.com.br/framework-license
  12.  */
  13. class TRepositorySum extends TRepository
  14. {
  15.     public function sum(TCriteria $criteria NULL, array $columns)
  16.     {
  17.         if (!$criteria)
  18.         {
  19.             $criteria = isset($this->criteria) ? $this->criteria : new TCriteria;
  20.         }
  21.         // creates a SELECT statement
  22.         $sql = new TSqlSelect;
  23.         
  24.         // Interact with the array and add the columns to sum
  25.         foreach ($columns as $key => $column)
  26.         {
  27.             $sql->addColumn("sum($column) as $key");
  28.         }
  29.         
  30.         $sql->setEntity($this->getEntity());
  31.         // assign the criteria to the SELECT statement
  32.         $sql->setCriteria($criteria);
  33.         
  34.         // get the connection of the active transaction
  35.         if ($conn TTransaction::get())
  36.         {
  37.             // register the operation in the LOG file
  38.             TTransaction::log($sql->getInstruction());
  39.             
  40.             $dbinfo TTransaction::getDatabaseInfo(); // get dbinfo
  41.             if (isset($dbinfo['prep']) AND $dbinfo['prep'] == '1'// prepared ON
  42.             {
  43.                 $result $conn-> prepare $sql->getInstructionTRUE ) , array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
  44.                 $result-> execute $criteria->getPreparedVars() );
  45.             }
  46.             else
  47.             {
  48.                 // executes the SELECT statement
  49.                 $result$conn-> query($sql->getInstruction());
  50.             }
  51.             
  52.             if ($result)
  53.             {
  54.                 $row $result->fetch();
  55.              
  56.                 // Initiate the stdClass and interact with the return of the sum 
  57.                 $stdClass = new stdClass();
  58.                 foreach ($columns as $key => $column)
  59.                 {
  60.                     if ($row["$key"])
  61.                         $stdClass->$key $row["$key"];
  62.                     else
  63.                         $stdClass->$key 0;
  64.                 }
  65.                 
  66.                 return $stdClass;
  67.             }
  68.         }
  69.         else
  70.         {
  71.             // if there's no active transaction opened
  72.             throw new Exception(AdiantiCoreTranslator::translate('No active transactions') . ': ' __METHOD__ .' '$this->getEntity());
  73.         }
  74.     }
  75. }
  76. ?>