MSSQL drivery – uprava applyLimit()
- knedle
- Člen | 34
pokusil jsem si zprovoznit strankovani (limit, offset) u mssql driveru, tj. upravil cast fce applyLimit() v příslušném mssql driveru (u mne na windows tedy sqlsrv), kde je při definovaném offsetu Exception…
if ($offset) {
throw new Nette\NotSupportedException('Offset is not supported by this database.');
}
vycházel jsem z:
https://forum.dibiphp.com/…-strankovani
má úprava
/**
* Injects LIMIT/OFFSET to the SQL query.
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($offset > 0) {
preg_match("#^\s*SELECT\s(.*)\sFROM\s([^\s]+)#i", $sql, $matches);
$columns = $matches[1];
$table = $matches[2];
if (preg_match("#ORDER\sBY\s(.*)#i", $sql, $matches)) {
$order = $matches[1];
} else {
throw new Nette\NotSupportedException('SQL must have ORDER BY ...');
}
$where = '';
if (preg_match("#WHERE\s(.+)\s(ORDER|GROUP|HAVING)#i", $sql, $matches)) {
$where = $matches[1];
}
$sql = 'SELECT '.$columns.'
FROM (
SELECT '.$columns.', ROW_NUMBER() OVER (ORDER BY '.$order.') AS RowNum
FROM '.$table. (!empty($where) ? ' WHERE '. $where : '') .'
) AS tbl
WHERE tbl.RowNum BETWEEN '.$offset.'+1 AND '.$offset.'+'.$limit.'';
//throw new Nette\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit >= 0) {
$sql = preg_replace('#^\s*(SELECT|UPDATE|DELETE)#i', '$0 TOP ' . (int) $limit, $sql, 1, $count);
if (!$count) {
throw new Nette\InvalidArgumentException('SQL query must begin with SELECT, UPDATE or DELETE command.');
}
}
}
je nedokonalá v mnoha směrech:
- neporadí s případmým join
- regulární výraz nějaký guru může zvládnout napoprvé…
- v REGEXP tuším i další problémy
- …
nicméně pro jednoduchou tabulku to funguje a mně to zatím stačí
třeba se toho chytne někdo chytřejší a doladí to do lepšího stavu
- svobodai
- Člen | 136
Já jsem si udělal úpravu pouze pro MSSQL 2012, který už má implementovánu funkci pro offset.
public function applyLimit(&$sql, $limit, $offset) {
if ($offset > 0) {
if ($limit = 0) {
$limit = 20;
}
if (!preg_match("#ORDER\sBY\s(.*)#i", $sql)) {
$sql .= ' ORDER BY 1';
}
$sql .= ' OFFSET ' . $offset . ' ROWS FETCH NEXT ' . $limit . ' ROWS ONLY';
} elseif ($limit >= 0) {
$sql = preg_replace('#^\s*(SELECT|UPDATE|DELETE)#i', '$0 TOP ' . (int) $limit, $sql, 1, $count);
if (!$count) {
throw new Nette\InvalidArgumentException('SQL query must begin with SELECT, UPDATE or DELETE command.');
}
}
}
jen nevím jak to udělat, aby to bylo jen pro ten MSSQL2012
Editoval svobodai (25. 10. 2014 11:20)
- svobodai
- Člen | 136
knedle napsal(a):
pokusil jsem si zprovoznit strankovani (limit, offset) u mssql driveru, tj. upravil cast fce applyLimit() v příslušném mssql driveru (u mne na windows tedy sqlsrv), kde je při definovaném offsetu Exception…
if ($offset) { throw new Nette\NotSupportedException('Offset is not supported by this database.'); }
vycházel jsem z:
https://forum.dibiphp.com/…-strankovani
má úprava
/** * Injects LIMIT/OFFSET to the SQL query. */ public function applyLimit(& $sql, $limit, $offset) { if ($offset > 0) { preg_match("#^\s*SELECT\s(.*)\sFROM\s([^\s]+)#i", $sql, $matches); $columns = $matches[1]; $table = $matches[2]; if (preg_match("#ORDER\sBY\s(.*)#i", $sql, $matches)) { $order = $matches[1]; } else { throw new Nette\NotSupportedException('SQL must have ORDER BY ...'); } $where = ''; if (preg_match("#WHERE\s(.+)\s(ORDER|GROUP|HAVING)#i", $sql, $matches)) { $where = $matches[1]; } $sql = 'SELECT '.$columns.' FROM ( SELECT '.$columns.', ROW_NUMBER() OVER (ORDER BY '.$order.') AS RowNum FROM '.$table. (!empty($where) ? ' WHERE '. $where : '') .' ) AS tbl WHERE tbl.RowNum BETWEEN '.$offset.'+1 AND '.$offset.'+'.$limit.''; //throw new Nette\NotSupportedException('Offset is not supported by this database.'); } elseif ($limit >= 0) { $sql = preg_replace('#^\s*(SELECT|UPDATE|DELETE)#i', '$0 TOP ' . (int) $limit, $sql, 1, $count); if (!$count) { throw new Nette\InvalidArgumentException('SQL query must begin with SELECT, UPDATE or DELETE command.'); } } }
je nedokonalá v mnoha směrech:
- neporadí s případmým join
- regulární výraz nějaký guru může zvládnout napoprvé…
- v REGEXP tuším i další problémy
- …
nicméně pro jednoduchou tabulku to funguje a mně to zatím stačí
třeba se toho chytne někdo chytřejší a doladí to do lepšího stavu
Pokud není v dotazu Order by tak nemusíš vyhazovat exception, ale můžeš do tohot dotazu dát ORBER BY 1 což zajistí řazení podle prvního sloupce v SELECTu.