When working with DSQL you need to understand the following basic concepts:

Basic Concepts

  • Expression (see expr)

    Expression object, represents a part of a SQL query. It can be used to express advanced logic in some part of a query, which Query itself might not support or can express a full statement Never try to look for “raw” queries, instead build expressions and think about escaping.

  • Query (see query)

    Object of a Query class can be used for building and executing valid SQL statements such as SELECT, INSERT, UPDATE, etc. After creating Query object you can call various methods to add “table”, “where”, “from” parts of your query.

  • Connection

    Represents a connection to the database. If you already have a PDO object you can feed it into Expression or Query, but for your comfort there is a Connection class with very little overhead.

Getting Started

We will start by looking at the Query building, because you do not need a database to create a query:

$query = $connection->dsql();

Once you have a query object, you can add parameters by calling some of it’s methods:

    ->where('birth_date', '1961-05-02')

Finally you can get the data:

$count = $query->getOne();

While DSQL is simple to use for basic queries, it also gives a huge power and consistency when you are building complex queries. Unlike other query builders that sometimes rely on “hacks” (such as method whereOr()) and claim to be useful for “most” database operations, with DSQL, you can use DSQL to build ALL of your database queries.

This is hugely beneficial for frameworks and large applications, where various classes need to interact and inject more clauses/fields/joins into your SQL query.

DSQL does not resolve conflicts between similarly named tables, but it gives you all the options to use aliases.

The next example might be a bit too complex for you, but still read through and try to understand what each section does to your base query:

// establish a query looking for a maximum salary
$salary = $connection->dsql();

// create few expression objects
$eMaxSalary = $salary->expr('max(salary)');
$eMonths = $salary->expr('TimeStampDiff(month, from_date, to_date)');

// configure our basic query
    ->field(['emp_no', 'max_salary' => $eMaxSalary, 'months' => $eMonths])

// define sub-query for employee "id" with certain birth-date
$employees = $salary->dsql()
    ->where('birth_date', '1961-05-02')

// use sub-select to condition salaries
$salary->where('emp_no', $employees);

// join with another table for more data

// finally, fetch result
foreach ($salary as $row) {
    echo 'Data: ' . json_encode($row) . "\n";

The above query resulting code will look like this:

    max(salary) `max_salary`,
    TimeStampDiff(month, from_date, to_date) `months`
    `employees` on `employees`.`emp_id` = `salary`.`emp_id`
    `salary`.`emp_no` in(select `id` from `employees` where `birth_date` = :a)
GROUP BY `emp_no`
ORDER BY max_salary desc

:a = "1961-05-02"

Using DSQL in higher level ORM libraries and frameworks allows them to focus on defining the database logic, while DSQL can perform the heavy-lifting of query building and execution.

Creating Objects and PDO

DSQL classes does not need database connection for most of it’s work. Once you create new instance of Expression or Query you can perform operation and finally call Expression::render() to get the final query string with params:

use Atk4\Data\Persistence\Sql\Query;

$q = (new Query())->table('user')->where('id', 1)->field('name');
[$query, $params] = $q->render();

When used in application you would typically generate queries with the purpose of executing them, which makes it very useful to create a Connection object. The usage changes slightly:

$c = Atk4\Data\Persistence\Sql\Connection::connect($dsn, $user, $password);
$q = $c->dsql()->table('user')->where('id', 1)->field('name');

$name = $q->getOne();

You no longer need “use” statement and Connection class will automatically do some of the hard work to adopt query building for your database vendor. There are more ways to create connection, see Advanced Connections section.

The format of the $dsn is the same as with for DBAL connection. If you need to execute query that is not supported by DSQL, you should always use expressions:

$tables = $c->expr('show tables like []', [$likeStr])->getRows();

DSQL classes are mindful about your SQL vendor and it’s quirks, so when you’re building sub-queries with Query::dsql, you can avoid some nasty problems:


The above code will work even though SQLite does not support truncate. That’s because DSQL takes care of this.

Query Building

Each Query object represents a query to the database in-the-making. Calling methods such as Query::table or Query::where affect part of the query you’re making. At any time you can either execute your query or use it inside another query.

Query supports majority of SQL syntax out of the box. Some unusual statements can be easily added by customizing template for specific query and we will look into examples in Extending Query Class

Query Mode

When you create a new Query object, it is going to be a SELECT query by default. If you wish to execute update operation instead, you cam simply call Query::mode to change it. For more information see Query Modes. You can actually perform multiple operations:

$q = $c->dsql()->table('employee')->where('emp_no', 1234);
$backupData = $q->getRows();

A good practice is to re-use the same query object before you branch out and perform the action:

$q = $c->dsql()->table('employee')->where('emp_no', 1234);

if ($confirmed) {
} else {
    echo 'Are you sure you want to delete ' . $q->field('count(*)') . ' employees?';

Fetching Result

When you are selecting data from your database, DSQL will prepare and execute statement for you. Depending on the connection, there may be some magic involved, but once the query is executed, you can start streaming your data:

foreach ($query->table('employee')->where('dep_no', 123) as $employee) {
    echo $employee['first_name'] . "\n";

When iterating you’ll have Doctrine\DBAL\Result. Remember that DQSL can support vendors, $employee will always contain associative array representing one row of data. (See also Manual Query Execution).