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. * Class Constructor
  18. * @param $class = Active Record class name
  19. */
  20. public function __construct($class)
  21. {
  22. if (class_exists($class))
  23. {
  24. if (is_subclass_of($class, 'TRecord'))
  25. {
  26. $this->class = $class;
  27. $this->criteria = new TCriteria;
  28. }
  29. else
  30. {
  31. 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'));
  32. }
  33. }
  34. else
  35. {
  36. throw new Exception(AdiantiCoreTranslator::translate('The class ^1 was not found. Check the class name or the file name. They must match', $class));
  37. }
  38. }
  39. /**
  40. * Returns the name of database entity
  41. * @return A String containing the name of the entity
  42. */
  43. protected function getEntity()
  44. {
  45. return constant($this->class.'::TABLENAME');
  46. }
  47. /**
  48. * Return the sum of columns of objects that satisfy a given criteria
  49. * @param $criteria An TCriteria object, specifiyng the filters
  50. * @param $columns An indexed array with the name and the column to sum
  51. * @return An stdClass with the named property storing the sum of values
  52. */
  53. public function sum(TCriteria $criteria = NULL, array $columns)
  54. {
  55. if (!$criteria)
  56. {
  57. $criteria = isset($this->criteria) ? $this->criteria : new TCriteria;
  58. }
  59. // creates a SELECT statement
  60. $sql = new TSqlSelect;
  61. // Interact with the array and add the columns to sum
  62. foreach ($columns as $key => $column)
  63. {
  64. $sql->addColumn("sum ($column) as $key");
  65. }
  66. $sql->setEntity($this->getEntity());
  67. // assign the criteria to the SELECT statement
  68. $sql->setCriteria($criteria);
  69. // get the connection of the active transaction
  70. if ($conn = TTransaction::get())
  71. {
  72. // register the operation in the LOG file
  73. TTransaction::log($sql->getInstruction());
  74. $dbinfo = TTransaction::getDatabaseInfo(); // get dbinfo
  75. if (isset($dbinfo['prep']) AND $dbinfo['prep'] == '1') // prepared ON
  76. {
  77. $result = $conn-> prepare ( $sql->getInstruction( TRUE ) , array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
  78. $result-> execute ( $criteria->getPreparedVars() );
  79. }
  80. else
  81. {
  82. // executes the SELECT statement
  83. $result= $conn-> query($sql->getInstruction());
  84. }
  85. if ($result)
  86. {
  87. $row = $result->fetch();
  88. // Initiate the stdClass and interact with the return of the sum
  89. $stdClass = new stdClass();
  90. foreach ($columns as $key => $column)
  91. {
  92. if ($row["$key"])
  93. $stdClass->$key = $row["$key"];
  94. else
  95. $stdClass->$key = 0;
  96. }
  97. return $stdClass;
  98. }
  99. }
  100. else
  101. {
  102. // if there's no active transaction opened
  103. throw new Exception(AdiantiCoreTranslator::translate('No active transactions') . ': ' . __METHOD__ .' '. $this->getEntity());
  104. }
  105. }
  106. public function get(TCriteria $criteria = NULL, $callObjectLoad = TRUE)
  107. {
  108. return $this->load($criteria, $callObjectLoad);
  109. }
  110. }
  111. ?>



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. // 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
  7. // 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
  8. // 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
  9. $simpleSum = $repo->sum($criteria, array('return_name' => 'column_to_sum'));
  10. echo "$simpleSum->return_name"; // retorna a soma da coluna column_to_sum realizada no banco de dados
  11. $otherSum = $repo->sum($criteria, array('return_name' => 'column_to_sum', 'return_sum' => 'column_to_sum_1 + solumn_to_sum_2'));
  12. echo "$otherSum->return_name"; // retorna a soma da coluna column_to_sum realizada no banco de dados
  13. echo "$otherSum->return_sum"; // retorna a soma das colunas column_to_sum_1 + column_to_sum_2 realizada no banco de dados
  14. ?>


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 Dominando o Adianti Framework

O material mais completo de treinamento do Framework.
Curso em vídeo aulas + Livro completo + Códigos fontes do projeto ERPHouse.
Conteúdo Atualizado!


Dominando o Adianti Framework 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. // Interact with the array and add the columns to sum
  24. foreach ($columns as $key => $column)
  25. {
  26. $sql->addColumn("sum($column) as $key");
  27. }
  28. $sql->setEntity($this->getEntity());
  29. // assign the criteria to the SELECT statement
  30. $sql->setCriteria($criteria);
  31. // get the connection of the active transaction
  32. if ($conn = TTransaction::get())
  33. {
  34. // register the operation in the LOG file
  35. TTransaction::log($sql->getInstruction());
  36. $dbinfo = TTransaction::getDatabaseInfo(); // get dbinfo
  37. if (isset($dbinfo['prep']) AND $dbinfo['prep'] == '1') // prepared ON
  38. {
  39. $result = $conn-> prepare ( $sql->getInstruction( TRUE ) , array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
  40. $result-> execute ( $criteria->getPreparedVars() );
  41. }
  42. else
  43. {
  44. // executes the SELECT statement
  45. $result= $conn-> query($sql->getInstruction());
  46. }
  47. if ($result)
  48. {
  49. $row = $result->fetch();
  50. // Initiate the stdClass and interact with the return of the sum
  51. $stdClass = new stdClass();
  52. foreach ($columns as $key => $column)
  53. {
  54. if ($row["$key"])
  55. $stdClass->$key = $row["$key"];
  56. else
  57. $stdClass->$key = 0;
  58. }
  59. return $stdClass;
  60. }
  61. }
  62. else
  63. {
  64. // if there's no active transaction opened
  65. throw new Exception(AdiantiCoreTranslator::translate('No active transactions') . ': ' . __METHOD__ .' '. $this->getEntity());
  66. }
  67. }
  68. }
  69. ?>