2019-11-18 05:58:33 +01:00
|
|
|
---
|
2016-01-21 23:45:00 +01:00
|
|
|
title: SQL Queries
|
|
|
|
summary: Write and modify direct database queries through SQLExpression subclasses.
|
2019-11-18 05:58:33 +01:00
|
|
|
iconBrand: searchengin
|
|
|
|
---
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2015-07-31 07:18:49 +02:00
|
|
|
# SQLSelect
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2016-01-21 23:45:00 +01:00
|
|
|
## Introduction
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2016-01-21 23:45:00 +01:00
|
|
|
An object representing a SQL select query, which can be serialized into a SQL statement.
|
|
|
|
It is easier to deal with object-wrappers than string-parsing a raw SQL-query.
|
|
|
|
This object is used by the SilverStripe ORM internally.
|
|
|
|
|
|
|
|
Dealing with low-level SQL is not encouraged, since the ORM provides
|
2019-06-10 18:46:14 +02:00
|
|
|
powerful abstraction APIs (see [datamodel](/developer_guides/model/data_model_and_orm)).
|
2016-01-21 23:45:00 +01:00
|
|
|
Starting with SilverStripe 3, records in collections are lazy loaded,
|
|
|
|
and these collections have the ability to run efficient SQL
|
|
|
|
such as counts or returning a single column.
|
|
|
|
|
|
|
|
For example, if you want to run a simple `COUNT` SQL statement,
|
|
|
|
the following three statements are functionally equivalent:
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\DB;
|
|
|
|
use SilverStripe\ORM\Queries\SQLSelect;
|
|
|
|
use SilverStripe\Security\Member;
|
2017-10-26 02:22:02 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Through raw SQL.
|
|
|
|
$count = DB::query('SELECT COUNT(*) FROM "Member"')->value();
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Through SQLSelect abstraction layer.
|
|
|
|
$query = new SQLSelect();
|
|
|
|
$count = $query->setFrom('Member')->setSelect('COUNT(*)')->value();
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Through the ORM.
|
|
|
|
$count = Member::get()->count();
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2016-01-21 23:45:00 +01:00
|
|
|
If you do use raw SQL, you'll run the risk of breaking
|
|
|
|
various assumptions the ORM and code based on it have:
|
|
|
|
|
|
|
|
* Custom getters/setters (object property can differ from database column)
|
2019-06-10 18:46:14 +02:00
|
|
|
* DataObject hooks like `onBeforeWrite()` and `onBeforeDelete()`
|
2016-01-21 23:45:00 +01:00
|
|
|
* Automatic casting
|
|
|
|
* Default values set through objects
|
|
|
|
* Database abstraction
|
|
|
|
|
|
|
|
We'll explain some ways to use *SELECT* with the full power of SQL,
|
|
|
|
but still maintain a connection to the ORM where possible.
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2019-11-18 05:58:33 +01:00
|
|
|
[warning]
|
2016-01-21 23:45:00 +01:00
|
|
|
Please read our [security topic](/developer_guides/security) to find out
|
|
|
|
how to properly prepare user input and variables for use in queries
|
2019-11-18 05:58:33 +01:00
|
|
|
[/warning]
|
2014-10-27 04:40:02 +01:00
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
2016-01-21 23:45:00 +01:00
|
|
|
### SELECT
|
|
|
|
|
|
|
|
Selection can be done by creating an instance of `SQLSelect`, which allows
|
|
|
|
management of all elements of a SQL SELECT query, including columns, joined tables,
|
|
|
|
conditional filters, grouping, limiting, and sorting.
|
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
E.g.:
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
$sqlQuery = new SQLSelect();
|
|
|
|
$sqlQuery->setFrom('Player');
|
|
|
|
$sqlQuery->selectField('FieldName', 'Name');
|
|
|
|
$sqlQuery->selectField('YEAR("Birthday")', 'Birthyear');
|
|
|
|
$sqlQuery->addLeftJoin('Team','"Player"."TeamID" = "Team"."ID"');
|
|
|
|
$sqlQuery->addWhere(['YEAR("Birthday") = ?' => 1982]);
|
|
|
|
// $sqlQuery->setOrderBy(...);
|
|
|
|
// $sqlQuery->setGroupBy(...);
|
|
|
|
// $sqlQuery->setHaving(...);
|
|
|
|
// $sqlQuery->setLimit(...);
|
|
|
|
// $sqlQuery->setDistinct(true);
|
|
|
|
|
|
|
|
// Get the raw SQL (optional) and parameters
|
|
|
|
$rawSQL = $sqlQuery->sql($parameters);
|
|
|
|
|
|
|
|
// Execute and return a Query object
|
|
|
|
$result = $sqlQuery->execute();
|
|
|
|
|
|
|
|
// Iterate over results
|
|
|
|
foreach($result as $row) {
|
|
|
|
echo $row['BirthYear'];
|
|
|
|
}
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-07-03 03:22:12 +02:00
|
|
|
The result of `SQLSelect::execute()` is an array lightly wrapped in a database-specific subclass of [Query](api:SilverStripe\ORM\Connect\Query).
|
2016-01-21 23:45:00 +01:00
|
|
|
This class implements the *Iterator*-interface, and provides convenience-methods for accessing the data.
|
|
|
|
|
|
|
|
### DELETE
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2016-01-21 23:45:00 +01:00
|
|
|
Deletion can be done either by calling `DB::query`/`DB::prepared_query` directly,
|
2017-08-07 01:46:19 +02:00
|
|
|
by creating a `SQLDelete` object, or by transforming a `SQLSelect` into a `SQLDelete`
|
2016-01-21 23:45:00 +01:00
|
|
|
object instead.
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
For example, creating a `SQLDelete` object:
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\Queries\SQLDelete;
|
2017-08-03 05:35:09 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
$query = SQLDelete::create()
|
|
|
|
->setFrom('"SiteTree"')
|
|
|
|
->setWhere(['"SiteTree"."ShowInMenus"' => 0]);
|
|
|
|
$query->execute();
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
Alternatively, turning an existing `SQLSelect` into a delete:
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\Queries\SQLSelect;
|
2017-08-03 05:35:09 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
$query = SQLSelect::create()
|
|
|
|
->setFrom('"SiteTree"')
|
|
|
|
->setWhere(['"SiteTree"."ShowInMenus"' => 0])
|
|
|
|
->toDelete();
|
|
|
|
$query->execute();
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
Directly querying the database:
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\DB;
|
|
|
|
DB::prepared_query('DELETE FROM "SiteTree" WHERE "SiteTree"."ShowInMenus" = ?', [0]);
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2016-01-21 23:45:00 +01:00
|
|
|
|
|
|
|
### INSERT/UPDATE
|
|
|
|
|
|
|
|
INSERT and UPDATE can be performed using the `SQLInsert` and `SQLUpdate` classes.
|
|
|
|
These both have similar aspects in that they can modify content in
|
|
|
|
the database, but each are different in the way in which they behave.
|
|
|
|
|
|
|
|
Previously, similar operations could be performed by using the `DB::manipulate`
|
|
|
|
function which would build the INSERT and UPDATE queries on the fly. This method
|
|
|
|
still exists, but internally uses `SQLUpdate` / `SQLInsert`, although the actual
|
|
|
|
query construction is now done by the `DBQueryBuilder` object.
|
|
|
|
|
|
|
|
Each of these classes implements the interface `SQLWriteExpression`, noting that each
|
|
|
|
accepts write key/value pairs in a number of similar ways. These include the following
|
2019-06-10 18:46:14 +02:00
|
|
|
API methods:
|
2016-01-21 23:45:00 +01:00
|
|
|
|
|
|
|
* `addAssignments` - Takes a list of assignments as an associative array of key -> value pairs,
|
2019-06-10 18:46:14 +02:00
|
|
|
but also supports SQL expressions as values if necessary
|
2016-01-21 23:45:00 +01:00
|
|
|
* `setAssignments` - Replaces all existing assignments with the specified list
|
|
|
|
* `getAssignments` - Returns all currently given assignments, as an associative array
|
2020-04-20 09:07:53 +02:00
|
|
|
in the format `['Column' => ['SQL' => ['parameters]]]`
|
2019-06-10 18:46:14 +02:00
|
|
|
* `assign` - Singular form of addAssignments, but only assigns a single column value
|
2016-01-21 23:45:00 +01:00
|
|
|
* `assignSQL` - Assigns a column the value of a specified SQL expression without parameters
|
2020-04-20 09:07:53 +02:00
|
|
|
`assignSQL('Column', 'SQL)` is shorthand for `assign('Column', ['SQL' => []])`
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
SQLUpdate also includes the following API methods:
|
2016-01-21 23:45:00 +01:00
|
|
|
|
|
|
|
* `clear` - Clears all assignments
|
|
|
|
* `getTable` - Gets the table to update
|
2019-06-10 18:46:14 +02:00
|
|
|
* `setTable` - Sets the table to update (this should be ANSI-quoted)
|
|
|
|
e.g. `$query->setTable('"SiteTree"');`
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
SQLInsert also includes the following API methods:
|
2016-01-21 23:45:00 +01:00
|
|
|
* `clear` - Clears all rows
|
|
|
|
* `clearRow` - Clears all assignments on the current row
|
|
|
|
* `addRow` - Adds another row of assignments, and sets the current row to the new row
|
|
|
|
* `addRows` - Adds a number of arrays, each representing a list of assignment rows,
|
2019-06-10 18:46:14 +02:00
|
|
|
and sets the current row to the last one
|
2016-01-21 23:45:00 +01:00
|
|
|
* `getColumns` - Gets the names of all distinct columns assigned
|
|
|
|
* `getInto` - Gets the table to insert into
|
2019-06-10 18:46:14 +02:00
|
|
|
* `setInto` - Sets the table to insert into (this should be ANSI-quoted),
|
|
|
|
e.g. `$query->setInto('"SiteTree"');`
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
E.g.:
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\Queries\SQLUpdate;
|
2017-10-26 02:22:02 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
$update = SQLUpdate::create('"SiteTree"')->addWhere(['ID' => 3]);
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// assigning a list of items
|
|
|
|
$update->addAssignments([
|
|
|
|
'"Title"' => 'Our Products',
|
|
|
|
'"MenuTitle"' => 'Products'
|
|
|
|
]);
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Assigning a single value
|
|
|
|
$update->assign('"MenuTitle"', 'Products');
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Assigning a value using parameterised expression
|
|
|
|
$title = 'Products';
|
|
|
|
$update->assign('"MenuTitle"', [
|
|
|
|
'CASE WHEN LENGTH("MenuTitle") > LENGTH(?) THEN "MenuTitle" ELSE ? END' =>
|
|
|
|
[$title, $title]
|
|
|
|
]);
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Assigning a value using a pure SQL expression
|
|
|
|
$update->assignSQL('"Date"', 'NOW()');
|
2017-08-03 05:35:09 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Perform the update
|
|
|
|
$update->execute();
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2016-01-21 23:45:00 +01:00
|
|
|
In addition to assigning values, the SQLInsert object also supports multi-row
|
|
|
|
inserts. For database connectors and API that don't have multi-row insert support
|
|
|
|
these are translated internally as multiple single row inserts.
|
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
For example:
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\Queries\SQLInsert;
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
$insert = SQLInsert::create('"SiteTree"');
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Add multiple rows in a single call. Note that column names do not need
|
|
|
|
// to be symmetric
|
|
|
|
$insert->addRows([
|
|
|
|
['"Title"' => 'Home', '"Content"' => '<p>This is our home page</p>'],
|
|
|
|
['"Title"' => 'About Us', '"ClassName"' => 'AboutPage']
|
|
|
|
]);
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Adjust an assignment on the last row
|
|
|
|
$insert->assign('"Content"', '<p>This is about us</p>');
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
// Add another row
|
|
|
|
$insert->addRow(['"Title"' => 'Contact Us']);
|
2016-01-21 23:45:00 +01:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
$columns = $insert->getColumns();
|
2020-04-20 09:07:53 +02:00
|
|
|
// $columns will be ['"Title"', '"Content"', '"ClassName"'];
|
2017-08-03 05:35:09 +02:00
|
|
|
|
2017-10-27 04:38:27 +02:00
|
|
|
$insert->execute();
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2016-01-21 23:45:00 +01:00
|
|
|
|
|
|
|
### Value Checks
|
|
|
|
|
|
|
|
Raw SQL is handy for performance-optimized calls,
|
|
|
|
e.g. when you want a single column rather than a full-blown object representation.
|
|
|
|
|
|
|
|
Example: Get the count from a relationship.
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\Queries\SQLSelect;
|
|
|
|
|
|
|
|
$sqlQuery = new SQLSelect();
|
|
|
|
$sqlQuery->setFrom('Player');
|
|
|
|
$sqlQuery->addSelect('COUNT("Player"."ID")');
|
|
|
|
$sqlQuery->addWhere(['"Team"."ID"' => 99]);
|
|
|
|
$sqlQuery->addLeftJoin('Team', '"Team"."ID" = "Player"."TeamID"');
|
|
|
|
$count = $sqlQuery->execute()->value();
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
|
|
|
|
2016-01-21 23:45:00 +01:00
|
|
|
Note that in the ORM, this call would be executed in an efficient manner as well:
|
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
$count = $myTeam->Players()->count();
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2014-10-27 04:40:02 +01:00
|
|
|
|
|
|
|
### Mapping
|
|
|
|
|
|
|
|
Creates a map based on the first two columns of the query result.
|
2016-01-21 23:45:00 +01:00
|
|
|
This can be useful for creating dropdowns.
|
|
|
|
|
|
|
|
Example: Show player names with their birth year, but set their birth dates as values.
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\Queries\SQLSelect;
|
|
|
|
use SilverStripe\Forms\DropdownField;
|
|
|
|
|
|
|
|
$sqlQuery = new SQLSelect();
|
|
|
|
$sqlQuery->setFrom('Player');
|
|
|
|
$sqlQuery->setSelect('Birthdate');
|
|
|
|
$sqlQuery->selectField('CONCAT("Name", ' - ', YEAR("Birthdate")', 'NameWithBirthyear');
|
|
|
|
$map = $sqlQuery->execute()->map();
|
|
|
|
$field = new DropdownField('Birthdates', 'Birthdates', $map);
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2016-01-21 23:45:00 +01:00
|
|
|
Note that going through SQLSelect is just necessary here
|
|
|
|
because of the custom SQL value transformation (`YEAR()`).
|
2019-06-10 18:46:14 +02:00
|
|
|
An alternative approach would be a custom getter in the object definition:
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2017-08-03 02:51:32 +02:00
|
|
|
```php
|
2017-10-27 04:38:27 +02:00
|
|
|
use SilverStripe\ORM\DataObject;
|
|
|
|
|
|
|
|
class Player extends DataObject
|
|
|
|
{
|
|
|
|
private static $db = [
|
|
|
|
'Name' => 'Varchar',
|
|
|
|
'Birthdate' => 'Date'
|
|
|
|
];
|
|
|
|
function getNameWithBirthyear() {
|
|
|
|
return date('y', $this->Birthdate);
|
2017-08-07 05:11:17 +02:00
|
|
|
}
|
2017-10-27 04:38:27 +02:00
|
|
|
}
|
|
|
|
$players = Player::get();
|
|
|
|
$map = $players->map('Name', 'NameWithBirthyear');
|
2017-08-03 02:51:32 +02:00
|
|
|
```
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2018-11-08 23:08:36 +01:00
|
|
|
### Data types
|
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
As of SilverStripe 4.4, the following PHP types will be used to return database content:
|
2018-11-08 23:08:36 +01:00
|
|
|
|
2019-06-10 18:46:14 +02:00
|
|
|
* booleans will be an integer 1 or 0, to ensure consistency with MySQL that doesn't have native booleans
|
2018-11-08 23:08:36 +01:00
|
|
|
* integer types returned as integers
|
|
|
|
* floating point / decimal types returned as floats
|
|
|
|
* strings returned as strings
|
|
|
|
* dates / datetimes returned as strings
|
|
|
|
|
|
|
|
Up until SilverStripe 4.3, bugs meant that strings were used for every column type.
|
|
|
|
|
2017-11-27 04:39:17 +01:00
|
|
|
## Related Lessons
|
|
|
|
* [Building custom SQL](https://www.silverstripe.org/learn/lessons/v4/beyond-the-orm-building-custom-sql-1)
|
|
|
|
|
|
|
|
|
|
|
|
## Related Documentation
|
2014-10-27 04:40:02 +01:00
|
|
|
|
2015-02-28 01:09:15 +01:00
|
|
|
* [Introduction to the Data Model and ORM](data_model_and_orm)
|
2014-10-27 04:40:02 +01:00
|
|
|
|
|
|
|
## API Documentation
|
|
|
|
|
2017-07-03 03:22:12 +02:00
|
|
|
* [DataObject](api:SilverStripe\ORM\DataObject)
|
|
|
|
* [SQLSelect](api:SilverStripe\ORM\Queries\SQLSelect)
|
|
|
|
* [DB](api:SilverStripe\ORM\DB)
|
|
|
|
* [Query](api:SilverStripe\ORM\Connect\Query)
|
|
|
|
* [Database](api:SilverStripe\ORM\Connect\Database)
|