A common ML pipeline trains a model in Python, saves it, starts a model server, and moves data between that server and a database.

This works, but it adds network calls and another service to maintain. I built SQLGBM for cases where that service is not needed.

How gradient boosting works

Gradient boosting builds decision trees in sequence. Each new tree learns from the errors left by the trees before it.

The model starts with a base score and adds the output of each tree. For binary classification, a function such as the sigmoid turns the final score into a probability.

A base score and successive decision trees summed into a final prediction
Each small tree corrects residual errors; their outputs add up to the prediction.

The training math is complex, but inference is simpler: each tree is a set of if-then rules.

The insight

For one row, each tree follows one decision path to a leaf. The model then adds the leaf values from all trees.

SQL can express the same logic with nested CASE statements. This means the database can run the model where the data already lives.

The toy example below keeps both representations visible. Change the row, and the highlighted path, leaf value, SQL branch, and probability all update together.

Interactive example

Follow one row from tree to SQL

Input row

Tree output+0.65
Final probability59.9%

42 > 35 → 68 ≤ 90 → leaf +0.65

A decision tree converted to SQLThe highlighted route changes with the age and income controls. The current route ends at a leaf value of positive 0.65.age ≤ 35income ≤ 55income ≤ 90−0.80+0.35+0.65−0.15
One tree from an ensemble. Active nodes show the path taken by the current row.
Generated SQL sigmoid(−0.25 + 0.65) = 59.9%
1 / (1 + EXP(-(
  -0.25 +
  CASE
    WHEN age <= 35 THEN
      CASE WHEN income <= 55 THEN -0.80
           ELSE 0.35 END
    ELSE
      CASE WHEN income <= 90 THEN 0.65
           ELSE -0.15 END
  END
))) AS probability

A real boosted model repeats this traversal for every tree, adds all the leaf values to the base score, and applies the model’s link function. The generated query is larger, but the mapping is the same.

What SQLGBM does

SQLGBM automates that mapping for a trained LightGBM or XGBoost binary classifier.

First, train the model as usual.

clf = lgb.LGBMClassifier(n_estimators=100, max_depth=5)
clf.fit(X, y)

Then convert it to SQL.

from sqlgbm import SQLGBM
sqlgbm = SQLGBM(clf, X=X)
sql = sqlgbm.generate_query('my_table', 'probability')

The generated query adds one nested CASE expression per tree, sums their outputs, and applies a sigmoid. At prediction time, it runs in a compatible SQL engine without a Python runtime or model server.

Why this is useful

This approach is most useful when the features already live in a database.

Traditional serving moves data through a model API, while SQLGBM runs the model inside the database
Traditional ML serving moves rows through an API; SQLGBM keeps prediction beside the data.

For batch scoring, one query can replace exporting rows, calling an API, and writing the results back.

For online scoring, the query can be part of a view or stored procedure, depending on the database.

This can cut latency by removing serialization and network calls. It also removes a separate deployment to operate.

Tradeoffs

This only fits some use cases.

Trees map cleanly to CASE statements; neural networks map less cleanly. Large or deep ensembles also create huge queries that some engines may struggle to plan or run. SQLGBM does not yet optimize its output for each database.

Multiclass classification is not supported, and the API is still early. The project is not ready for production use.

It is best suited to simple binary classification tasks where the data is already in SQL and a model server would be overkill.

Summary

ML inference does not always need its own service. SQLGBM turns LightGBM and XGBoost trees into CASE expressions and runs them next to the data. The result is less data movement and less infrastructure for the cases it supports.

Check it out on GitHub.