silverstripe-framework/docs/en/reference/sqlquery.md
Ingo Schommer 402297ee1f Removed outdated docs for SQLQuery and data model techniques
Some of the SQLQuery recipes are now handled better by DataList,
others should be contrasted with their respective DataList implementation,
pointing out the limitations
2012-06-28 14:51:04 +02:00

4.3 KiB

SQL Query

Introduction

An object representing a SQL 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 powerful abstraction APIs (see datamodel. 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:

:::php
// Through raw SQL
$count = DB::query('SELECT COUNT(*) FROM "Member"')->value();
// Through SQLQuery abstraction layer
$query = new SQLQuery();
$count = $query->setFrom('Member')->setSelect('COUNT(*)')->value();
// Through the ORM
$count = Member::get()->count();

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)
  • DataObject hooks like onBeforeWrite() and onBeforeDelete()
  • 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.

Please read our ["security" topic](/topics/security) to find out how to sanitize user input before using it in SQL queries.

Usage

SELECT

:::php
$sqlQuery = new SQLQuery();
$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)
$rawSQL = $sqlQuery->sql();

// Execute and return a Query object
$result = $sqlQuery->execute();

// Iterate over results
foreach($result as $row) {
  echo $row['BirthYear'];
}

The result is an array lightly wrapped in a database-specific subclass of [api:Query]. This class implements the Iterator-interface, and provides convenience-methods for accessing the data.

DELETE

:::php
$sqlQuery->setDelete(true);

INSERT/UPDATE

Currently not supported through the SQLQuery class, please use raw DB::query() calls instead.

:::php
DB::query('UPDATE "Player" SET "Status"=\'Active\'');

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.

:::php
$sqlQuery = new SQLQuery();

$sqlQuery->setFrom('Player'); $sqlQuery->addSelect('COUNT("Player"."ID")'); $sqlQuery->addWhere('"Team"."ID" = 99'); $sqlQuery->addLeftJoin('Team', '"Team"."ID" = "Player"."TeamID"'); $count = $sqlQuery->execute()->value();

Note that in the ORM, this call would be executed in an efficient manner as well:

:::php
$count = $myTeam->Players()->count();

Mapping

Creates a map based on the first two columns of the query result. This can be useful for creating dropdowns.

Example: Show player names with their birth year, but set their birth dates as values.

:::php
$sqlQuery = new SQLQuery();
$sqlQuery->setFrom('Player');
$sqlQuery->setSelect('Birthdate');
$sqlQuery->selectField('CONCAT("Name", ' - ', YEAR("Birthdate")', 'NameWithBirthyear');
$map = $sqlQuery->execute()->map();
$field = new DropdownField('Birthdates', 'Birthdates', $map);

Note that going through SQLQuery is just necessary here because of the custom SQL value transformation (YEAR()). An alternative approach would be a custom getter in the object definition.

:::php
class Player extends DataObject {
	static $db = array(
		'Name' => 
		'Birthdate' => 'Date'
	);
	function getNameWithBirthyear() {
		return date('y', $this->Birthdate);
	}
}
$players = Player::get();
$map = $players->map('Name', 'NameWithBirthyear');