以前の行の値と実際の行の値に基づいて行の値を計算します


9

みなさん、こんにちは。あなたの助けに感謝します。
私は次のような状況にあります。フィールドid(int)、stmnt_date(date)、debit(double)、credit(double)およびbalance(double) を含むステートメントと呼ばれるテーブル私のテーブルの構造

次のルールに従って残高を計算したい:

最初の行の残高(発生)=借方-貸方および残りの行

現在の行の残高=発生順に前の行の残高+現在の行の借方-現在の行の貸方

上の図からわかるように、行は日付順に並べられていません。そのため、この単語を時系列で2回使用して、stmnt_date値の重要性を強調しています。

手伝ってくれてありがとうございます。


借方フィールドと貸方フィールドを1つのフィールドに結合できますか?その場合、負の値を借方として使用し、正の値を貸方として使用できます。
マイク

1
今後の質問については(これは回答済みであるため)、印刷画面ではなくテキストでコードを投稿してください。CREATE TABLEステートメントとサンプルデータも含めます(を使用INSERT)。
ypercubeᵀᴹ

あなたの回答@ypercubeに敬意を表して、これを読んでいる人のために、CREATE TABLEとINSERTの例をdba.stackexchange.com/a/183207/131900の
Zack Morris

回答:


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の答えは非常に壮観です(そのようなダミーの選択を介した単一のクエリ内での変数の作成を見たことがありませんでした)。

Google画像検索の表形式のデータ画像の場合、https://convertio.co/ocr/またはhttps://ocr.space/を使用して、テキストドキュメントに変換できます。次に、OCRが列を適切に検出せず、Macを使用している場合は、オプションキーを押したままTextWranglerを使用して長方形の選択を実行し、列を移動します。Sequel ProやTextWranglerなどのSQLエディタとGoogleドキュメントなどのスプレッドシートを組み合わせることで、タブで区切られた表形式データを非常に効率的に処理できます。

これらすべてをコメントに含めることができるのであれば、この回答に反対票を投じないでください。

-- 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でこのタスクを処理するために、格納されたフィールドの「バランス」を計算するためにトリガー関数を使用することにしました。すべての計算は、行ごとに1回だけ行われます。

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では:

CTEを生成するには、with()ステートメントを使用します。これは基本的に、各行の値を表示する一時的な結果セットです。withステートメントで数式を使用して最後に列を作成し、数式を使用して行の合計がDEBIT-CREDITであることを示すことができます。withステートメントでは、各行に行番号を割り当てる必要があります。WITH()のOVER句を使用して、stmnt_date順に並べ替えます。

次に、a.ROWNUMBER = b.ROWNUMBER-1または+1を使用して、テーブルをそれ自体に再帰的に結合します。これにより、この行と前の行のa.total + b.total =合計を参照できます。

私はコードを提供していないことに感謝しますが、これはこれを実現するための実用的な方法です。リクエストがあればコードを提供できます:)


1
問題はMySQLについてです。CTEまたはそれを備えたDBMSのウィンドウ関数(Postgres、SQL-Server、DB2、Oracleなど)でこれを実行する方法のコードを提供することは(逆に)悪くないわけではありませんが、 MySQLでこれを行う方法に関するコードを少なくとも提供する必要があります。
ypercubeᵀᴹ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.