Plano de Contas - como gerar relatório via código ou banco - [resolvido (via banco)]

Boa tarde,

Eu tenho as seguintes tabelas no banco de dados:

PlanoContas
-Conta String
-CodRed int
-Desc String
-Tipo String

Lancamento
-idLancamento serial
-idContaCredito  String
-idContaDebito String
-valor double

Eu preciso criar um relatório que mostre a conta do plano de contas, sua descrição, total debitado, total creditado e saldo.

A estrutura do meu plano de contas é a seguinte:

1 digito - sintética
3 digitos - sintética
5 digitos - sintética
7 digitos - sintética
12 digitos - analítica

Um exemplo básico:

1 - ATIVO

101 - ATIVO CIRCULANTE

10101 - DISPONIBILIDADE

1010101 - CAIXA GERAL

101010100001 - CAIXA

Sendo assim, os valores da conta 1 (ativo) é a soma de todas as contas que tem três digitos começando com 1.
os valores da conta 101 é a soma de todas as contas que tem cinco digitos começando com 101.
os valores da conta 10101 é a soma de todas as contas que tem sete digitos começando com 10101…
[e assim sucessivamente]

Sendo assim, devo varrer essa estrutura para conseguir montar o relatório, fazendo alguns selects básicos nas contas analíticas, por exemplo

select sum(valor) from lancamento where idContaCredito = ? select sum(valor) from lancamento where idContaDebito = ? --saldo é só subtrair um pelo outro

Estava pensando em fazer isso usando uma função no banco de dados, porém com recursividade… Ou seria melhor fazer isso via código [temos que levar em conta que se for via código pode travar o sistema caso haja muitos lançamentos] ?

Alguém tem alguma ideia de qual a melhor maneira de fazer isso?

Obrigado

cara,

ve esse sql funciona.

select pc.conta, p.desc, pc.totalCredito, pd.totalDebito, pc.totalCredito - pd.totalDebito 
from PlanoContas pc
join (select idContaCredito, sum(valor) as totalCredito from Lancamento where idContaCredito = ? group by idContaCredito) lc on (lc.idContaCredito = pc.conta)
join (select idContaDebito, sum(valor) as totalDebito from Lancamento where idContaDebito = ? group by idContaDebito) ld on (ld.idContaDebito = pc.conta)
where pc.conta = ?

t+

Com uma query voce poderia resolver fazendo um select na tabela de Contas e uma subquery na de lançamentos onde o início do campo idConta seja igual ao valor do campo conta da tabela de lançamentos. Algo assim:

( select sum(valor) from lancamento where left(idconta,length(pc.conta)) = pc.conta )

Entãooo,
a query não funcionou alissonvla, mas obrigado por tentar me ajudar.

AbelBueno achei que precisava fazer uma query recursiva sabe? Ou uma function para resolver isso…
pq ai ele pega a conta 1 (Ativo) … dps busca as que tem 3 digitos e começa com 1…
ai busca as que tem 5 digitos e começa com os 3 digitos
depois as que tem 7 e começa com os 5,
e as que tem 12 que começa com os 7…

tendo em vista que só haverão lançamentos nas contas que tem 12 digitos…

Ou ffazer o processo inverso…
pega todas as que tem 12 do mesmo grupo e vai somando pra cima… sustring ?..

mas não faço ideia =/

opa, arrumei aqui a consulta e deu certo…

porém ela só funcionaria para as contas analíticas… Para as sintéticas como descrevi no comentário acima,
não funcionaria :frowning:

[quote=d34d_d3v1l]opa, arrumei aqui a consulta e deu certo…

porém ela só funcionaria para as contas analíticas… Para as sintéticas como descrevi no comentário acima,
não funcionaria :([/quote]

Por que só funciona para contas analíticas?

Mostra como tá sua query, alguns dados de exemplo e o resultado esperado (de preferencia mostrando o motivo de não funcionar)

Certo, vou tentar exemplificar…
Vamos supor que eu tenho este plano de contas fictício claro só para demonstrar o que quero dizer…

[code]1 - ATIVO

101 - ATIVO CIRCULANTE

10101 - DISPONIBILIDADE

1010101 - CAIXA GERAL

101010100001 - CAIXA

1010102 - CAIXA ESCONDIDO

101010200001 - CAIXA 2

[/code]

Vamos aos lançamentos [os lançamentos só são feitos em contas analiticas, por tanto]:

CONTA CREDITO DEBITO SALDO CAIXA 800 100 700 CAIXA 2 400 100 300

beleza, agora vamos montar o relatório como deveria ser:


CONTA___________________________CREDITO_______________DEBITO________________SALDO

1 - ATIVO________________________1200___________________200____________________1000 [SOMA DAS CONTAS QUE TEM 3 DIGITOS]

101 - ATIVO CIRCULANTE__________1200____________________200____________________1000 [SOMA DAS CONTAS QUE TEM 5 DIGITOS]
 
10101 - DISPONIBILIDADE_________1200____________________200____________________1000 [SOMA DAS CONTAS QUE TEM 7 DIGITOS]

1010101 - CAIXA GERAL___________800_____________________100____________________700    [SOMA DAS CONTAS ANALITICAS DE 12 DIGITOS]

101010100001 - CAIXA____________800_____________________100___________________700  [CONTA ANALITICA]

1010102 - CAIXA ESCONDIDO______400_____________________100___________________300  [SOMA DAS CONTAS ANALITICAS DE 12 DIGITOS]

101010200001 - CAIXA 2__________400______________________100___________________300  [CONTA ANALITICA]

AFF NAO FICOU FORMATADO =/

Olha só, rodando a query que te passei com um modelo resumido que criei aqui deu justamente o resultado que voce montou aí.

Não colocou sua query, para ver se tem alguma diferença nela.

Segue o script que rodei aqui:

DROP TABLE IF EXISTS lancamento;
DROP TABLE IF EXISTS planocontas;

CREATE TABLE  planocontas (
  conta varchar(100) default NULL,
  descricao varchar(100) default NULL
);


CREATE TABLE  lancamento (
  idConta varchar(100) default NULL,
  valor double default NULL
);

insert into planocontas
select '1', 'ATIVO' union
select '101', 'ATIVO CIRCULANTE' union
select '10101', 'DISPONIBILIDADE' union
select '1010101', 'DISPONIBILIDADE' union
select '101010100002', 'CAIXA' union
select '1010102', 'CAIXA ESCONDIDO' union
select '101010200001', 'CAIXA 2';

insert into lancamento
select '101010100002', 700 union
select '101010200001', 300;

select
  pc.*,
  ( select sum(valor) from lancamento where left(idconta,length(pc.conta)) = pc.conta )  saldo
from
  planocontas pc;

Tive que criar uma função aqui, pois estou no postogresql…

tipo, deu certo…estranhamente, mas não consigo entender a consulta, como que ela funciona?
Fiz as adaptações aqui e funcionou… veja só como que ficou:

[code]select
pc.conta,pc.descricao,
( select sum(valor) from lancamento where left(idcontacredito,length(pc.conta)) = pc.conta ) Credito,
( select sum(valor) from lancamento where left(idcontadebito,length(pc.conta)) = pc.conta ) Debito,
( select sum(valor) from lancamento where left(idcontacredito,length(pc.conta)) = pc.conta ) - ( select sum(valor) from lancamento where left(idcontadebito,length(pc.conta)) = pc.conta ) Saldo

from
planocontas pc order by conta;[/code]

muito obrigado :slight_smile:

Uma função para fazer o que?
A função length() existe no Postgresql
E você pode usar substr() no lugar da left()

Estranhamente não… era para funcionar mesmo!..rs

O truque está naquele Where da subquery:

where left(idcontacredito,length(pc.conta)) = pc.conta

O que esse trecho faz? Ele vai procurar em lançamento todos os códigos que comecem com o mesmo valor da conta atual.
Por exemplo:

1 - Ativo: pc.conta = 1, logo: length(pc.conta) = 1
Seus dois lançamentos se encaixam pois todos começam com 1.

1010101 - DISPONIBILIDADE: pc.conta = 1010101, logo: length(pc.conta) = 7
Ou seja, todos lançamentos em que os sétimos primeiros dígitos sejam ‘1010101’, apenas o de 700 neste caso.

O select se aproveita que no código da conta já se encontra toda a hierarquia dela.

Um ponto que devo chamar atenção é que no quesito performance talvez não seja a melhor das soluções:

  • Ao utilizar uma function no where você praticamente mata a chance de utilizar indíces.
  • Você realiza a subquery 3 vezes com o mesmo where em lançamentos (que é uma tabela que tende a crescer muito com o tempo).
  • Talvez um Inner Join (ou Left, para trazer contas sem lançamento), teria melhor performance.
  • E, TALVEZ, o like permitisse que a consulta utilizasse indíce (já que o % só estaria do lado direito da expressão).

Outro ponto que chamaria a atenção é que no quesito clareza, montar o relatório com um “truque” de sql talvez não seja a melhor solução também.
Se for apenas para um relatório, usaria sem problemas.
Se precisar utilizar em outros lugares, eu isolaria esse sql numa View, numa Table Function, ou mesmo em código.

Entendi…
Eu criei a função left… então fiz atoa… na verdade foi burrice minha, mas fazer isso atrapalha na performance:

CREATE OR REPLACE FUNCTION LEFT(text, integer) RETURNS text AS $$ SELECT SUBSTRING($1 FOR $2); $$ LANGUAGE sql IMMUTABLE; GRANT ALL ON FUNCTION LEFT(text, integer) TO public;

É só para realizar um relatório mesmo,
então está tranquilo…

Muitíssimo obrigado cara :wink:
vlw de mais msmo!
Nao consegueria chegar a este resultado sozinho :slight_smile:
abração