sqlserver.php 3.53 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<?php namespace Laravel\Database\Query\Grammars;

use Laravel\Database\Query;

class SQLServer extends Grammar {

	/**
	 * The keyword identifier for the database system.
	 *
	 * @var string
	 */
	protected $wrapper = '[%s]';

	/**
15 16 17 18 19 20 21
	 * The format for properly saving a DateTime.
	 *
	 * @var string
	 */
	public $datetime = 'Y-m-d H:i:s.000';

	/**
22 23 24 25 26 27 28 29 30 31 32
	 * Compile a SQL SELECT statement from a Query instance.
	 *
	 * @param  Query   $query
	 * @return string
	 */
	public function select(Query $query)
	{
		$sql = parent::components($query);

		// SQL Server does not currently implement an "OFFSET" type keyword, so we
		// actually have to generate the ANSI standard SQL for doing offset like
33
		// functionality. OFFSET is in SQL Server 2012, however.
34 35 36 37 38 39 40
		if ($query->offset > 0)
		{
			return $this->ansi_offset($query, $sql);
		}

		// Once all of the clauses have been compiled, we can join them all as
		// one statement. Any segments that are null or an empty string will
41
		// be removed from the array before imploding.
42 43 44 45 46 47 48 49 50 51 52
		return $this->concatenate($sql);
	}

	/**
	 * Compile the SELECT clause for a query.
	 *
	 * @param  Query   $query
	 * @return string
	 */
	protected function selects(Query $query)
	{
53 54
		if ( ! is_null($query->aggregate)) return;

55 56
		$select = ($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT ';

57 58 59
		// Instead of using a "LIMIT" keyword, SQL Server uses the TOP keyword
		// within the SELECT statement. So, if we have a limit, we will add
		// it to the query here if there is not an OFFSET present.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
		if ($query->limit > 0 and $query->offset <= 0)
		{
			$select .= 'TOP '.$query->limit.' ';
		}

		return $select.$this->columnize($query->selects);
	}

	/**
	 * Generate the ANSI standard SQL for an offset clause.
	 *
	 * @param  Query  $query
	 * @param  array  $components
	 * @return array
	 */
	protected function ansi_offset(Query $query, $components)
	{
77 78 79
		// An ORDER BY clause is required to make this offset query work, so if
		// one doesn't exist, we'll just create a dummy clause to trick the
		// database and pacify it so it doesn't complain about the query.
80 81 82 83 84
		if ( ! isset($components['orderings']))
		{
			$components['orderings'] = 'ORDER BY (SELECT 0)';
		}

85 86 87
		// We need to add the row number to the query so we can compare it to
		// the offset and limit values given for the statement. So we'll add
		// an expression to the select for the row number.
88 89 90 91 92 93 94 95
		$orderings = $components['orderings'];

		$components['selects'] .= ", ROW_NUMBER() OVER ({$orderings}) AS RowNum";

		unset($components['orderings']);

		$start = $query->offset + 1;

96 97 98
		// Next we need to calculate the constraint that should be placed on
		// the row number to get the correct offset and limit on the query.
		// If there is not limit, we'll just handle the offset.
99 100 101 102 103 104 105 106 107 108 109
		if ($query->limit > 0)
		{
			$finish = $query->offset + $query->limit;

			$constraint = "BETWEEN {$start} AND {$finish}";
		}
		else
		{
			$constraint = ">= {$start}";
		}

110 111 112
		// We're finally ready to build the final SQL query so we'll create
		// a common table expression with the query and select all of the
		// results with row numbers between the limit and offset.
113 114 115 116 117 118 119 120 121 122 123 124 125
		$sql = $this->concatenate($components);

		return "SELECT * FROM ($sql) AS TempTable WHERE RowNum {$constraint}";
	}

	/**
	 * Compile the LIMIT clause for a query.
	 *
	 * @param  Query   $query
	 * @return string
	 */
	protected function limit(Query $query)
	{
126
		return '';
127 128 129 130 131 132 133 134 135 136
	}

	/**
	 * Compile the OFFSET clause for a query.
	 *
	 * @param  Query   $query
	 * @return string
	 */
	protected function offset(Query $query)
	{
137
		return '';
138 139 140
	}

}