Consulta SQL - Saldo devedor
Estou com a seguinte consulta:
Essa consulta está trazendo as contas a receber, porém preciso abater os lançamentos dos pagamentos já realizados.
Os pagamentos estão na tabela SUBLANCA.
Como posso filtrar e abater os pagamentos de cada titulo a receber para encontrar o saldo devedor ?
select c.CODCABRECPAG,c.DATAVCTO, c.CODTRANSACAO, c.CODUNIDCLIFOR,u.NOME, c.VALOR
from CABRECPAG C
inner join UNIDCLIFOR U on u.CODUNIDCLIFOR=c.CODUNIDCLIFOR
where c.TIPODOC='R'
and C.DATAVCTO <= '25/12/2024'
and c.EFEITO in ('A-----', 'P0----')
and c.CODFILIAL=1
and u.codgrupoclifor <> 0
order by c.CODCABRECPAG
Essa consulta está trazendo as contas a receber, porém preciso abater os lançamentos dos pagamentos já realizados.
Os pagamentos estão na tabela SUBLANCA.
Como posso filtrar e abater os pagamentos de cada titulo a receber para encontrar o saldo devedor ?
Mylena
Curtidas 0
Melhor post
Arthur Heinrich
12/12/2024
Existem diversas formas de mostrar um extrato. Porém, sua pergunta inicial é como calcular o saldo devedor.
Na teoria, quando consideramos a contabilidade, o saldo devedor de um cliente é a soma de todos os seus débitos, menos os seus créditos.
Algo do tipo:
No seu caso, existe uma tabela diferente para débitos e créditos, o que complica um pouco:
Porém, tecnicamente falando, as queries do banco precisam ser eficientes. Imagine o esforço que o seu banco faria, toda a vez que precisar do saldo de um cliente e ele se deparar com um cliente há 10 anos, com múltiplos lançamentos ao dia. Escrever um sistema assim é colocar uma data para que ele falhe, pois a cada dia que passa, o esforço aumentará, até que não seja mais capaz de entregar o resultado no tempo necessário.
O ideal é que o seu sistema contenha uma tabela de saldos, onde você possa armazenar o saldo em datas específicas. Por exemplo, no último dia do mês, permitindo que este valor seja exibido como "Saldo Anterior", sem precisar somar uma grande quantidade de dados. Daí, você soma a ele (union all), os lançamentos do mês subsequente a este saldo, limitando o seu acesso a, no máximo, 30 dias.
Outro erro comum é querer escrever uma query única para múltiplos propósitos. Isto torna a query mais complexa do que o necessário e também consome recursos.
De uma maneira geral, bancos como o Oracle e o SQL Server, possuem recursos para escrever queries analíticas.
Ao fazer isso, você consegue retornar um conjunto de dados histórico, como se fosse um extrato de conta e pode adicionar uma função analítica para, a cada linha, retornar o saldo (soma de todos os lançamentos retornados desde a primeira linha, até a linha atual.
Na teoria, quando consideramos a contabilidade, o saldo devedor de um cliente é a soma de todos os seus débitos, menos os seus créditos.
Algo do tipo:
select sum(valor) saldo from lancamentos where cliente = ?
No seu caso, existe uma tabela diferente para débitos e créditos, o que complica um pouco:
select debitos - creditos as saldo from ( select sum(valor) debitos from tab_debitos where cliente = ? ) d, ( select sum(valor) creditos from tab_creditos where cliente = ? ) c
Porém, tecnicamente falando, as queries do banco precisam ser eficientes. Imagine o esforço que o seu banco faria, toda a vez que precisar do saldo de um cliente e ele se deparar com um cliente há 10 anos, com múltiplos lançamentos ao dia. Escrever um sistema assim é colocar uma data para que ele falhe, pois a cada dia que passa, o esforço aumentará, até que não seja mais capaz de entregar o resultado no tempo necessário.
O ideal é que o seu sistema contenha uma tabela de saldos, onde você possa armazenar o saldo em datas específicas. Por exemplo, no último dia do mês, permitindo que este valor seja exibido como "Saldo Anterior", sem precisar somar uma grande quantidade de dados. Daí, você soma a ele (union all), os lançamentos do mês subsequente a este saldo, limitando o seu acesso a, no máximo, 30 dias.
Outro erro comum é querer escrever uma query única para múltiplos propósitos. Isto torna a query mais complexa do que o necessário e também consome recursos.
De uma maneira geral, bancos como o Oracle e o SQL Server, possuem recursos para escrever queries analíticas.
Ao fazer isso, você consegue retornar um conjunto de dados histórico, como se fosse um extrato de conta e pode adicionar uma função analítica para, a cada linha, retornar o saldo (soma de todos os lançamentos retornados desde a primeira linha, até a linha atual.
GOSTEI 1
Mais Respostas
Mylena
12/12/2024
Montei da seguinte forma:
Porém o resultado está exibindo titulo a titulo, como posso agrupar o total por cliente ?
Cliente Joao: 5.000,00 a receber no total, já abateu 2.000,00, então o saldo total é de 3.000,00
WITH TIT AS (SELECT CABRECPAG.CODFILIAL, CABRECPAG.CODCABRECPAG, SUBLANCA.CODFILIALRECPAG CODFILIALORIG,
SUBLANCA.CODCABRECPAG CODTITULOORIG, RENCABRECPAG.CODRENCABRECPAG, CABRECPAG.CODFILIALPARCELA,
CABRECPAG.CODTRANSACAO, CABRECPAG.PROVISORIO
FROM CABRECPAG
LEFT JOIN RENCABRECPAG ON RENCABRECPAG.CODFILIALRECPAG=CABRECPAG.CODFILIAL AND RENCABRECPAG.CODCABRECPAG=CABRECPAG.CODCABRECPAG
LEFT JOIN SUBLANCA ON (SUBLANCA.CODLANCA = RENCABRECPAG.CODLANCA)
WHERE ((CABRECPAG.TIPODOC='R')
AND CABRECPAG.CODTRANSDEV IS NULL )
AND SUBSTRING(CABRECPAG.EFEITO,1,1) <> 'N'
AND CABRECPAG.CODFILIAL=1 AND (CABRECPAG.DATAVCTO<='15/12/2024')
AND (EXISTS(SELECT UNIDCLIFOR.CODUNIDCLIFOR
FROM UNIDCLIFOR UNIDCLIFOR, CLIFOR CLIFOR WHERE (UNIDCLIFOR.CODCLIFOR=CABRECPAG.CODCLIFOR) AND
(UNIDCLIFOR.CODUNIDCLIFOR=CABRECPAG.CODUNIDCLIFOR) AND ((UNIDCLIFOR.CODGRUPOCLIFOR is not null)) AND
(CLIFOR.CODCLIFOR=UNIDCLIFOR.CODCLIFOR))) ),
TITULOS AS (SELECT C.*, NULL CODRENCABRECPAG,NULL CODVENDE, NULL NOMEVENDE, NULL CODVENDEDOR, NULL NOMEVENDEDOR, NULL CODGER, NULL NOMEGER
FROM TIT T
INNER JOIN CABRECPAG C ON C.CODFILIAL = T.CODFILIAL AND C.CODCABRECPAG = T.CODCABRECPAG)
SELECT CABRECPAG_1.DATAVCTO AS VCTO, CABRECPAG_1.DOCUMENTO, CLIFOR_1.CODCLIFOR, UNIDCLIFOR_1.CODUNIDCLIFOR,UNIDCLIFOR_1.CODTIPOCLIFOR,
(CASE WHEN(ASCII(CABRECPAG_1.TIPODOC)-ASCII('R'))=0 THEN 1 ELSE -1 END) * CABRECPAG_1.VALOR AS VALOR,
SUM(CASE WHEN SUBLANCA_1.TIPO IN ('R','P','X','Y','S') THEN SUBLANCA_1.VLRMOEDA ELSE 0 END) / (COUNT(*) / COALESCE(NULLIF(COUNT(DISTINCT SUBLANCA_1.CODSUBLANCA),0),1)) AS VALORPG,
((CASE WHEN(ASCII(CABRECPAG_1.TIPODOC)-ASCII('R'))=0 THEN 1 ELSE -1 END) * CABRECPAG_1.VALOR) - SUM(CASE WHEN SUBLANCA_1.TIPO IN ('R','P','X','Y','S') THEN SUBLANCA_1.VLRMOEDA ELSE 0 END) / (COUNT(*) / COALESCE(NULLIF(COUNT(DISTINCT SUBLANCA_1.CODSUBLANCA),0),1)) AS SALDO
FROM TITULOS CABRECPAG_1 LEFT JOIN CLIFOR CLIFOR_1 ON CLIFOR_1.CODCLIFOR=CABRECPAG_1.CODCLIFOR
LEFT JOIN UNIDCLIFOR UNIDCLIFOR_1 ON CABRECPAG_1.CODCLIFOR=UNIDCLIFOR_1.CODCLIFOR AND CABRECPAG_1.CODUNIDCLIFOR=UNIDCLIFOR_1.CODUNIDCLIFOR
LEFT JOIN SUBLANCA SUBLANCA_1 ON SUBLANCA_1.CODFILIALRECPAG=CABRECPAG_1.CODFILIAL AND SUBLANCA_1.CODCABRECPAG=CABRECPAG_1.CODCABRECPAG AND SUBLANCA_1.DATA <= '13/12/2024'
LEFT JOIN LANCA LANCA_1 ON LANCA_1.CODLANCA=SUBLANCA_1.CODLANCA
LEFT JOIN EMPRESA EMPRESA_1 ON EMPRESA_1.CODEMPRESA=CABRECPAG_1.CODFILIAL
LEFT JOIN TIPOCLIFOR TIPOCLIFOR_1 ON UNIDCLIFOR_1.CODTIPOCLIFOR=TIPOCLIFOR_1.CODTIPOCLIFOR
LEFT JOIN GRUPOCLIFOR GRUPOCLIFOR_1 ON GRUPOCLIFOR_1.CODGRUPOCLIFOR=SUBSTRING(UNIDCLIFOR_1.CODGRUPOCLIFOR,1,4)
LEFT JOIN GRUPOCLIFOR GRUPOCLIFOR_2 ON GRUPOCLIFOR_2.CODGRUPOCLIFOR=SUBSTRING(UNIDCLIFOR_1.CODGRUPOCLIFOR,1,2)
LEFT JOIN CRSITUACAO CRSITUACAO_1 ON CRSITUACAO_1.CODCRSITUACAO = CABRECPAG_1.CODCRSITUACAO
GROUP BY
CABRECPAG_1.DATAVCTO, CABRECPAG_1.DOCUMENTO, CABRECPAG_1.VALOR,UNIDCLIFOR_1.CODTIPOCLIFOR,
CLIFOR_1.CODCLIFOR, CLIFOR_1.NOME, CLIFOR_1.CODCLIFOR,
UNIDCLIFOR_1.CODUNIDCLIFOR, CABRECPAG_1.CODFILIAL,
EMPRESA_1.NOMEFILIAL, CABRECPAG_1.TIPODOC,
SUBSTRING(CABRECPAG_1.EFEITO,1,1), CABRECPAG_1.NROBLOQUETO, CABRECPAG_1.NOSSONUMERO, UNIDCLIFOR_1.CODCOBRADOR,CLIFOR_1.ECLIENTE, CLIFOR_1.EFORNECED
HAVING ((COALESCE(ROUND(SUM(CASE WHEN SUBLANCA_1.TIPO IN ('R','P','X','Y','S') THEN SUBLANCA_1.VLRMOEDA ELSE 0 END),2),0) / (COUNT(*) / COALESCE(NULLIF(COUNT(DISTINCT
SUBLANCA_1.CODSUBLANCA),0),1))) + 0.001 < ROUND(CABRECPAG_1.VALOR,2)) OR (SUM(SUBLANCA_1.VLRMOEDA) IS NULL)
ORDER BY UNIDCLIFOR_1.CODUNIDCLIFOR
Porém o resultado está exibindo titulo a titulo, como posso agrupar o total por cliente ?
Cliente Joao: 5.000,00 a receber no total, já abateu 2.000,00, então o saldo total é de 3.000,00
GOSTEI 0
Arthur Heinrich
12/12/2024
Sua query está muito complexa para entender o que é relevante ou não para este resultado. Aparentemente há uma checagem, título a título, para considerar o título quitado e excluí-lo da relação. Então, não está fácil simplificar a sua query, mas dá para adicionar mais uma parte, para simplificar o resultado final.
with
tit as
( select
cabrecpag.codfilial, cabrecpag.codcabrecpag, sublanca.codfilialrecpag codfilialorig,
sublanca.codcabrecpag codtituloorig, rencabrecpag.codrencabrecpag, cabrecpag.codfilialparcela,
cabrecpag.codtransacao, cabrecpag.provisorio
from
cabrecpag
left join rencabrecpag
on rencabrecpag.codfilialrecpag=cabrecpag.codfilial and
rencabrecpag.codcabrecpag=cabrecpag.codcabrecpag
left join sublanca
on sublanca.codlanca = rencabrecpag.codlanca
where
( cabrecpag.tipodoc='R' and
cabrecpag.codtransdev is null ) and
substring(cabrecpag.efeito,1,1) <> 'N' and
cabrecpag.codfilial = 1 and
cabrecpag.datavcto <= '15/12/2024' and
( exists( select unidclifor.codunidclifor
from
unidclifor unidclifor,
clifor clifor
where
(unidclifor.codclifor=cabrecpag.codclifor) and
(unidclifor.codunidclifor=cabrecpag.codunidclifor) and
(unidclifor.codgrupoclifor is not null) and
(clifor.codclifor=unidclifor.codclifor) ) ) ),
titulos as
( select
c.*, null codrencabrecpag, null codvende, null nomevende, null codvendedor,
null nomevendedor, null codger, null nomeger
from
tit t
inner join cabrecpag c
on c.codfilial = t.codfilial and
c.codcabrecpag = t.codcabrecpag),
lanc_indiv as
( select
clifor_1.nome,
(case when cabrecpag_1.tipodoc = 'R' then 1 else -1 end) * cabrecpag_1.valor as debito,
sum(case when sublanca_1.tipo in ('R','P','X','Y','S') then sublanca_1.vlrmoeda else 0 end)/
(count(*) / coalesce(nullif(count(distinct sublanca_1.codsublanca),0),1)) as valorpg
from
titulos cabrecpag_1
left join clifor clifor_1
on clifor_1.codclifor=cabrecpag_1.codclifor
left join unidclifor unidclifor_1
on cabrecpag_1.codclifor=unidclifor_1.codclifor and
cabrecpag_1.codunidclifor=unidclifor_1.codunidclifor
left join sublanca sublanca_1
on sublanca_1.codfilialrecpag=cabrecpag_1.codfilial and
sublanca_1.codcabrecpag=cabrecpag_1.codcabrecpag and sublanca_1.data <= '13/12/2024'
left join lanca lanca_1
on lanca_1.codlanca=sublanca_1.codlanca
left join empresa empresa_1
on empresa_1.codempresa=cabrecpag_1.codfilial
left join tipoclifor tipoclifor_1
on unidclifor_1.codtipoclifor=tipoclifor_1.codtipoclifor
left join grupoclifor grupoclifor_1
on grupoclifor_1.codgrupoclifor=substring(unidclifor_1.codgrupoclifor,1,4)
left join grupoclifor grupoclifor_2
on grupoclifor_2.codgrupoclifor=substring(unidclifor_1.codgrupoclifor,1,2)
left join crsituacao crsituacao_1
on crsituacao_1.codcrsituacao = cabrecpag_1.codcrsituacao
group by
cabrecpag_1.datavcto, cabrecpag_1.documento, cabrecpag_1.valor,unidclifor_1.codtipoclifor,
clifor_1.codclifor, clifor_1.nome, clifor_1.codclifor,
unidclifor_1.codunidclifor, cabrecpag_1.codfilial,
empresa_1.nomefilial, cabrecpag_1.tipodoc,
substring(cabrecpag_1.efeito,1,1), cabrecpag_1.nrobloqueto, cabrecpag_1.nossonumero,
unidclifor_1.codcobrador,clifor_1.ecliente, clifor_1.eforneced
having
((coalesce(round(sum(case when sublanca_1.tipo in ('R','P','X','Y','S') then sublanca_1.vlrmoeda else 0 end),2),0)/
(count(*) / coalesce(nullif(count(distinctsublanca_1.codsublanca),0),1))) + 0.001 < round(cabrecpag_1.valor,2)) or (sum(sublanca_1.vlrmoeda) is null) )
select
nome,
sum(debito) debito,
sum(valorpg) pago,
sum(debito) - sum(valorpg) saldo
from lanc_indiv
group by nome
order by 1
GOSTEI 1
Mylena
12/12/2024
Deu certinho, muito obrigada
GOSTEI 0
Mylena
12/12/2024
estou com o seguinte comando:
Preciso somar somente os titulos, somente das transações que estejam com a situação = 3, porem essa informação está na tabela TRANSACAO e a chave de ligação é CODTRANSACAO
como posso fazer esse where para somar somente os titulos gerados pelas transações com a situação =3 ?
with
tit as
( select
cabrecpag.codfilial, cabrecpag.codcabrecpag, sublanca.codfilialrecpag codfilialorig,
sublanca.codcabrecpag codtituloorig, rencabrecpag.codrencabrecpag, cabrecpag.codfilialparcela, cabrecpag.codtransacao
from
cabrecpag
left join rencabrecpag on rencabrecpag.codfilialrecpag=cabrecpag.codfilial and rencabrecpag.codcabrecpag=cabrecpag.codcabrecpag
left join sublanca on sublanca.codlanca = rencabrecpag.codlanca
where
( cabrecpag.tipodoc='R' and cabrecpag.codtransdev is null ) and
cabrecpag.EFEITO in ('A-----', 'P0----') and
cabrecpag.codfilial = 1 and
cabrecpag.datavcto <= '25/01/2025' and
( exists( select unidclifor.codunidclifor
from
unidclifor unidclifor, clifor clifor
where
(unidclifor.codclifor=cabrecpag.codclifor) and
(unidclifor.codunidclifor=cabrecpag.codunidclifor) and
(unidclifor.codgrupoclifor is not null) and
(clifor.codclifor=unidclifor.codclifor) ) ) ),
titulos as
( select
c.*, null codrencabrecpag, null codvende, null nomevende, null codvendedor, null nomevendedor, null codger, null nomeger
from
tit t
inner join cabrecpag c on c.codfilial = t.codfilial and c.codcabrecpag = t.codcabrecpag),
lanc_indiv as
( select
unidclifor_1.CODUNIDCLIFOR, clifor_1.NOME,
(case when cabrecpag_1.tipodoc = 'R' then 1 else -1 end) * cabrecpag_1.valor as debito,
sum(case when sublanca_1.tipo in ('R','P','X','Y','S') then sublanca_1.vlrmoeda else 0 end)/
(count(*) / coalesce(nullif(count(distinct sublanca_1.codsublanca),0),1)) as valorpg
from
titulos cabrecpag_1
left join clifor clifor_1 on clifor_1.codclifor=cabrecpag_1.codclifor
left join unidclifor unidclifor_1 on cabrecpag_1.codclifor=unidclifor_1.codclifor and cabrecpag_1.codunidclifor=unidclifor_1.codunidclifor
left join sublanca sublanca_1 on sublanca_1.codfilialrecpag=cabrecpag_1.codfilial and sublanca_1.codcabrecpag=cabrecpag_1.codcabrecpag and sublanca_1.data <= '25/01/2025'
left join lanca lanca_1 on lanca_1.codlanca=sublanca_1.codlanca
left join empresa empresa_1 on empresa_1.codempresa=cabrecpag_1.codfilial
left join tipoclifor tipoclifor_1 on unidclifor_1.codtipoclifor=tipoclifor_1.codtipoclifor
left join grupoclifor grupoclifor_1 on grupoclifor_1.codgrupoclifor=substring(unidclifor_1.codgrupoclifor,1,4)
left join grupoclifor grupoclifor_2 on grupoclifor_2.codgrupoclifor=substring(unidclifor_1.codgrupoclifor,1,2)
left join crsituacao crsituacao_1 on crsituacao_1.codcrsituacao = cabrecpag_1.codcrsituacao
left join TRANSACAO transacao_1 on transacao_1.CODTRANSACAO = cabrecpag_1.CODTRANSACAO
group by
cabrecpag_1.datavcto, cabrecpag_1.documento, cabrecpag_1.valor,unidclifor_1.codtipoclifor,
clifor_1.codclifor, clifor_1.nome, clifor_1.codclifor, unidclifor_1.codunidclifor, cabrecpag_1.codfilial,
empresa_1.nomefilial, cabrecpag_1.tipodoc, substring(cabrecpag_1.efeito,1,1), cabrecpag_1.nrobloqueto, cabrecpag_1.nossonumero,
unidclifor_1.codcobrador,clifor_1.ecliente, clifor_1.eforneced
having
((coalesce(round(sum(case when sublanca_1.tipo in ('R','P','X','Y','S') then sublanca_1.vlrmoeda else 0 end),2),0)/
(count(*) / coalesce(nullif(count(distinct sublanca_1.codsublanca),0),1))) + 0.001 < round(cabrecpag_1.valor,2)) or (sum(sublanca_1.vlrmoeda) is null) )
select
CODUNIDCLIFOR, NOME, sum(debito) - sum(valorpg) TOTAL
from lanc_indiv
group by NOME,CODUNIDCLIFOR
order by nome
Preciso somar somente os titulos, somente das transações que estejam com a situação = 3, porem essa informação está na tabela TRANSACAO e a chave de ligação é CODTRANSACAO
como posso fazer esse where para somar somente os titulos gerados pelas transações com a situação =3 ?
GOSTEI 0