이전 및 실제 행 값을 기반으로 행 값 계산


9

여러분 안녕하세요, 도와 주셔서 감사합니다.
나는 id (int), stmnt_date (date), debit (double), credit (double) 및 balance (double) 필드를 포함하는 statement라는 테이블이 있습니다.내 테이블의 구조

다음 규칙에 따라 잔액을 계산하고 싶습니다.

첫 번째 행 잔액 ( 시간순 ) = 차변 대변 및 나머지 행

현재 행 잔액 = 시간순으로 이전 행 잔액 + 현재 행 차변-현재 행 크레딧

위 그림에서 알 수 있듯이 행은 날짜별로 정렬되지 않았으므로 stmnt_date 값의 중요성을 강조하기 위해 시간순으로 단어를 두 번 사용했습니다.

당신의 도움을 주셔서 대단히 감사합니다.


직불 및 신용 필드를 하나의 필드로 결합 할 수 있습니까? 그렇다면 음수 값을 차변으로 사용하고 양수 값을 신용으로 사용할 수 있습니다.
— Mike

1
향후 질문 (답변이 있기 때문에)은 인쇄 화면이 아닌 텍스트로 코드를 게시하십시오. 또한 CREATE TABLE문과 샘플 데이터를 포함합니다 (with INSERT).
— ypercubeᵀᴹ

당신의 응답 @ypercube에 경의,이 I를 읽는 사람을 위해 아래 표와 INSERT 예를 CREATE 추가 dba.stackexchange.com/a/183207/131900
— 잭 모리스

답변:


8

그 가정 stmnt_date이 UNIQUE제약이 창 / 분석 기능을 매우 쉽게 될 것이다 :

SELECT 
    s.stmnt_date, s.debit, s.credit,
    SUM(s.debit - s.credit) OVER (ORDER BY s.stmnt_date
                                  ROWS BETWEEN UNBOUNDED PRECEDING
                                           AND CURRENT ROW)
        AS balance
FROM
    statements AS s
ORDER BY
    stmnt_date ;

불행히도 MySQL은 아직 분석 기능을 구현하지 않았습니다. 엄격한 SQL, 테이블을 자체 조인 (100 % 작업하더라도 다소 비효율적 임)하거나 특정 MySQL 기능인 변수 (매우 효율적이지만 테스트해야 함)를 사용하여 문제를 해결할 수 있습니다. mysql을 업그레이드 할 때 결과가 여전히 정확하고 최적화 개선으로 인해 엉망이되지 않도록하십시오)

SELECT 
    s.stmnt_date, s.debit, s.credit,
    @b := @b + s.debit - s.credit AS balance
FROM
    (SELECT @b := 0.0) AS dummy 
  CROSS JOIN
    statements AS s
ORDER BY
    stmnt_date ;

데이터를 사용하면 다음과 같은 결과가 발생합니다.

+------------+-------+--------+---------+
| stmnt_date | debit | credit | balance |
+------------+-------+--------+---------+
| 2014-05-15 |  3000 |      0 |    3000 |
| 2014-06-17 | 20000 |      0 |   23000 |
| 2014-07-16 |     0 |   3000 |   20000 |
| 2014-08-14 |     0 |   3000 |   17000 |
| 2015-02-01 |  3000 |      0 |   20000 |
+------------+-------+--------+---------+
5 rows in set (0.00 sec)

6

나는 당신이 다음을 시도 할 수 있다고 생각합니다.

set @balance := 0;

SELECT stmnt_date, debit, credit, (@balance := @balance + (debit - credit)) as Balance
FROM statements
ORDER BY stmnt_date;

2

ypercube의 답변 은 매우 훌륭합니다 (나는 더미 선택을 통해 단일 쿼리 내에서 변수 생성을 본 적이 없었습니다). 여기에 편의를 위해 CREATE TABLE 문이 있습니다.

Google 이미지 검색의 표 형식 데이터 이미지의 경우 https://convertio.co/ocr/ 또는 https://ocr.space/ 를 사용 하여 텍스트 문서로 변환 할 수 있습니다. 그런 다음 OCR이 열을 제대로 감지하지 못하고 Mac이있는 경우 옵션 키를 누른 상태에서 TextWrangler 를 사용하여 사각형을 선택 하고 열을 이동하십시오. Sequel Pro , TextWrangler와 같은 SQL 편집기와 Google Docs 와 같은 스프레드 시트를 조합 하면 탭으로 구분 된 표 형식 데이터를 매우 효율적으로 처리 할 수 ​​있습니다.

이 모든 것을 의견에 넣을 수 있다면이 답변을 찬성하지 마십시오.

-- DROP TABLE statements;

CREATE TABLE IF NOT EXISTS statements (
  id integer NOT NULL AUTO_INCREMENT,
  stmnt_date date,
  debit integer not null default 0,
  credit integer not null default 0,
  PRIMARY KEY (id)
);

INSERT INTO statements
(stmnt_date  , debit, credit) VALUES
('2014-06-17', 20000, 0     ),
('2014-08-14', 0    , 3000  ),
('2014-07-16', 0    , 3000  ),
('2015-02-01', 3000 , 0     ),
('2014-05-15', 3000 , 0     );

-- this is slightly modified from ypercube's (@b := 0 vs @b := 0.0)
SELECT 
    s.stmnt_date, s.debit, s.credit,
    @b := @b + s.debit - s.credit AS balance
FROM
    (SELECT @b := 0) AS dummy 
CROSS JOIN
    statements AS s
ORDER BY
    stmnt_date ASC;

/* result
+------------+-------+--------+---------+
| stmnt_date | debit | credit | balance |
+------------+-------+--------+---------+
| 2014-05-15 |  3000 |      0 |    3000 |
| 2014-06-17 | 20000 |      0 |   23000 |
| 2014-07-16 |     0 |   3000 |   20000 |
| 2014-08-14 |     0 |   3000 |   17000 |
| 2015-02-01 |  3000 |      0 |   20000 |
+------------+-------+--------+---------+
5 rows in set (0.00 sec)
*/

1

자체 조인 테이블 큰 테이블에서는 그리 빠르지 않습니다. PostgreSQL 에서이 작업을 처리하기 위해 저장된 필드 "밸런스"를 계산하기 위해 트리거 기능을 사용하기로 결정했습니다. 모든 계산은 각 행마다 한 번만 발생합니다.

DROP TABLE IF EXISTS statements;

CREATE TABLE IF NOT EXISTS statements (
  id BIGSERIAL,
  stmnt_date TIMESTAMP,
  debit NUMERIC(18,2) not null default 0,
  credit NUMERIC(18,2) not null default 0,
  balance NUMERIC(18,2)
);

CREATE OR REPLACE FUNCTION public.tr_fn_statements_balance()
RETURNS trigger AS
$BODY$
BEGIN

    UPDATE statements SET
    balance=(SELECT SUM(a.debit)-SUM(a.credit) FROM statements a WHERE a.stmnt_date<=statements.stmnt_date)
    WHERE stmnt_date>=NEW.stmnt_date;

RETURN NULL;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE TRIGGER tr_statements_after_update
  AFTER INSERT OR UPDATE OF debit, credit
  ON public.statements
  FOR EACH ROW
  EXECUTE PROCEDURE public.tr_fn_statements_balance();


INSERT INTO statements
(stmnt_date  , debit, credit) VALUES
('2014-06-17', 20000, 0     ),
('2014-08-14', 0    , 3000  ),
('2014-07-16', 0    , 3000  ),
('2015-02-01', 3000 , 0     ),
('2014-05-15', 3000 , 0     );


select * from statements order by stmnt_date;

-1

예를 들어, MSSQL에서 :

with () 문을 사용하여 CTE를 생성하십시오. 이것은 본질적으로 각 행의 값을 표시하는 임시 결과 집합입니다. with 문에서 math를 사용하여 마지막에 열을 만들 수 있으며 math를 사용하여 행의 총계가 DEBIT-CREDIT임을 표시 할 수 있습니다. with 문에서 각 행에 행 번호를 지정하고 WITH ()의 OVER 절을 사용하여 stmnt_date로 정렬하십시오.

그런 다음 a.ROWNUMBER = b.ROWNUMBER-1 또는 +1을 사용하여이 행과 이전 행의 a.total + b.total = 총계를 참조 할 수 있도록 테이블을 재귀 적으로 결합합니다.

코드를 제공하지 않아서 고맙지 만 이것이 달성하기위한 실용적인 방법입니다. 요청하면 코드를 제공 할 수 있습니다 :)


1
문제는 MySQL에 관한 것입니다. Postgres, SQL-Server, DB2, Oracle 등과 같은 DBMS에서 CTE 또는 창 기능 으로이 작업을 수행하는 방법에 대한 코드를 제공하는 것은 나쁘지 않지만 (목록이 길다) 적어도 MySQL 에서이 작업을 수행하는 방법에 대한 코드를 제공해야합니다.
— ypercubeᵀᴹ
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.