Generated banner for Matlab Use Database
Generated logo for Matlab Use Database

Matlab Use Database

MathWorksOfficialCreated by MathWorks

  • Skill
  • Tool integration
  • Data and AI
  • BSD-2-Clause
  • No issues found

From the author

Reads from, writes to, and manages relational databases using MATLAB Database Toolbox. Use when connecting to databases, reading data with sqlread or fetch, filtering with rowfilter, writing with sqlwrite, updating with sqlupdate, executing SQL statements, managing transactions with commit and rollback, mapping MATLAB classes to tables with ORM (Mappable, ormread, ormwrite, ormupdate), or performing any database operation from MATLAB. Triggers on: database, SQL, sqlread, sqlwrite, sqlupdate, fetch, execute, rowfilter, RowFilter, ORM, Mappable, ormread, ormwrite, ormupdate, orm2sql, transaction, commit, rollback, Database Toolbox, PostgreSQL, MySQL, SQLite, SQL Server, Oracle, database connection, database table, query database, insert data, update rows, delete rows, stored procedure, prepared statement, odbc, databaseConnectionOptions, datasource, data source, DSN, connection string, multithreaded, parallel.
— the description by MathWorks

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The MATLAB Database Toolbox skill provides a comprehensive interface for interacting with relational databases, including PostgreSQL, MySQL, SQLite, SQL Server, and Oracle. It supports the full data lifecycle, from establishing secure connections and executing SQL queries to performing CRUD operations and mapping database tables to MATLAB classes using an ORM. The skill emphasizes secure credential handling, efficient data retrieval through pushdown filtering, and transactional integrity.

What it does

  • Connects to relational databases using DSN or DSN-less ODBC and native drivers.
  • Reads and writes data using sqlread, sqlwrite, and sqlupdate.
  • Executes arbitrary SQL statements and manages transactions with commit and rollback.
  • Maps MATLAB classes to database tables using the Mappable ORM mixin.
  • Filters data at the database level using RowFilter to optimize data transfer.
  • Supports multithreaded read and write operations for large datasets using parfeval.

When to use it

  • When you need to perform CRUD operations on relational databases from within MATLAB.
  • When you need to map database tables to custom MATLAB objects.
  • When you need to execute complex SQL queries or stored procedures.
  • When you need to manage atomic transactions across multiple database operations.
  • When you need to perform bulk data operations or parallelized I/O.

Advantages

  • Enables direct integration between MATLAB workflows and relational database systems.
  • Provides built-in ORM capabilities for object-oriented database interaction.
  • Supports secure credential management via setSecret and getSecret.
  • Allows for efficient data filtering by pushing operations to the database server.
  • Facilitates transactional consistency through explicit commit and rollback controls.

Usage examples

“How do I read data from a products table where the price is greater than 100?”
The agent will demonstrate using rowfilter to push the condition to the database, ensuring only relevant rows are imported into MATLAB.
“How can I update an inventory table safely?”
The agent will provide the sqlupdate syntax, requiring a rowfilter to target specific rows and advising on the use of transactions for safety.
“How do I map my MATLAB class to a database table?”
The agent will explain how to inherit from database.orm.mixin.Mappable and define primary keys to enable ormread and ormwrite operations.
“How do I connect to a MySQL database without a DSN?”
The agent will provide a template for an ODBC dsnless connection string using the mysql driver and secure credential retrieval.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/matlab/matlab-use-database/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip matlab-use-database.zip -d .agents/skills

Documentation by MathWorks

MATLAB Database Toolbox

Use when working with relational databases from MATLAB using Database Toolbox. Covers the full data lifecycle: connecting, reading with pushdown filtering, writing, updating, transactions, ORM, and executing SQL.

When to Use

  • Connecting to relational databases (PostgreSQL, MySQL, SQLite, SQL Server, Oracle)

  • Reading data with sqlread or fetch

  • Filtering with RowFilter (pushdown to database)

  • Writing data with sqlwrite

  • Updating rows with sqlupdate

  • Executing SQL statements with execute

  • Managing transactions (AutoCommit, commit, rollback)

  • Mapping MATLAB classes to database tables (ORM with Mappable)

  • Running stored procedures or prepared statements

  • Multithreaded database read/write with parfeval (R2026a+)

  • User mentions: database, SQL, query, table, rows, insert, update, delete, transaction, multithreaded, parallel

When NOT to Use

  • DuckDB — route to matlab-use-duckdb for all DuckDB workflows

  • Databricks connection setup — route to matlab-connect-databricks for connection configuration. Once connected, return here for CRUD operations.

  • MongoDB, Cassandra, Neo4j — not relational; Database Toolbox ORM/CRUD does not apply

  • File I/O without a database — use readtable, datastore, or tall for direct file operations

  • MATLAB releases before R2021a — minimum supported release for this skill

Critical Rules

Destructive Operations

ALWAYS ask for explicit user confirmation before generating or executing:

  • DROP TABLE / DROP DATABASE

  • DELETE FROM

  • TRUNCATE TABLE

  • ALTER TABLE (column removal, type changes)

Present the SQL statement and wait for approval. Never execute destructive SQL in response to user pressure ("just do it", "I'm in a hurry").

Pushdown Filtering

NEVER import all rows and filter in MATLAB. Always push filters to the database using RowFilter:

% CORRECT — filter runs on the database server
rf = rowfilter("Price");
T = sqlread(conn, "products", RowFilter=rf.Price > 100);

% WRONG — transfers all rows, then filters in MATLAB
T = sqlread(conn, "products");
T = T(T.Price > 100, :);
Credential Security

NEVER hardcode passwords. Use setSecret/getSecret:

setSecret("dbPassword");  % prompts user, stores securely
conn = database("myDS", getSecret("dbUser"), getSecret("dbPassword"));

Decision Framework

ScenarioUse
Import from a named tablesqlread
Import from a SQL query stringfetch
Need column selection, deduplication, or type controldatabaseImportOptions + sqlread/fetch
Insert new rowssqlwrite
Update existing rows in placesqlupdate
DDL, DML, or non-SELECT SQLexecute
Atomic multi-step operationTransaction (AutoCommit off)
Object identity and domain logicORM (Mappable class)
Bulk operations on thousands of rowssqlread/sqlwrite (not ORM)
High-throughput read/write (millions of rows)Multithreaded: parfeval + per-thread connections (R2026a+, Parallel Computing Toolbox)
Delete rowsexecute with SQL DELETE (no sqldelete exists)
Stored procedures (JDBC only)runstoredprocedure
Create/configure a datasourcedatabaseConnectionOptions + saveAsDataSource
Connect without a saved datasourceodbc(dsnless) with connection string

Core Patterns

Connection
conn = database("myDataSource", getSecret("dbUser"), getSecret("dbPass"));
% Or native connections:
conn = postgresql("myDS", getSecret("user"), getSecret("pass"));
conn = mysql("myDS", getSecret("user"), getSecret("pass"));
conn = sqlite("myDB.db");
ODBC DSN-Less Connection

Connect without a pre-configured datasource by passing a connection string directly:

dsnless = "Driver={MySQL ODBC 8.0 Unicode Driver};" + ...
    "Server=dbtb09;Database=production;UID=" + getSecret("dbUser") + ...
    ";PWD=" + getSecret("dbPass");
conn = odbc(dsnless);
Create and Save a Data Source

Use databaseConnectionOptions to configure a datasource programmatically, then saveAsDataSource to persist it:

opts = databaseConnectionOptions("odbc", "MySQL");
opts = setoptions(opts, DataSourceName="myDS", ...
    Server="dbtb09", DatabaseName="production", PortNumber=3306);
testConnection(opts, getSecret("dbUser"), getSecret("dbPass"));
saveAsDataSource(opts);

% Now connect using the saved datasource name
conn = odbc("myDS", getSecret("dbUser"), getSecret("dbPass"));
Read with RowFilter
rf = rowfilter(["Status", "Timestamp"]);
T = sqlread(conn, "orders", RowFilter=rf.Status == "Active" & rf.Timestamp > datetime(2024,1,1));

rowfilter requires column names as input. Access columns via property syntax (rf.ColumnName).

Read with Import Options
opts = databaseImportOptions(conn, "orders");
opts.SelectedVariableNames = ["OrderID", "Status", "Total"];
opts.RowFilter = opts.RowFilter.Total > 1000;
opts.ExcludeDuplicates = true;
T = sqlread(conn, "orders", opts);

Import options (opts) are a positional 3rd argument — not a name-value pair.

Write
data = table("Widget", 50, 9.99, VariableNames=["Product", "Qty", "Price"]);
sqlwrite(conn, "inventory", data);
Update
rf = rowfilter("ProductID");
newData = table(5.99, VariableNames="Price");
sqlupdate(conn, "inventory", newData, rf.ProductID == 42);

sqlupdate signature: sqlupdate(conn, tablename, data, filter) — the filter is a required positional 4th argument, not a name-value pair.

Transaction
conn.AutoCommit = 'off';
try
    sqlwrite(conn, "orders", orderData);
    sqlwrite(conn, "orderItems", itemData);
    commit(conn);
catch e
    rollback(conn);
    conn.AutoCommit = 'on';
    rethrow(e);
end
conn.AutoCommit = 'on';

ALWAYS use conn.AutoCommit = 'off' with commit(conn)/rollback(conn). Do not use raw SQL BEGIN/COMMIT/ROLLBACK via execute.

ORM — Define a Mappable Class
classdef Employee < database.orm.mixin.Mappable

    properties (PrimaryKey)
        EmployeeID int32
    end

    properties
        Name string
        Department string
    end

    properties (ColumnType = "date")
        HireDate datetime
    end

    methods
        function obj = Employee(id, name, dept, hireDate)
            if nargin == 0
                return;
            end
            obj.EmployeeID = id;
            obj.Name = name;
            obj.Department = dept;
            obj.HireDate = hireDate;
        end
    end
end

ORM requirements:

  • Inherit from database.orm.mixin.Mappable

  • Mark at least one property block (PrimaryKey)

  • Constructor must handle nargin == 0 (ORM constructs empty objects)

  • R2023b or later

  • (TableName = "name") on classdef only if the database table name differs from the class name (defaults to class name)

ORM — CRUD Operations
% Write
emp = Employee(1, "Alice", "Engineering", datetime(2024,3,15));
ormwrite(conn, emp);

% Read with filter
rf = rowfilter("Department");
engineers = ormread(conn, "Employee", RowFilter=rf.Department == "Engineering");

% Update
engineers(1).Department = "Data Science";
ormupdate(conn, engineers(1));

% Refresh from database
emp = ormread(conn, emp);
Bulk Write (Chunked)

sqlwrite has no BatchSize parameter. Chunk manually:

chunkSize = 50000;
numChunks = ceil(height(data) / chunkSize);
for c = 1:numChunks
    startIdx = (c - 1) * chunkSize + 1;
    endIdx = min(c * chunkSize, height(data));
    sqlwrite(conn, "targetTable", data(startIdx:endIdx, :));
end
Multithreaded Write (R2026a+, requires Parallel Computing Toolbox)

For maximum throughput, use parfeval with per-thread connections. Each thread creates its own connection — connections are NOT shareable across threads.

data = parallel.pool.Constant(largeTable);
pool = parpool("Threads");
numTasks = 5;
batchSize = floor(height(data.Value) / numTasks);
startRow = 1;
endRow = 1 + batchSize;

writeFutures(1:numTasks) = parallel.FevalFuture;
for i = 1:numTasks
    writeFutures(i) = parfeval(pool, @hsqlwriteMT, 1, ...
        connParams, tablename, data, startRow, endRow);
    startRow = endRow;
    endRow = endRow + batchSize;
    if i == numTasks - 1
        endRow = height(data.Value) + 1;
    end
end
results = writeFutures.fetchOutputs("UniformOutput", false);

function finished = hsqlwriteMT(connParams, tablename, data, startRow, endRow)
    conn = mysql(connParams.user, connParams.pass, ...
        Server=connParams.server, DatabaseName=connParams.db);
    sqlwrite(conn, tablename, data.Value(startRow:endRow-1, :));
    close(conn);
    finished = true;
end

Supported: mysql(), postgresql(), sqlite(), ODBC. See references/multithreaded-io.md for the read pattern and common mistakes.

Common Mistakes

MistakeWhy It's WrongCorrect Approach
rf = rowfilter("Table"); rf == "val"Passes table name; uses == on the filter objectrf = rowfilter("Column"); rf.Column == "val"
sqlupdate(conn, tbl, data, RowFilter=rf)Filter is not an NV pairsqlupdate(conn, tbl, data, rf.Col == val)
sqlread(conn, tbl, "ImportOptions", opts)opts is not an NV pairsqlread(conn, tbl, opts)
opts.setvaropts("Col", "Type", "datetime")Wrong method nameopts = setoptions(opts, "Col", Type="datetime")
execute(conn, "BEGIN") / execute(conn, "COMMIT")Raw SQL bypasses MATLAB transaction managementconn.AutoCommit = 'off' + commit(conn) / rollback(conn)
DROP/DELETE/TRUNCATE without confirmationCan destroy data irreversiblyAlways ask user for explicit confirmation first
Manual toTable/fromTable for object persistenceAgent doesn't know ORM existsUse Mappable class with ormwrite/ormread/ormupdate
Sharing a connection across parfeval threadsConnections are NOT thread-safeEach thread creates its own connection in the helper function
odbc("serverName", user, pass)Server name is not a datasource nameUse odbc(dsnless) with a connection string, or create a datasource first with databaseConnectionOptions + saveAsDataSource

Reference Cards

  • See references/sqlread-fetch.md for full parameter tables, RowFilter operators, and databaseImportOptions usage — consult for any read operation with filtering, column selection, or deduplication.

  • See references/sqlwrite-sqlupdate.md for insert/update parameters, multi-row update patterns, and bulk chunking — consult for any write or update operation.

  • See references/transactions.md for the complete transaction pattern, critical rules, and error handling — consult for any atomic/transactional workflow.

  • See references/orm.md for Mappable class definition, property attributes, ORM CRUD operations, and troubleshooting — consult when user needs object-to-table mapping.

  • See references/execute-storedproc.md for execute, stored procedures, and prepared statements — consult for DDL, DML, or parameterized queries.

  • See references/multithreaded-io.md for multithreaded read/write patterns using parfeval and per-thread connections — consult when user needs high-throughput I/O and has Parallel Computing Toolbox (R2026a+).


Copyright 2026 The MathWorks, Inc.


Files

references/multithreaded-io.md

Multithreaded Read & Write (R2026a+)

Requires: Parallel Computing Toolbox

Supported connections: mysql(), postgresql(), sqlite(), ODBC via odbc().

Critical Rules

  • Each thread must create its own connection. Database connections are NOT thread-safe — never share a connection across threads.

  • Use parpool("Threads") — not "Processes". Thread pools share memory and avoid data serialization overhead.

  • Use parallel.pool.Constant to wrap large data for write operations — avoids copying the full dataset to each thread.

  • Divide work into row-range batches — each thread handles a contiguous slice.

Multithreaded Write

% Prepare data (wrap in Constant to avoid copies)
data = parallel.pool.Constant(largeTable);

% Connection parameters
server = "myserver.example.com";
databaseName = "production";
username = getSecret("dbUser");
password = getSecret("dbPass");
portNumber = 3306;
tablename = "measurements";

nRow = height(data.Value);
numTasks = 5;
batchSize = floor(nRow / numTasks);
startRow = 1;
endRow = 1 + batchSize;

pool = parpool("Threads");
writeFutures(1:numTasks) = parallel.FevalFuture;

for i = 1:numTasks
    writeFutures(i) = parfeval(pool, @hsqlwriteMT, 1, ...
        server, databaseName, username, password, portNumber, ...
        tablename, data, startRow, endRow);
    startRow = endRow;
    endRow = endRow + batchSize;
    if i == numTasks - 1
        endRow = nRow + 1;
    end
end

results = writeFutures.fetchOutputs("UniformOutput", false);

function finished = hsqlwriteMT(server, databaseName, username, password, portNumber, tablename, data, startRow, endRow)
    conn = mysql(username, password, Server=server, DatabaseName=databaseName, PortNumber=portNumber);
    sqlwrite(conn, tablename, data.Value(startRow:endRow-1, :));
    close(conn);
    finished = true;
end

Multithreaded Read

Partition by a column (e.g., a categorical or ID range) so each thread reads a disjoint subset. Avoid OFFSET/LIMIT — it forces the database to scan skipped rows, which negates parallelization gains at high offsets and is not supported by all databases.

Choose a partition column that distributes rows evenly across threads. Good candidates: categorical columns with known distinct values, or numeric ID columns that can be divided into ranges.

Partition by Distinct Values (categorical column)
% Get distinct partition values using sqlStatistics (R2025a+)
conn = postgresql(getSecret("dbUser"), getSecret("dbPass"), ...
    Server="myserver.example.com", DatabaseName="analytics");
tblName = "events";
partitionCol = "Region";
stats = sqlStatistics(conn, tblName, VariableNames=partitionCol, StatsInfo="unique");
partitionValues = stats.Unique{1};
close(conn);

% Parallel read — one thread per partition value
pool = parpool("Threads");
numFutures = numel(partitionValues);
futures(1:numFutures) = parallel.FevalFuture;

for i = 1:numFutures
    futures(i) = parfeval(pool, @hfetchMT, 1, ...
        tblName, partitionCol, partitionValues(i));
end

data = futures.fetchOutputs("UniformOutput", false);
allData = vertcat(data{:});

function data = hfetchMT(tblName, partitionCol, partitionValue)
    conn = postgresql(getSecret("dbUser"), getSecret("dbPass"), ...
        Server="myserver.example.com", DatabaseName="analytics");
    rf = rowfilter(partitionCol);
    data = sqlread(conn, tblName, RowFilter=rf.(partitionCol) == partitionValue);
    close(conn);
end
Partition by ID Range (numeric column)
% Get ID range using sqlStatistics (R2025a+)
conn = postgresql(getSecret("dbUser"), getSecret("dbPass"), ...
    Server="myserver.example.com", DatabaseName="analytics");
tblName = "events";
partitionCol = "EventID";
stats = sqlStatistics(conn, tblName, VariableNames=partitionCol, StatsInfo=["min", "max"]);
bounds.minID = stats.Min;
bounds.maxID = stats.Max;
close(conn);

% Divide range into batches
pool = parpool("Threads");
numFutures = 5;
rangeSize = bounds.maxID - bounds.minID + 1;
batchSize = ceil(rangeSize / numFutures);
futures(1:numFutures) = parallel.FevalFuture;

for i = 1:numFutures
    lo = bounds.minID + (i - 1) * batchSize;
    hi = min(bounds.minID + i * batchSize - 1, bounds.maxID);
    futures(i) = parfeval(pool, @hfetchRangeMT, 1, tblName, partitionCol, lo, hi);
end

data = futures.fetchOutputs("UniformOutput", false);
allData = vertcat(data{:});

function data = hfetchRangeMT(tblName, partitionCol, lo, hi)
    conn = postgresql(getSecret("dbUser"), getSecret("dbPass"), ...
        Server="myserver.example.com", DatabaseName="analytics");
    rf = rowfilter(partitionCol);
    data = sqlread(conn, tblName, ...
        RowFilter=rf.(partitionCol) >= lo & rf.(partitionCol) <= hi);
    close(conn);
end

Adapting for Other Databases

Replace the connection call in the helper function:

DatabaseConnection in helper
MySQLconn = mysql(user, pass, Server=s, DatabaseName=db, PortNumber=p)
PostgreSQLconn = postgresql(user, pass, Server=s, DatabaseName=db)
SQLiteconn = sqlite(filePath)
ODBCconn = odbc(connectionString)
ScenarioApproach
Large read/write, Parallel Computing Toolbox availableMultithreaded (parfeval + per-thread connections)
Large write, no Parallel Computing ToolboxSingle-threaded chunked sqlwrite loop
Small dataset (< 100K rows)Single sqlread or sqlwrite call
Read with complex SQL (joins, aggregation)Single fetch with SQL — hard to partition

Common Mistakes

% WRONG — sharing a connection across threads
conn = mysql(user, pass, Server=s, DatabaseName=db);
for i = 1:numTasks
    futures(i) = parfeval(pool, @sqlwrite, 0, conn, tbl, batch);  % NOT thread-safe!
end

% CORRECT — each thread creates its own connection
for i = 1:numTasks
    futures(i) = parfeval(pool, @helperWrite, 1, connParams, batch);
end

% WRONG — using parpool("Processes") (serializes data across process boundaries)
pool = parpool("Processes");

% CORRECT — threads share memory
pool = parpool("Threads");

% WRONG — passing large table directly (copied to each thread)
parfeval(pool, @helperWrite, 1, largeTable, startRow, endRow);

% CORRECT — wrap in parallel.pool.Constant
data = parallel.pool.Constant(largeTable);
parfeval(pool, @helperWrite, 1, data, startRow, endRow);

% WRONG — using OFFSET/LIMIT for parallel reads (scans skipped rows, slow at high offsets)
fetch(conn, "SELECT * FROM events OFFSET 1000000 LIMIT 200000");

% CORRECT — partition by column values or ID ranges
rf = rowfilter("Region");
sqlread(conn, "events", RowFilter=rf.Region == "US-West");

Copyright 2026 The MathWorks, Inc.


Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
MathWorks
Publisher
MathWorks (Official)
Source
matlab/matlab-agentic-toolkit · skills-catalog/reporting-and-database-access/matlab-use-database · commit 2a7f215
Licence
BSD 2-Clause License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-use-database.zip