Consulta SQL - Saldo devedor

SQL Server

SQL

Banco de Dados

12/12/2024

Estou com a seguinte consulta:

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

Mylena

Curtidas 0

Melhor post

Arthur Heinrich

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:

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

Mylena

12/12/2024

Montei da seguinte forma:

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

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

Mylena

12/12/2024

Deu certinho, muito obrigada
GOSTEI 0
POSTAR