@@ -19,6 +19,25 @@ class SqlServerGrammar extends Grammar
19
19
'& ' , '&= ' , '| ' , '|= ' , '^ ' , '^= ' ,
20
20
];
21
21
22
+ /**
23
+ * The components that make up a select clause.
24
+ *
25
+ * @var string[]
26
+ */
27
+ protected $ selectComponents = [
28
+ 'aggregate ' ,
29
+ 'columns ' ,
30
+ 'from ' ,
31
+ 'joins ' ,
32
+ 'wheres ' ,
33
+ 'groups ' ,
34
+ 'havings ' ,
35
+ 'orders ' ,
36
+ 'offset ' ,
37
+ 'limit ' ,
38
+ 'lock ' ,
39
+ ];
40
+
22
41
/**
23
42
* Compile a select query into SQL.
24
43
*
@@ -27,26 +46,12 @@ class SqlServerGrammar extends Grammar
27
46
*/
28
47
public function compileSelect (Builder $ query )
29
48
{
30
- if (! $ query ->offset ) {
31
- return parent ::compileSelect ($ query );
32
- }
33
-
34
- if (is_null ($ query ->columns )) {
35
- $ query ->columns = ['* ' ];
49
+ // An ORDER BY clause is required for offset to work
50
+ if ($ query ->offset && empty ($ query ->orders )) {
51
+ $ query ->orders [] = ['sql ' => '(SELECT 0) ' ];
36
52
}
37
53
38
- $ components = $ this ->compileComponents ($ query );
39
-
40
- if (! empty ($ components ['orders ' ])) {
41
- return parent ::compileSelect ($ query )." offset {$ query ->offset } rows fetch next {$ query ->limit } rows only " ;
42
- }
43
-
44
- // If an offset is present on the query, we will need to wrap the query in
45
- // a big "ANSI" offset syntax block. This is very nasty compared to the
46
- // other database systems but is necessary for implementing features.
47
- return $ this ->compileAnsiOffset (
48
- $ query , $ components
49
- );
54
+ return parent ::compileSelect ($ query );
50
55
}
51
56
52
57
/**
@@ -246,69 +251,6 @@ protected function compileHavingBitwise($having)
246
251
return '( ' .$ column .' ' .$ having ['operator ' ].' ' .$ parameter .') != 0 ' ;
247
252
}
248
253
249
- /**
250
- * Create a full ANSI offset clause for the query.
251
- *
252
- * @param \Illuminate\Database\Query\Builder $query
253
- * @param array $components
254
- * @return string
255
- */
256
- protected function compileAnsiOffset (Builder $ query , $ components )
257
- {
258
- // An ORDER BY clause is required to make this offset query work, so if one does
259
- // not exist we'll just create a dummy clause to trick the database and so it
260
- // does not complain about the queries for not having an "order by" clause.
261
- if (empty ($ components ['orders ' ])) {
262
- $ components ['orders ' ] = 'order by (select 0) ' ;
263
- }
264
-
265
- // We need to add the row number to the query so we can compare it to the offset
266
- // and limit values given for the statements. So we will add an expression to
267
- // the "select" that will give back the row numbers on each of the records.
268
- $ components ['columns ' ] .= $ this ->compileOver ($ components ['orders ' ]);
269
-
270
- unset($ components ['orders ' ]);
271
-
272
- if ($ this ->queryOrderContainsSubquery ($ query )) {
273
- $ query ->bindings = $ this ->sortBindingsForSubqueryOrderBy ($ query );
274
- }
275
-
276
- // Next we need to calculate the constraints that should be placed on the query
277
- // to get the right offset and limit from our query but if there is no limit
278
- // set we will just handle the offset only since that is all that matters.
279
- $ sql = $ this ->concatenate ($ components );
280
-
281
- return $ this ->compileTableExpression ($ sql , $ query );
282
- }
283
-
284
- /**
285
- * Compile the over statement for a table expression.
286
- *
287
- * @param string $orderings
288
- * @return string
289
- */
290
- protected function compileOver ($ orderings )
291
- {
292
- return ", row_number() over ( {$ orderings }) as row_num " ;
293
- }
294
-
295
- /**
296
- * Determine if the query's order by clauses contain a subquery.
297
- *
298
- * @param \Illuminate\Database\Query\Builder $query
299
- * @return bool
300
- */
301
- protected function queryOrderContainsSubquery ($ query )
302
- {
303
- if (! is_array ($ query ->orders )) {
304
- return false ;
305
- }
306
-
307
- return Arr::first ($ query ->orders , function ($ value ) {
308
- return $ this ->isExpression ($ value ['column ' ] ?? null );
309
- }, false ) !== false ;
310
- }
311
-
312
254
/**
313
255
* Move the order bindings to be after the "select" statement to account for an order by subquery.
314
256
*
@@ -322,20 +264,6 @@ protected function sortBindingsForSubqueryOrderBy($query)
322
264
});
323
265
}
324
266
325
- /**
326
- * Compile a common table expression for a query.
327
- *
328
- * @param string $sql
329
- * @param \Illuminate\Database\Query\Builder $query
330
- * @return string
331
- */
332
- protected function compileTableExpression ($ sql , $ query )
333
- {
334
- $ constraint = $ this ->compileRowConstraint ($ query );
335
-
336
- return "select * from ( {$ sql }) as temp_table where row_num {$ constraint } order by row_num " ;
337
- }
338
-
339
267
/**
340
268
* Compile the limit / offset row constraint for a query.
341
269
*
@@ -392,6 +320,10 @@ public function compileRandom($seed)
392
320
*/
393
321
protected function compileLimit (Builder $ query , $ limit )
394
322
{
323
+ if ($ limit && $ query ->offset > 0 ) {
324
+ return "fetch next {$ limit } rows only " ;
325
+ }
326
+
395
327
return '' ;
396
328
}
397
329
@@ -404,6 +336,10 @@ protected function compileLimit(Builder $query, $limit)
404
336
*/
405
337
protected function compileOffset (Builder $ query , $ offset )
406
338
{
339
+ if ($ offset ) {
340
+ return "offset {$ offset } rows " ;
341
+ }
342
+
407
343
return '' ;
408
344
}
409
345
0 commit comments