GaiaEx AcademyGaiaEx Academy
ധനകാര്യ ഡേറ്റ വെയർഹൌസുകൾക്കുള്ള SQL
ഡെവലപ്പർപ്രോഗ്രാമിംഗ്9 min read

ധനകാര്യ ഡേറ്റ വെയർഹൌസുകൾക്കുള്ള SQL

ഘടനാപരമായ ധനകാര്യ ഡേറ്റ ക്വറി ചെയ്യുക, അഗ്രിഗേറ്റ് ചെയ്യുക, വിശകലനം ചെയ്യുക

പോസ്റ്റുകൾ പങ്കിടുക

ഫിനാൻഷ്യൽ ഡാറ്റയുടെ ഭാഷ SQL ആയിരിക്കുന്നത് എന്തുകൊണ്ട്

ഓരോ ട്രേഡും, ഓരോ tick-ഉം, ഓരോ order book അപ്ഡേറ്റും — ഫിനാൻഷ്യൽ മാർക്കറ്റുകൾ വളരെ വലിയ അളവ് structured ഡാറ്റ ഉണ്ടാക്കുന്നു. നാല് ദശാബ്ദത്തിലധികമായി, SQL (Structured Query Language) ആ ഡാറ്റ query ചെയ്യാനും, transform ചെയ്യാനും, analyze ചെയ്യാനും ഉള്ള പ്രധാന tool ആണ്. ഇത് glamorous അല്ല. trendy അല്ല. പക്ഷേ ഒരു portfolio manager "കഴിഞ്ഞ Tuesday 2 PM മുതൽ 3 PM UTC വരെ ETH ട്രേഡുകളിലെ എന്റെ average fill price എന്തായിരുന്നു?" എന്ന് ചോദിക്കുമ്പോൾ, ഉത്തരം SQL-ൽ നിന്നാണ് വരുന്നത്.

ഫിനാൻഷ്യൽ ഡാറ്റ അന്തർലീനമായി relational ആണ്. ഒരു ട്രേഡ് ഒരു ഓർഡറിനെ റെഫർ ചെയ്യുന്നു, അത് ഒരു account-നെ, അത് ഒരു user-ന് belong ചെയ്യുന്നു. Positions instrument-കളുമായി ബന്ധപ്പെട്ടിരിക്കുന്നു; instruments market-കളുമായി ബന്ധപ്പെട്ടിരിക്കുന്നു. relational model — rows, columns, foreign keys വഴി connect ചെയ്ത tables — ഈ ബന്ധങ്ങളിലേക്ക് സ്വാഭാവികമായി map ചെയ്യുന്നു. അതുകൊണ്ടാണ് PostgreSQL, MySQL, SQL Server പോലുള്ള relational database-കൾ Goldman Sachs മുതൽ GaiaEx വരെ ഫിനാൻഷ്യൽ ഇൻഫ്രാസ്ട്രക്ചറിന്റെ backbone ആയി തുടരുന്നത്.

SQL-ന്റെ ശക്തി അതിന്റെ declarative സ്വഭാവത്തിലാണ്: നിങ്ങൾക്ക് എന്ത് വേണം എന്ന് വിവരിക്കുന്നു, എങ്ങനെ അത് ലഭിക്കണം എന്നല്ല. Database engine optimal execution plan കണ്ടെത്തുന്നു. നിങ്ങളുടെ trade history table-ന് 500 മില്യൺ rows ഉള്ളപ്പോൾ, ഒരു സെക്കൻഡിന് താഴെ results ആവശ്യമുള്ളപ്പോൾ ഇത് വളരെ പ്രധാനമാണ്. ശരിയായ query എഴുതുക, ശരിയായ index ബിൽഡ് ചെയ്യുക, SQL ഡെലിവർ ചെയ്യും — ഒരു local PostgreSQL instance-ലോ cloud-scale data warehouse-ലോ run ചെയ്യുന്നത് ആയാലും.

Relational model (simplified) users user_id PK accounts user_id FK → users tier, limits … trades user_id FK → accounts symbol, price, time … JOINs follow foreign keys — the shape of finance data is already relational.
Trade-കൾ accounts-ലും users-ലും തൂങ്ങിക്കിടക്കുന്നു: അതേ links തന്നെ നിങ്ങളുടെ SQL JOIN-കൾ traverse ചെയ്യുന്നു.

SELECT, JOIN, WHERE: Trade Data Query ചെയ്യൽ

ഒരു concrete schema-യിൽ നിന്ന് ആരംഭിക്കാം. GaiaEx പോലുള്ള ഒരു പ്ലാറ്റ്ഫോമിലെ trades table സങ്കൽപ്പിക്കുക:

CREATE TABLE trades (
    trade_id    BIGINT PRIMARY KEY,
    user_id     INT NOT NULL,
    symbol      VARCHAR(20) NOT NULL,
    side        VARCHAR(4) NOT NULL,  -- 'buy' or 'sell'
    price       NUMERIC(18,8) NOT NULL,
    quantity    NUMERIC(18,8) NOT NULL,
    fee         NUMERIC(18,8) DEFAULT 0,
    executed_at TIMESTAMPTZ NOT NULL
);

ഏറ്റവും അടിസ്ഥാനപരമായ operation filtering-ഓടെയുള്ള SELECT statement ആണ്. കഴിഞ്ഞ 24 മണിക്കൂറിൽ $3,000-ന് മുകളിലുള്ള എല്ലാ ETH-USD buy trades കണ്ടെത്താൻ:

SELECT trade_id, price, quantity, executed_at
FROM trades
WHERE symbol = 'ETH-USD'
  AND side = 'buy'
  AND price > 3000
  AND executed_at > NOW() - INTERVAL '24 hours'
ORDER BY executed_at DESC;

JOIN-കൾ ബന്ധപ്പെട്ട table-കൾ connect ചെയ്യുന്നു. നിങ്ങൾക്ക് ഒരു accounts table ഉണ്ടെന്നും, account tier അനുസരിച്ച് trading volume കാണണമെന്നും കരുതുക:

SELECT a.tier, COUNT(*) AS trade_count,
       SUM(t.price * t.quantity) AS total_volume
FROM trades t
JOIN accounts a ON t.user_id = a.user_id
WHERE t.executed_at > NOW() - INTERVAL '30 days'
GROUP BY a.tier
ORDER BY total_volume DESC;

ഈ query-കൾ ഫിനാൻഷ്യൽ analyst-കൾ, risk team-കൾ, compliance officer-കൾ എന്നിവരുടെ ദിവസേനയുള്ള റൊട്ടിയാണ്. അവ master ചെയ്താൽ നിങ്ങളുടെ ഡാറ്റയിൽ അടങ്ങിയിരിക്കുന്ന ഏത് ചോദ്യത്തിനും ഉത്തരം കണ്ടെത്താം.

Window Function-കൾ: Analyst-ന്റെ Secret Weapon

Window function-കൾ ആണ് ഫിനാൻസിൽ SQL beginner-കളെയും SQL practitioner-കളെയും വേർതിരിക്കുന്നത്. Result ഒരു single aggregated value-ലേക്ക് collapse ചെയ്യാതെ, current row-മായി ബന്ധപ്പെട്ട rows-ന്റെ ഒരു set-ൽ calculations perform ചെയ്യാൻ അവ അനുവദിക്കുന്നു. നിങ്ങളുടെ ഡാറ്റയിലുടനീളമുള്ള "running computations" ആയി അവയെ കരുതുക.

ഫിനാൻഷ്യൽ analysis-ൽ ഏറ്റവും കൂടുതൽ ഉപയോഗിക്കുന്ന window function-കൾ:

  • ROW_NUMBER() — ഒരു partition-നുള്ളിൽ ഓരോ row-ക്കും sequential integer assign ചെയ്യുന്നു. Deduplication-ന് അല്ലെങ്കിൽ ഓരോ symbol-ന്റെയും Nth trade select ചെയ്യാൻ ഉപകാരപ്രദമാണ്.
  • LAG(), LEAD() — previous അല്ലെങ്കിൽ next row-ന്റെ value access ചെയ്യുന്നു. trade-to-trade returns compute ചെയ്യാനോ time-series ഡാറ്റയിലെ gap-കൾ detect ചെയ്യാനോ അത്യാവശ്യമാണ്.
  • SUM() OVER () — Running totals. Cumulative volume, cumulative P&L, അല്ലെങ്കിൽ running position size compute ചെയ്യുന്നു.
  • AVG() OVER (ROWS BETWEEN) — SQL-ൽ നേരിട്ട് moving average-കൾ. Python ആവശ്യമില്ല.

ഒരു practical ഉദാഹരണം — BTC-ക്ക് trade-to-trade price change-ഉം running position-ഉം compute ചെയ്യുന്നു:

SELECT executed_at, price, quantity, side,
       price - LAG(price) OVER (ORDER BY executed_at) AS price_change,
       SUM(CASE WHEN side = 'buy' THEN quantity ELSE -quantity END)
           OVER (ORDER BY executed_at) AS running_position
FROM trades
WHERE symbol = 'BTC-USD'
ORDER BY executed_at;

Window function-കൾ നിങ്ങളുടെ row count കുറയ്ക്കുന്നില്ല — GROUP BY-ക്ക് വിരുദ്ധമായി, ഓരോ input row-ഉം ഒരു output row ഉണ്ടാക്കുന്നു. Granularity നഷ്ടപ്പെടാതെ detailed trade log-കളിലേക്ക് analytical column-കൾ ചേർക്കാൻ ഇത് perfect ആക്കുന്നു. നിങ്ങളുടെ ഡാറ്റ "summarize" ചെയ്യുന്നതിന് പകരം "enrich" ചെയ്യുന്നത് പോലെ അവയെ കരുതുക.

OHLCV aggregation-ന് — candlestick chart-കളുടെ അടിസ്ഥാനം — GROUP BY time-bucketing-ഓടെയും standard aggregate-കളോടെയും combine ചെയ്യാം:

SELECT date_trunc('hour', executed_at) AS bucket,
       (ARRAY_AGG(price ORDER BY executed_at))[1] AS open,
       MAX(price) AS high,
       MIN(price) AS low,
       (ARRAY_AGG(price ORDER BY executed_at DESC))[1] AS close,
       SUM(quantity) AS volume
FROM trades
WHERE symbol = 'ETH-USD'
GROUP BY bucket
ORDER BY bucket;

CTE-കൾ: സങ്കീർണ്ണമായ Analytical Query-കൾ Compose ചെയ്യൽ

Common Table Expressions (CTE-കൾ) ഒരു സങ്കീർണ്ണമായ query-യെ named, readable stage-കളായി — പ്രോഗ്രാമിംഗിലെ function-കൾ പോലെ — ബ്രേക്ക് ചെയ്യാൻ അനുവദിക്കുന്നു. WITH keyword-ഓടെ അവ introduce ചെയ്യപ്പെടുന്നു, sequentially പരസ്പരം refer ചെയ്യാൻ കഴിയും.

നിങ്ങളുടെ top 10 ഏറ്റവും profitable trading days identify ചെയ്യണം എന്ന് കരുതുക, പക്ഷേ profitability ഫീസ് ഉൾപ്പെടെയുള്ള ദിവസേനയുള്ള net P&L compute ചെയ്യേണ്ടതുണ്ട്. CTE-കൾ ഉപയോഗിച്ച്, ഇത് step by step ബിൽഡ് ചെയ്യാം:

WITH daily_trades AS (
    SELECT DATE(executed_at) AS trade_date,
           SUM(CASE WHEN side = 'sell' THEN price * quantity
                    ELSE -price * quantity END) AS gross_pnl,
           SUM(fee) AS total_fees
    FROM trades
    WHERE user_id = 42
    GROUP BY DATE(executed_at)
),
daily_pnl AS (
    SELECT trade_date,
           gross_pnl - total_fees AS net_pnl,
           SUM(gross_pnl - total_fees) OVER (ORDER BY trade_date) AS cumulative_pnl
    FROM daily_trades
)
SELECT trade_date, net_pnl, cumulative_pnl
FROM daily_pnl
ORDER BY net_pnl DESC
LIMIT 10;

ഓരോ CTE-യും ഒരു paragraph പോലെ വായിക്കുന്നു. daily_trades raw trades-നെ ദിവസേനയുള്ള summary-കളിലേക്ക് aggregate ചെയ്യുന്നു. daily_pnl net P&L-ഉം ഒരു running total-ഉം compute ചെയ്യുന്നു. അവസാന SELECT top 10 ദിവസങ്ങൾ പിക്ക് ചെയ്യുന്നു. ഇത് ഒരു single monolithic subquery-യുമായി compare ചെയ്യുക — CTE version maintainable, testable, self-documenting ആണ്.

Production ഫിനാൻഷ്യൽ സിസ്റ്റങ്ങളിൽ, CTE-കൾ regulatory reporting-ന് (accounts-കളിലുടനീളം margin requirements aggregate ചെയ്യുന്നത്) മുതൽ real-time dashboard query-കൾക്ക് (GaiaEx-ൽ rolling 24-hour volume compute ചെയ്യുന്നത്) വരെ എല്ലാ കാര്യത്തിനും ഉപയോഗിക്കുന്നു. well-factored കോഡ് compose ചെയ്യുന്ന അതേ രീതിയിൽ അവ compose ചെയ്യുന്നു — ഓരോ ലെയറും മുൻപത്തേതിന്റെ മുകളിൽ ബിൽഡ് ചെയ്യുന്നു.

Time-Series Performance-നുള്ള Indexing-ഉം Partitioning-ഉം

ഒരു query അതിന് പിന്തുണ നൽകുന്ന index-ത്തോളം മാത്രമേ വേഗതയുള്ളതാകൂ. ശരിയായ index ഇല്ലാതെ, ഒരു ലളിതമായ WHERE clause പോലും table-ലെ ഓരോ row-ഉം read ചെയ്യുന്ന sequential scan-ന് നിർബന്ധിക്കുന്നു. 500 മില്യൺ rows-ൽ, അത് മില്ലിസെക്കൻഡുകൾക്ക് പകരം മിനിറ്റുകളാണ്.

ഫിനാൻഷ്യൽ time-series ഡാറ്റയ്ക്ക്, ഏറ്റവും critical index pattern (symbol, executed_at)-ൽ ഒരു composite B-tree index ആണ്:

CREATE INDEX idx_trades_symbol_time
ON trades (symbol, executed_at DESC);

ഈ single index മുൻഭാഗം analytical query-കൾ accelerate ചെയ്യുന്നു: "കഴിഞ്ഞ ഒരു മണിക്കൂറിലെ എല്ലാ ETH trades", "രണ്ട് timestamp-കൾക്കിടയിലുള്ള BTC trades", അല്ലെങ്കിൽ "ഓരോ symbol-ന്റെയും ഏറ്റവും പുതിയ trade". column order പ്രധാനമാണ് — symbol ആദ്യം equality filtering enable ചെയ്യുന്നു, എന്നിട്ട് executed_at index-ന്റെ ആ partition-നുള്ളിൽ efficient range scan-കൾ enable ചെയ്യുന്നു.

ഹൺഡ്രഡ്സ് ഓഫ് മില്യൺസ് rows-നപ്പുറം വളരുന്ന table-കൾക്ക്, table partitioning അത്യാവശ്യമാണ്. PostgreSQL range അനുസരിച്ച് declarative partitioning support ചെയ്യുന്നു — time-series-ന് perfect:

CREATE TABLE trades (
    trade_id BIGINT, symbol VARCHAR(20),
    price NUMERIC(18,8), executed_at TIMESTAMPTZ
) PARTITION BY RANGE (executed_at);

CREATE TABLE trades_2026_q1 PARTITION OF trades
    FOR VALUES FROM ('2026-01-01') TO ('2026-04-01');
CREATE TABLE trades_2026_q2 PARTITION OF trades
    FOR VALUES FROM ('2026-04-01') TO ('2026-07-01');

Partitioning-ഓടെ, 2026 മാർച്ചിലെ ഡാറ്റയ്ക്കുള്ള ഒരു query Q1 partition മാത്രമേ scan ചെയ്യൂ — engine Q2, Q3, Q4 പൂർണ്ണമായി skip ചെയ്യുന്നു. Partition pruning എന്ന് വിളിക്കുന്ന ഈ technique, വലിയ historical ഡാറ്റാസെറ്റുകളിൽ order-of-magnitude speedup-കൾ ഡെലിവർ ചെയ്യുന്നു.

Time-range partition pruning Q1 2026 Jan–Mar rows Q2 2026 skipped Q3 2026 Q4 2026 Query: WHERE executed_at in March → planner reads only Q1 Composite index (symbol, executed_at) still helps within each partition
Partition-കൾ time range-കൾ ബൗണ്ട് ചെയ്യുന്നു; pruning match ചെയ്യാൻ കഴിയാത്ത മുഴുവൻ chunk-കളും skip ചെയ്യുന്നു.

PostgreSQL vs ClickHouse vs BigQuery: ശരിയായ Engine തിരഞ്ഞെടുക്കൽ

എല്ലാ database-കളും ഫിനാൻഷ്യൽ analytics ഒരുപോലെ handle ചെയ്യുന്നില്ല. തിരഞ്ഞെടുപ്പ് നിങ്ങളുടെ query pattern-കൾ, ഡാറ്റ volume, latency requirements എന്നിവയെ ആശ്രയിക്കുന്നു.

PostgreSQL ആണ് workhorse. ഇത് transactional workload-കളിൽ (real time-ൽ trades record ചെയ്യുന്നത്) excel ചെയ്യുന്നു, ACID guarantee-കൾ support ചെയ്യുന്നു, സങ്കീർണ്ണമായ JOIN-കൾ gracefully handle ചെയ്യുന്നു, ശരിയായ indexing, partitioning-ഓടെ ഹൺഡ്രഡ്സ് ഓഫ് മില്യൺസ് rows-ലേക്ക് സ്കെയിൽ ചെയ്യുന്നു. operational database-കൾക്ക് — system of record-ന് — ഇത് ശരിയായ തിരഞ്ഞെടുപ്പാണ്. ഉദാഹരണത്തിന് GaiaEx, ഫിനാൻഷ്യൽ സിസ്റ്റങ്ങൾ ആവശ്യപ്പെടുന്ന reliability guarantee-കളോടെ അതിന്റെ core trading data-ക്ക് PostgreSQL-compatible ഇൻഫ്രാസ്ട്രക്ചർ ആശ്രയിക്കുന്നു.

ClickHouse ബില്യൺ കണക്കിന് rows-ൽ analytical query-കൾക്കായി ഡിസൈൻ ചെയ്ത ഒരു columnar database ആണ്. PostgreSQL row by row ഡാറ്റ സ്റ്റോർ ചെയ്യുന്നിടത്ത് (individual trades insert ചെയ്യാൻ മികച്ചത്), ClickHouse column by column ഡാറ്റ സ്റ്റോർ ചെയ്യുന്നു (ദശലക്ഷക്കണക്കിന് rows-ലുടനീളം ഒരു single column aggregate ചെയ്യാൻ മികച്ചത്). "3 വർഷത്തിനുള്ളിൽ BTC-യുടെ average hourly volume" പോലുള്ള ഒരു query PostgreSQL-ൽ 30 സെക്കൻഡ് എടുക്കുന്നത് ClickHouse-ൽ 200 മില്ലിസെക്കൻഡിൽ പൂർത്തിയാകാം. trade-off: ClickHouse UPDATE-യോ DELETE-യോ efficiently support ചെയ്യുന്നില്ല — ഇത് ഡിസൈൻ പ്രകാരം append-only ആണ്.

BigQuery (Google Cloud) ഒരു serverless columnar warehouse ആണ്. Manage ചെയ്യേണ്ട ഇൻഫ്രാസ്ട്രക്ചർ ഇല്ല, pay-per-query pricing, petabyte-scale capacity. ad-hoc analytics, research, exploration എന്നിവയ്ക്ക് ideal ആണ് — പക്ഷേ query latency സെക്കൻഡുകളിലാണ് അളക്കുന്നത്, മില്ലിസെക്കൻഡിലല്ല, real-time applications-ന് ഇത് unsuitable ആക്കുന്നു.

  • Real-time trade recording, order management → PostgreSQL
  • ബില്യൺ കണക്കിന് rows-ലുടനീളമുള്ള historical analytics, backtesting → ClickHouse
  • Ad-hoc exploration, cross-team data sharing → BigQuery

പല professional trading firm-കളും ഒരു tiered architecture-ൽ മൂന്നും ഉപയോഗിക്കുന്നു: hot operational store ആയി PostgreSQL, warm analytical layer ആയി ClickHouse, cold archive ആയി BigQuery. നല്ല SQL എഴുതുന്ന skill ഇവയിലുടനീളം transfer ചെയ്യുന്നു — syntax 90% identical ആണ്, analytical thinking 100% ഒരേപോലെയാണ്.