◆ SQL で日付から消費税を求めたい場合とかの書き方

やりたいこと

消費税が上がるとかでシステム作ってる人はどうこうというのをネットで見かけました
私は消費税が入るようなもの作ってないので大変だねーと他人事だったのですが データベースのデータで効率的に消費税求めるのってどうするんだろう?と思ったのでちょっと考えてみました
消費税は日付で決まるもので 過去のものは変わりません
なのでわざわざデータベースのカラムに入れるよりも日付から自動取得したいです

もちろん 計算で求められるものであっても高速化のために計算済みを入れるカラムを用意して保存するケースもありますが 今回はその辺は考えません
日付から求めるという条件です

これだと 消費税に限るものじゃなくて 「◯ 以上なら ✖ という情報の組み合わせから ◯ が ☆ のときに ✖ は何?」 という一般的な問題になります
今回の例だと ◯ に 2019-10-01 で ✖ に 10 という値です
これで ☆ (調べる対象) に 2019-10-20 をいれると 10 で 2018-12-30 をいれると 8 が返ってきてほしいです
ありがちな感じはしますが こういうのを SQL で書いた記憶がないです

SQL 力はないのです

個人的に DB はデータを永続化するためだけのもので 複雑な select 処理は基本的にしないです
世間のウェブサービスみたいに何百万何千万とかいう顧客情報持つとかとは全然違って DB の全サイズがせいぜい数十 MB 程度なものです
なので起動時に全部のデータを Node.js のメモリ上に載せて基本は変数上での処理です
処理中に永続化してるデータに変更が生じたときだけ DB に差分を更新するクエリを送ってます
それも DB アクセスは非同期なので DB アクセスによる待ち時間 0 で速度第一です
そうなると読み取りは 「select * from tabl1;」 みたいのを全テーブルに送るだけなのですよね
しかもこれだと RDB の表形式である必要はなくて JSON のまま保存や取り出しできたほうがいいくらいです
最近は MongoDB とかの人気が落ちてきてる気がするので PostgreSQL の jsonb 型にそのまま保存したりです

プログラムだと

やることをまとめると

threshold   point
12 1000
8 300
4 100
2 10
1 1
0 0

というデータがあった場合に

x >= 12 ? 1000:
x >= 8 ? 300:
x >= 4 ? 100
x >= 2 ? 10:
x >= 1 ? 1:
0

の結果が取得できれば良いです
ただ行数は不定で増えるかもしれないというものです

全部 select で持ってきて find メソッドでやりたくなります

SQL

SQL であれこれやってみると これでできました

select point from table where threshold <= {score} order by threshold desc limit 1

{score} に値を埋め込みます
上の表のように threshold で降順ソートした上で threshold が score 以下の最初のものを取得します

SQL 的な考えは普段しないので脳内がごちゃごちゃしてきますが 配列メソッド風にして考えるとわかりやすかったです
.NET Framework で LINQ というのがあるくらいですしね

JavaScript でやるとこういうイメージです

rows.sort((a, b) => b.threshold - a.threshold)
.filter(e => e.threshold <= score)[0].point

遅延評価と take ができる LINQ メソッドと違って JavaScript の filter は全部処理してしまうので最初の一つを取るなら find に置き換えたほうが無駄は少ないです

実際に実行してみます

CREATE TABLE public.test1
(
threshold integer NOT NULL,
point integer,
CONSTRAINT test1_pkey PRIMARY KEY (threshold)
);

INSERT INTO public.test1 VALUES
(12, 1000),
( 8, 300),
( 4, 100),
( 2, 10),
( 1, 1),
( 0, 0);

select point from test1 where threshold <= 15 order by threshold desc limit 1;
-- 1000
select point from test1 where threshold <= 9 order by threshold desc limit 1;
-- 300
select point from test1 where threshold <= 2 order by threshold desc limit 1;
-- 10