sqlserver.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <?php namespace Laravel\Database\Query\Grammars;
  2. use Laravel\Database\Query;
  3. class SQLServer extends Grammar {
  4. /**
  5. * The keyword identifier for the database system.
  6. *
  7. * @var string
  8. */
  9. protected $wrapper = '[%s]';
  10. /**
  11. * Compile a SQL SELECT statement from a Query instance.
  12. *
  13. * @param Query $query
  14. * @return string
  15. */
  16. public function select(Query $query)
  17. {
  18. $sql = parent::components($query);
  19. // SQL Server does not currently implement an "OFFSET" type keyword, so we
  20. // actually have to generate the ANSI standard SQL for doing offset like
  21. // functionality. In the next version of SQL Server, an OFFSET like
  22. // keyword is included for convenience.
  23. if ($query->offset > 0)
  24. {
  25. return $this->ansi_offset($query, $sql);
  26. }
  27. // Once all of the clauses have been compiled, we can join them all as
  28. // one statement. Any segments that are null or an empty string will
  29. // be removed from the array of clauses before they are imploded.
  30. return $this->concatenate($sql);
  31. }
  32. /**
  33. * Compile the SELECT clause for a query.
  34. *
  35. * @param Query $query
  36. * @return string
  37. */
  38. protected function selects(Query $query)
  39. {
  40. $select = ($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT ';
  41. // Instead of using a "LIMIT" keyword, SQL Server uses the "TOP"
  42. // keyword within the SELECT statement. So, if we have a limit,
  43. // we will add it here.
  44. //
  45. // We will not add the TOP clause if there is an offset however,
  46. // since we will have to handle offsets using the ANSI syntax
  47. // and will need to remove the TOP clause in that situation.
  48. if ($query->limit > 0 and $query->offset <= 0)
  49. {
  50. $select .= 'TOP '.$query->limit.' ';
  51. }
  52. return $select.$this->columnize($query->selects);
  53. }
  54. /**
  55. * Generate the ANSI standard SQL for an offset clause.
  56. *
  57. * @param Query $query
  58. * @param array $components
  59. * @return array
  60. */
  61. protected function ansi_offset(Query $query, $components)
  62. {
  63. // An ORDER BY clause is required to make this offset query
  64. // work, so if one doesn't exist, we'll just create a dummy
  65. // clause to satisfy the database.
  66. if ( ! isset($components['orderings']))
  67. {
  68. $components['orderings'] = 'ORDER BY (SELECT 0)';
  69. }
  70. // We need to add the row number to the query results so we
  71. // can compare it against the offset and limit values given
  72. // for the statement. To do that we'll add an expression to
  73. // the select statement for the row number.
  74. $orderings = $components['orderings'];
  75. $components['selects'] .= ", ROW_NUMBER() OVER ({$orderings}) AS RowNum";
  76. unset($components['orderings']);
  77. $start = $query->offset + 1;
  78. // Next we need to calculate the constraint that should be
  79. // placed on the row number to get the correct offset and
  80. // limit on the query. If a limit has not been set, we'll
  81. // only add a constraint to handle offset.
  82. if ($query->limit > 0)
  83. {
  84. $finish = $query->offset + $query->limit;
  85. $constraint = "BETWEEN {$start} AND {$finish}";
  86. }
  87. else
  88. {
  89. $constraint = ">= {$start}";
  90. }
  91. // Now, we're finally ready to build the final SQL query.
  92. // We'll create a common table expression with the query
  93. // and then select all of the results from it where the
  94. // row number is between oru given limit and offset.
  95. $sql = $this->concatenate($components);
  96. return "SELECT * FROM ($sql) AS TempTable WHERE RowNum {$constraint}";
  97. }
  98. /**
  99. * Compile the LIMIT clause for a query.
  100. *
  101. * @param Query $query
  102. * @return string
  103. */
  104. protected function limit(Query $query)
  105. {
  106. return;
  107. }
  108. /**
  109. * Compile the OFFSET clause for a query.
  110. *
  111. * @param Query $query
  112. * @return string
  113. */
  114. protected function offset(Query $query)
  115. {
  116. return;
  117. }
  118. }