How to optimize limit query to access data faster from a huge table?
I am trying to fetch data from a table of size 9 GB + and having millions of records. I'm populating DataTable with that data. I am getting the records in chunks from the table i.e 10 per page through Ajax and SQL Limit query.
pagination
In the above image you can see We have 223,740
pages so when i try to access the last page the query takes forever to load the data. However data loads quicker when i try to access first pages. But accessing higher offset pages directly takes forever to load.
public static function getAllEvaluationsWithNameForDataTable($start){
$queryBuilder = new Builder();
return $queryBuilder
->from(array('e' => static::class))
->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u')
->columns('e.id, e.first_name, u.initials as assigned_coach, e.gender, e.email, e.phone, e.age, e.version, e.evaluation_status, e.ip_address, e.date_created, e.date_updated')
->orderBy('e.id asc')
->limit(10, $start)
->getQuery()
->execute()
->toArray();
}
PHP Function/Controller:
public function getEvaluationsAction() {
// Enable Json response
$this->setJsonResponse();
// This action can be called only via ajax
$this->requireAjax();
// Forward to access denied if current user is not allowed to view evaluation details
if (!$this->CxAuth->currentUserIsAllowedTo('VIEW', CxEbEvaluation::getClassResourceName()))
return $this->forwardToAccessDeniedError();
if(isset($_GET['start'])){
$start = $this->request->get('start');
}else{
$start = 10;
}
$recordsTotal = count(CxEbEvaluation::getAllForDataTable(array('id')));
//Get Evaluations from DB
$evaluation_quizzes = CxEbEvaluation::getAllEvaluationsWithNameForDataTable(intval($start));
//for getting base URL
$url = new Url();
$data = array();
foreach ($evaluation_quizzes as $key => $quiz) {
$data[ $key ][ 'id' ] = $quiz[ 'id' ];
$data[ $key ][ 'first_name' ] = $quiz[ 'first_name' ];
if($quiz[ 'assigned_coach' ]){
$data[ $key ][ 'assigned_coach' ] = $quiz['assigned_coach'];
}else{
$data[ $key ][ 'assigned_coach' ] = "Not assigned";
}
$data[ $key ][ 'gender' ] = $quiz[ 'gender' ];
$data[ $key ][ 'email' ] = $quiz[ 'email' ];
$data[ $key ][ 'phone' ] = $quiz[ 'phone' ];
$data[ $key ][ 'age' ] = $quiz[ 'age' ];
$data[ $key ][ 'version' ] = $quiz[ 'version' ];
$data[ $key ][ 'quiz' ] = $url->get('/admin/get-evaluation-quiz-by-id');
$data[ $key ][ 'manage-notes-messages-and-calls' ] = $url->get('/admin/manage-notes-messages-and-calls');
$data[ $key ][ 'date_created' ] = date("m/d/Y H:i:s", $quiz[ 'date_created' ]);
$data[ $key ][ 'evaluation_status' ] = $quiz[ 'evaluation_status' ];
}
// Return data array
return array(
"recordsTotal" => $recordsTotal,
"recordsFiltered" => $recordsTotal ,
"data" => $data //How To Retrieve This Data
);
// Return data
}
Javascript:
cx.common.data.cxAdminDataTables.EbEvaluation = $CxRecordsTable.cxAdminDataTable({
ajaxUrl: '<?php echo $this->CxHelper->Route('eb-admin-get-evaluations')?>' + eqQuizIdQueryString,
serverSide: true,
processing: true,
recordsFiltered :true,
columns: [
cx.common.admin.tableEditColumn('id',{ delete: true }),
{ data: 'first_name' },
{ data: 'assigned_coach' },
{ data: 'gender' },
{ data: 'email' },
{ data: 'phone' },
{ data: 'age' },
cx.common.admin.tableLinkColumn('quiz', quizLinkOptions),
cx.common.admin.tableEditColumn('id', healthCoachLinkOptions),
cx.common.admin.tableLinkColumn('manage-notes-messages-and-calls', manageNotesMessagesAndCalls),
{ data: 'date_created' },
cx.common.admin.tableSwitchableColumn('evaluation_status', {
editable: true,
createdCell: function (td, cellData, rowData, row, col){
$(td).data('evaluation-status-id', rowData.id);
},
onText: 'Complete',
offText: 'In progress'
})
],
toolbarOptions:{
enabled: false
}, success: function (data) {
cx.common.data.cxAdminDataTables.EbEvaluation.cxAdminDataTable("reloadAjax");
}
});
}
else {
$row.removeClass('alert');
}
});
}
});
I hope the question is clear. If anything else is required just update me I will provide.
(From Comment)
SELECT e.id` AS id, e.first_name AS first_name,
u.initials AS assigned_coach,
e.gender AS gender, e.email AS email, e.phone AS phone,
e.age AS age, e.version AS version,
e.evaluation_status AS evaluation_status,
e.ip_address AS ip_address, e.date_created AS date_created,
e.date_updated AS date_updated
FROM evaluation_client AS e
LEFT JOIN cx_user AS u ON e.cx_hc_user_id = u.id
ORDER BY e.id ASC
LIMIT :APL0 OFFSET, :APL1
php mysql sql performance datatable
|
show 5 more comments
I am trying to fetch data from a table of size 9 GB + and having millions of records. I'm populating DataTable with that data. I am getting the records in chunks from the table i.e 10 per page through Ajax and SQL Limit query.
pagination
In the above image you can see We have 223,740
pages so when i try to access the last page the query takes forever to load the data. However data loads quicker when i try to access first pages. But accessing higher offset pages directly takes forever to load.
public static function getAllEvaluationsWithNameForDataTable($start){
$queryBuilder = new Builder();
return $queryBuilder
->from(array('e' => static::class))
->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u')
->columns('e.id, e.first_name, u.initials as assigned_coach, e.gender, e.email, e.phone, e.age, e.version, e.evaluation_status, e.ip_address, e.date_created, e.date_updated')
->orderBy('e.id asc')
->limit(10, $start)
->getQuery()
->execute()
->toArray();
}
PHP Function/Controller:
public function getEvaluationsAction() {
// Enable Json response
$this->setJsonResponse();
// This action can be called only via ajax
$this->requireAjax();
// Forward to access denied if current user is not allowed to view evaluation details
if (!$this->CxAuth->currentUserIsAllowedTo('VIEW', CxEbEvaluation::getClassResourceName()))
return $this->forwardToAccessDeniedError();
if(isset($_GET['start'])){
$start = $this->request->get('start');
}else{
$start = 10;
}
$recordsTotal = count(CxEbEvaluation::getAllForDataTable(array('id')));
//Get Evaluations from DB
$evaluation_quizzes = CxEbEvaluation::getAllEvaluationsWithNameForDataTable(intval($start));
//for getting base URL
$url = new Url();
$data = array();
foreach ($evaluation_quizzes as $key => $quiz) {
$data[ $key ][ 'id' ] = $quiz[ 'id' ];
$data[ $key ][ 'first_name' ] = $quiz[ 'first_name' ];
if($quiz[ 'assigned_coach' ]){
$data[ $key ][ 'assigned_coach' ] = $quiz['assigned_coach'];
}else{
$data[ $key ][ 'assigned_coach' ] = "Not assigned";
}
$data[ $key ][ 'gender' ] = $quiz[ 'gender' ];
$data[ $key ][ 'email' ] = $quiz[ 'email' ];
$data[ $key ][ 'phone' ] = $quiz[ 'phone' ];
$data[ $key ][ 'age' ] = $quiz[ 'age' ];
$data[ $key ][ 'version' ] = $quiz[ 'version' ];
$data[ $key ][ 'quiz' ] = $url->get('/admin/get-evaluation-quiz-by-id');
$data[ $key ][ 'manage-notes-messages-and-calls' ] = $url->get('/admin/manage-notes-messages-and-calls');
$data[ $key ][ 'date_created' ] = date("m/d/Y H:i:s", $quiz[ 'date_created' ]);
$data[ $key ][ 'evaluation_status' ] = $quiz[ 'evaluation_status' ];
}
// Return data array
return array(
"recordsTotal" => $recordsTotal,
"recordsFiltered" => $recordsTotal ,
"data" => $data //How To Retrieve This Data
);
// Return data
}
Javascript:
cx.common.data.cxAdminDataTables.EbEvaluation = $CxRecordsTable.cxAdminDataTable({
ajaxUrl: '<?php echo $this->CxHelper->Route('eb-admin-get-evaluations')?>' + eqQuizIdQueryString,
serverSide: true,
processing: true,
recordsFiltered :true,
columns: [
cx.common.admin.tableEditColumn('id',{ delete: true }),
{ data: 'first_name' },
{ data: 'assigned_coach' },
{ data: 'gender' },
{ data: 'email' },
{ data: 'phone' },
{ data: 'age' },
cx.common.admin.tableLinkColumn('quiz', quizLinkOptions),
cx.common.admin.tableEditColumn('id', healthCoachLinkOptions),
cx.common.admin.tableLinkColumn('manage-notes-messages-and-calls', manageNotesMessagesAndCalls),
{ data: 'date_created' },
cx.common.admin.tableSwitchableColumn('evaluation_status', {
editable: true,
createdCell: function (td, cellData, rowData, row, col){
$(td).data('evaluation-status-id', rowData.id);
},
onText: 'Complete',
offText: 'In progress'
})
],
toolbarOptions:{
enabled: false
}, success: function (data) {
cx.common.data.cxAdminDataTables.EbEvaluation.cxAdminDataTable("reloadAjax");
}
});
}
else {
$row.removeClass('alert');
}
});
}
});
I hope the question is clear. If anything else is required just update me I will provide.
(From Comment)
SELECT e.id` AS id, e.first_name AS first_name,
u.initials AS assigned_coach,
e.gender AS gender, e.email AS email, e.phone AS phone,
e.age AS age, e.version AS version,
e.evaluation_status AS evaluation_status,
e.ip_address AS ip_address, e.date_created AS date_created,
e.date_updated AS date_updated
FROM evaluation_client AS e
LEFT JOIN cx_user AS u ON e.cx_hc_user_id = u.id
ORDER BY e.id ASC
LIMIT :APL0 OFFSET, :APL1
php mysql sql performance datatable
dba.stackexchange.com/questions/66294/…
– Masivuye Cokile
Nov 12 at 15:07
dba.stackexchange.com/questions/75963/…
– Masivuye Cokile
Nov 12 at 15:07
1
stackoverflow.com/questions/4481388/…
– Masivuye Cokile
Nov 12 at 15:08
you need an index.
– Hogan
Nov 12 at 15:12
@MasivuyeCokile tried the above stackoverflow link but it isn't related to my scenario.
– DojoDev
Nov 12 at 15:14
|
show 5 more comments
I am trying to fetch data from a table of size 9 GB + and having millions of records. I'm populating DataTable with that data. I am getting the records in chunks from the table i.e 10 per page through Ajax and SQL Limit query.
pagination
In the above image you can see We have 223,740
pages so when i try to access the last page the query takes forever to load the data. However data loads quicker when i try to access first pages. But accessing higher offset pages directly takes forever to load.
public static function getAllEvaluationsWithNameForDataTable($start){
$queryBuilder = new Builder();
return $queryBuilder
->from(array('e' => static::class))
->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u')
->columns('e.id, e.first_name, u.initials as assigned_coach, e.gender, e.email, e.phone, e.age, e.version, e.evaluation_status, e.ip_address, e.date_created, e.date_updated')
->orderBy('e.id asc')
->limit(10, $start)
->getQuery()
->execute()
->toArray();
}
PHP Function/Controller:
public function getEvaluationsAction() {
// Enable Json response
$this->setJsonResponse();
// This action can be called only via ajax
$this->requireAjax();
// Forward to access denied if current user is not allowed to view evaluation details
if (!$this->CxAuth->currentUserIsAllowedTo('VIEW', CxEbEvaluation::getClassResourceName()))
return $this->forwardToAccessDeniedError();
if(isset($_GET['start'])){
$start = $this->request->get('start');
}else{
$start = 10;
}
$recordsTotal = count(CxEbEvaluation::getAllForDataTable(array('id')));
//Get Evaluations from DB
$evaluation_quizzes = CxEbEvaluation::getAllEvaluationsWithNameForDataTable(intval($start));
//for getting base URL
$url = new Url();
$data = array();
foreach ($evaluation_quizzes as $key => $quiz) {
$data[ $key ][ 'id' ] = $quiz[ 'id' ];
$data[ $key ][ 'first_name' ] = $quiz[ 'first_name' ];
if($quiz[ 'assigned_coach' ]){
$data[ $key ][ 'assigned_coach' ] = $quiz['assigned_coach'];
}else{
$data[ $key ][ 'assigned_coach' ] = "Not assigned";
}
$data[ $key ][ 'gender' ] = $quiz[ 'gender' ];
$data[ $key ][ 'email' ] = $quiz[ 'email' ];
$data[ $key ][ 'phone' ] = $quiz[ 'phone' ];
$data[ $key ][ 'age' ] = $quiz[ 'age' ];
$data[ $key ][ 'version' ] = $quiz[ 'version' ];
$data[ $key ][ 'quiz' ] = $url->get('/admin/get-evaluation-quiz-by-id');
$data[ $key ][ 'manage-notes-messages-and-calls' ] = $url->get('/admin/manage-notes-messages-and-calls');
$data[ $key ][ 'date_created' ] = date("m/d/Y H:i:s", $quiz[ 'date_created' ]);
$data[ $key ][ 'evaluation_status' ] = $quiz[ 'evaluation_status' ];
}
// Return data array
return array(
"recordsTotal" => $recordsTotal,
"recordsFiltered" => $recordsTotal ,
"data" => $data //How To Retrieve This Data
);
// Return data
}
Javascript:
cx.common.data.cxAdminDataTables.EbEvaluation = $CxRecordsTable.cxAdminDataTable({
ajaxUrl: '<?php echo $this->CxHelper->Route('eb-admin-get-evaluations')?>' + eqQuizIdQueryString,
serverSide: true,
processing: true,
recordsFiltered :true,
columns: [
cx.common.admin.tableEditColumn('id',{ delete: true }),
{ data: 'first_name' },
{ data: 'assigned_coach' },
{ data: 'gender' },
{ data: 'email' },
{ data: 'phone' },
{ data: 'age' },
cx.common.admin.tableLinkColumn('quiz', quizLinkOptions),
cx.common.admin.tableEditColumn('id', healthCoachLinkOptions),
cx.common.admin.tableLinkColumn('manage-notes-messages-and-calls', manageNotesMessagesAndCalls),
{ data: 'date_created' },
cx.common.admin.tableSwitchableColumn('evaluation_status', {
editable: true,
createdCell: function (td, cellData, rowData, row, col){
$(td).data('evaluation-status-id', rowData.id);
},
onText: 'Complete',
offText: 'In progress'
})
],
toolbarOptions:{
enabled: false
}, success: function (data) {
cx.common.data.cxAdminDataTables.EbEvaluation.cxAdminDataTable("reloadAjax");
}
});
}
else {
$row.removeClass('alert');
}
});
}
});
I hope the question is clear. If anything else is required just update me I will provide.
(From Comment)
SELECT e.id` AS id, e.first_name AS first_name,
u.initials AS assigned_coach,
e.gender AS gender, e.email AS email, e.phone AS phone,
e.age AS age, e.version AS version,
e.evaluation_status AS evaluation_status,
e.ip_address AS ip_address, e.date_created AS date_created,
e.date_updated AS date_updated
FROM evaluation_client AS e
LEFT JOIN cx_user AS u ON e.cx_hc_user_id = u.id
ORDER BY e.id ASC
LIMIT :APL0 OFFSET, :APL1
php mysql sql performance datatable
I am trying to fetch data from a table of size 9 GB + and having millions of records. I'm populating DataTable with that data. I am getting the records in chunks from the table i.e 10 per page through Ajax and SQL Limit query.
pagination
In the above image you can see We have 223,740
pages so when i try to access the last page the query takes forever to load the data. However data loads quicker when i try to access first pages. But accessing higher offset pages directly takes forever to load.
public static function getAllEvaluationsWithNameForDataTable($start){
$queryBuilder = new Builder();
return $queryBuilder
->from(array('e' => static::class))
->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u')
->columns('e.id, e.first_name, u.initials as assigned_coach, e.gender, e.email, e.phone, e.age, e.version, e.evaluation_status, e.ip_address, e.date_created, e.date_updated')
->orderBy('e.id asc')
->limit(10, $start)
->getQuery()
->execute()
->toArray();
}
PHP Function/Controller:
public function getEvaluationsAction() {
// Enable Json response
$this->setJsonResponse();
// This action can be called only via ajax
$this->requireAjax();
// Forward to access denied if current user is not allowed to view evaluation details
if (!$this->CxAuth->currentUserIsAllowedTo('VIEW', CxEbEvaluation::getClassResourceName()))
return $this->forwardToAccessDeniedError();
if(isset($_GET['start'])){
$start = $this->request->get('start');
}else{
$start = 10;
}
$recordsTotal = count(CxEbEvaluation::getAllForDataTable(array('id')));
//Get Evaluations from DB
$evaluation_quizzes = CxEbEvaluation::getAllEvaluationsWithNameForDataTable(intval($start));
//for getting base URL
$url = new Url();
$data = array();
foreach ($evaluation_quizzes as $key => $quiz) {
$data[ $key ][ 'id' ] = $quiz[ 'id' ];
$data[ $key ][ 'first_name' ] = $quiz[ 'first_name' ];
if($quiz[ 'assigned_coach' ]){
$data[ $key ][ 'assigned_coach' ] = $quiz['assigned_coach'];
}else{
$data[ $key ][ 'assigned_coach' ] = "Not assigned";
}
$data[ $key ][ 'gender' ] = $quiz[ 'gender' ];
$data[ $key ][ 'email' ] = $quiz[ 'email' ];
$data[ $key ][ 'phone' ] = $quiz[ 'phone' ];
$data[ $key ][ 'age' ] = $quiz[ 'age' ];
$data[ $key ][ 'version' ] = $quiz[ 'version' ];
$data[ $key ][ 'quiz' ] = $url->get('/admin/get-evaluation-quiz-by-id');
$data[ $key ][ 'manage-notes-messages-and-calls' ] = $url->get('/admin/manage-notes-messages-and-calls');
$data[ $key ][ 'date_created' ] = date("m/d/Y H:i:s", $quiz[ 'date_created' ]);
$data[ $key ][ 'evaluation_status' ] = $quiz[ 'evaluation_status' ];
}
// Return data array
return array(
"recordsTotal" => $recordsTotal,
"recordsFiltered" => $recordsTotal ,
"data" => $data //How To Retrieve This Data
);
// Return data
}
Javascript:
cx.common.data.cxAdminDataTables.EbEvaluation = $CxRecordsTable.cxAdminDataTable({
ajaxUrl: '<?php echo $this->CxHelper->Route('eb-admin-get-evaluations')?>' + eqQuizIdQueryString,
serverSide: true,
processing: true,
recordsFiltered :true,
columns: [
cx.common.admin.tableEditColumn('id',{ delete: true }),
{ data: 'first_name' },
{ data: 'assigned_coach' },
{ data: 'gender' },
{ data: 'email' },
{ data: 'phone' },
{ data: 'age' },
cx.common.admin.tableLinkColumn('quiz', quizLinkOptions),
cx.common.admin.tableEditColumn('id', healthCoachLinkOptions),
cx.common.admin.tableLinkColumn('manage-notes-messages-and-calls', manageNotesMessagesAndCalls),
{ data: 'date_created' },
cx.common.admin.tableSwitchableColumn('evaluation_status', {
editable: true,
createdCell: function (td, cellData, rowData, row, col){
$(td).data('evaluation-status-id', rowData.id);
},
onText: 'Complete',
offText: 'In progress'
})
],
toolbarOptions:{
enabled: false
}, success: function (data) {
cx.common.data.cxAdminDataTables.EbEvaluation.cxAdminDataTable("reloadAjax");
}
});
}
else {
$row.removeClass('alert');
}
});
}
});
I hope the question is clear. If anything else is required just update me I will provide.
(From Comment)
SELECT e.id` AS id, e.first_name AS first_name,
u.initials AS assigned_coach,
e.gender AS gender, e.email AS email, e.phone AS phone,
e.age AS age, e.version AS version,
e.evaluation_status AS evaluation_status,
e.ip_address AS ip_address, e.date_created AS date_created,
e.date_updated AS date_updated
FROM evaluation_client AS e
LEFT JOIN cx_user AS u ON e.cx_hc_user_id = u.id
ORDER BY e.id ASC
LIMIT :APL0 OFFSET, :APL1
php mysql sql performance datatable
php mysql sql performance datatable
edited Nov 12 at 16:41
Rick James
65.8k55897
65.8k55897
asked Nov 12 at 15:04
DojoDev
336
336
dba.stackexchange.com/questions/66294/…
– Masivuye Cokile
Nov 12 at 15:07
dba.stackexchange.com/questions/75963/…
– Masivuye Cokile
Nov 12 at 15:07
1
stackoverflow.com/questions/4481388/…
– Masivuye Cokile
Nov 12 at 15:08
you need an index.
– Hogan
Nov 12 at 15:12
@MasivuyeCokile tried the above stackoverflow link but it isn't related to my scenario.
– DojoDev
Nov 12 at 15:14
|
show 5 more comments
dba.stackexchange.com/questions/66294/…
– Masivuye Cokile
Nov 12 at 15:07
dba.stackexchange.com/questions/75963/…
– Masivuye Cokile
Nov 12 at 15:07
1
stackoverflow.com/questions/4481388/…
– Masivuye Cokile
Nov 12 at 15:08
you need an index.
– Hogan
Nov 12 at 15:12
@MasivuyeCokile tried the above stackoverflow link but it isn't related to my scenario.
– DojoDev
Nov 12 at 15:14
dba.stackexchange.com/questions/66294/…
– Masivuye Cokile
Nov 12 at 15:07
dba.stackexchange.com/questions/66294/…
– Masivuye Cokile
Nov 12 at 15:07
dba.stackexchange.com/questions/75963/…
– Masivuye Cokile
Nov 12 at 15:07
dba.stackexchange.com/questions/75963/…
– Masivuye Cokile
Nov 12 at 15:07
1
1
stackoverflow.com/questions/4481388/…
– Masivuye Cokile
Nov 12 at 15:08
stackoverflow.com/questions/4481388/…
– Masivuye Cokile
Nov 12 at 15:08
you need an index.
– Hogan
Nov 12 at 15:12
you need an index.
– Hogan
Nov 12 at 15:12
@MasivuyeCokile tried the above stackoverflow link but it isn't related to my scenario.
– DojoDev
Nov 12 at 15:14
@MasivuyeCokile tried the above stackoverflow link but it isn't related to my scenario.
– DojoDev
Nov 12 at 15:14
|
show 5 more comments
3 Answers
3
active
oldest
votes
The Why does MYSQL higher LIMIT offset slow the query down? question and answers, linked by Masivuye Cokile, as well as https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ link provided there, contain an excellent rundown on why large offset queries are slow. Basically, for LIMIT 150000, 10
MySQL still scans the entire 150000 rows even if it discards them later. To speed it up you can either:
- use sequential pagination, i.e. "show 10 entries after ID #N", which works very fast and is a good alternative, but discards actual page number; your users will be left with "next/prev" links and/or an approximate page number you can calculate using a
count
query. - or create an index on
id
, then force mysql to perform an index-only search.
For the second approach, you'll have to rewrite the query from
SELECT ...
FROM table t
WHERE ...
ORDER by t.id ASC
LIMIT 150000, 10
to
SELECT ...
FROM (
SELECT id
FROM table
ORDER BY
id ASC
LIMIT 150000, 10
) o
JOIN table t
ON t.id = o.id
WHERE ...
ORDER BY t.id ASC
Alternatively, since you're not confined to the single query, you can retrieve the ID of the first item on your page using
SELECT id
FROM table
ORDER BY id ASC
LIMIT 150000, 1
then use said id to retrieve actual data:
SELECT ...
FROM table
WHERE id >= $id
AND ...
ORDER BY id ASC
LIMIT 0, 10
1
More on 'remembering where you left off'.
– Rick James
Nov 12 at 16:45
@Timekiller where to find the id of first item? Do you mean $start variable of datatable?
– DojoDev
Nov 18 at 7:16
First item overall or first item on page? If you mean overall, eitherselect min(id)
or use something likeid >= 0
; if you mean first item on page, look at my second to last query and adjust thelimit 150000, 1
part so that offset matchespages*items_per_page
instead of 150000.
– Timekiller
Nov 19 at 18:26
add a comment |
The pattern SELECT whatever FROM vast_table ORDER BY something LIMIT 10 large_number
is a notorious performance antipattern. Why? Because it has to examine a great many rows just to return a few.
If your id
value is a primary key (or any indexed column) you can paginate by
SELECT whatever FROM vast_table WHERE id BETWEEN large_value AND large_value+9 ORDER BY id;
Or you might try
SELECT whatever FROM vast_table WHERE id >= large_value ORDER BY id LIMIT 10;
This doesn't paginate perfectly if your id
values have gaps in them. But it performs tolerably well.
Hello @O. Jones i already tried similar but as you mentioned it skips records.return $queryBuilder ->from(array('e' => static::class)) ->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u') ->columns('e.*') ->where('e.id > :ID:') ->orderBy('e.id asc') ->limit(10, $start) ->getQuery() ->execute(array('ID' => $start)) ->toArray();
– DojoDev
Nov 12 at 15:43
@DojoDev - Well, sounds like QueryBuilder is not a good tool for this use.
– Rick James
Nov 12 at 16:48
You shouldn't include your starting point inlimit(10, $start)
. And you probably wantwhere('e.id >= :ID:')
note>=
.
– O. Jones
Nov 12 at 19:33
add a comment |
The issue was related to dates column datatype in my table. I was using int
datatype for dates fields and when I changed the datatype of my dates column to datetime
, the search results were in seconds.
Source where i found the solution @ http://dbscience.blogspot.com/2008/08/can-timestamp-be-slower-than-datetime.html
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53264899%2fhow-to-optimize-limit-query-to-access-data-faster-from-a-huge-table%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
The Why does MYSQL higher LIMIT offset slow the query down? question and answers, linked by Masivuye Cokile, as well as https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ link provided there, contain an excellent rundown on why large offset queries are slow. Basically, for LIMIT 150000, 10
MySQL still scans the entire 150000 rows even if it discards them later. To speed it up you can either:
- use sequential pagination, i.e. "show 10 entries after ID #N", which works very fast and is a good alternative, but discards actual page number; your users will be left with "next/prev" links and/or an approximate page number you can calculate using a
count
query. - or create an index on
id
, then force mysql to perform an index-only search.
For the second approach, you'll have to rewrite the query from
SELECT ...
FROM table t
WHERE ...
ORDER by t.id ASC
LIMIT 150000, 10
to
SELECT ...
FROM (
SELECT id
FROM table
ORDER BY
id ASC
LIMIT 150000, 10
) o
JOIN table t
ON t.id = o.id
WHERE ...
ORDER BY t.id ASC
Alternatively, since you're not confined to the single query, you can retrieve the ID of the first item on your page using
SELECT id
FROM table
ORDER BY id ASC
LIMIT 150000, 1
then use said id to retrieve actual data:
SELECT ...
FROM table
WHERE id >= $id
AND ...
ORDER BY id ASC
LIMIT 0, 10
1
More on 'remembering where you left off'.
– Rick James
Nov 12 at 16:45
@Timekiller where to find the id of first item? Do you mean $start variable of datatable?
– DojoDev
Nov 18 at 7:16
First item overall or first item on page? If you mean overall, eitherselect min(id)
or use something likeid >= 0
; if you mean first item on page, look at my second to last query and adjust thelimit 150000, 1
part so that offset matchespages*items_per_page
instead of 150000.
– Timekiller
Nov 19 at 18:26
add a comment |
The Why does MYSQL higher LIMIT offset slow the query down? question and answers, linked by Masivuye Cokile, as well as https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ link provided there, contain an excellent rundown on why large offset queries are slow. Basically, for LIMIT 150000, 10
MySQL still scans the entire 150000 rows even if it discards them later. To speed it up you can either:
- use sequential pagination, i.e. "show 10 entries after ID #N", which works very fast and is a good alternative, but discards actual page number; your users will be left with "next/prev" links and/or an approximate page number you can calculate using a
count
query. - or create an index on
id
, then force mysql to perform an index-only search.
For the second approach, you'll have to rewrite the query from
SELECT ...
FROM table t
WHERE ...
ORDER by t.id ASC
LIMIT 150000, 10
to
SELECT ...
FROM (
SELECT id
FROM table
ORDER BY
id ASC
LIMIT 150000, 10
) o
JOIN table t
ON t.id = o.id
WHERE ...
ORDER BY t.id ASC
Alternatively, since you're not confined to the single query, you can retrieve the ID of the first item on your page using
SELECT id
FROM table
ORDER BY id ASC
LIMIT 150000, 1
then use said id to retrieve actual data:
SELECT ...
FROM table
WHERE id >= $id
AND ...
ORDER BY id ASC
LIMIT 0, 10
1
More on 'remembering where you left off'.
– Rick James
Nov 12 at 16:45
@Timekiller where to find the id of first item? Do you mean $start variable of datatable?
– DojoDev
Nov 18 at 7:16
First item overall or first item on page? If you mean overall, eitherselect min(id)
or use something likeid >= 0
; if you mean first item on page, look at my second to last query and adjust thelimit 150000, 1
part so that offset matchespages*items_per_page
instead of 150000.
– Timekiller
Nov 19 at 18:26
add a comment |
The Why does MYSQL higher LIMIT offset slow the query down? question and answers, linked by Masivuye Cokile, as well as https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ link provided there, contain an excellent rundown on why large offset queries are slow. Basically, for LIMIT 150000, 10
MySQL still scans the entire 150000 rows even if it discards them later. To speed it up you can either:
- use sequential pagination, i.e. "show 10 entries after ID #N", which works very fast and is a good alternative, but discards actual page number; your users will be left with "next/prev" links and/or an approximate page number you can calculate using a
count
query. - or create an index on
id
, then force mysql to perform an index-only search.
For the second approach, you'll have to rewrite the query from
SELECT ...
FROM table t
WHERE ...
ORDER by t.id ASC
LIMIT 150000, 10
to
SELECT ...
FROM (
SELECT id
FROM table
ORDER BY
id ASC
LIMIT 150000, 10
) o
JOIN table t
ON t.id = o.id
WHERE ...
ORDER BY t.id ASC
Alternatively, since you're not confined to the single query, you can retrieve the ID of the first item on your page using
SELECT id
FROM table
ORDER BY id ASC
LIMIT 150000, 1
then use said id to retrieve actual data:
SELECT ...
FROM table
WHERE id >= $id
AND ...
ORDER BY id ASC
LIMIT 0, 10
The Why does MYSQL higher LIMIT offset slow the query down? question and answers, linked by Masivuye Cokile, as well as https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ link provided there, contain an excellent rundown on why large offset queries are slow. Basically, for LIMIT 150000, 10
MySQL still scans the entire 150000 rows even if it discards them later. To speed it up you can either:
- use sequential pagination, i.e. "show 10 entries after ID #N", which works very fast and is a good alternative, but discards actual page number; your users will be left with "next/prev" links and/or an approximate page number you can calculate using a
count
query. - or create an index on
id
, then force mysql to perform an index-only search.
For the second approach, you'll have to rewrite the query from
SELECT ...
FROM table t
WHERE ...
ORDER by t.id ASC
LIMIT 150000, 10
to
SELECT ...
FROM (
SELECT id
FROM table
ORDER BY
id ASC
LIMIT 150000, 10
) o
JOIN table t
ON t.id = o.id
WHERE ...
ORDER BY t.id ASC
Alternatively, since you're not confined to the single query, you can retrieve the ID of the first item on your page using
SELECT id
FROM table
ORDER BY id ASC
LIMIT 150000, 1
then use said id to retrieve actual data:
SELECT ...
FROM table
WHERE id >= $id
AND ...
ORDER BY id ASC
LIMIT 0, 10
edited Nov 24 at 11:07
Valerian Pereira
411311
411311
answered Nov 12 at 15:43
Timekiller
1,80111112
1,80111112
1
More on 'remembering where you left off'.
– Rick James
Nov 12 at 16:45
@Timekiller where to find the id of first item? Do you mean $start variable of datatable?
– DojoDev
Nov 18 at 7:16
First item overall or first item on page? If you mean overall, eitherselect min(id)
or use something likeid >= 0
; if you mean first item on page, look at my second to last query and adjust thelimit 150000, 1
part so that offset matchespages*items_per_page
instead of 150000.
– Timekiller
Nov 19 at 18:26
add a comment |
1
More on 'remembering where you left off'.
– Rick James
Nov 12 at 16:45
@Timekiller where to find the id of first item? Do you mean $start variable of datatable?
– DojoDev
Nov 18 at 7:16
First item overall or first item on page? If you mean overall, eitherselect min(id)
or use something likeid >= 0
; if you mean first item on page, look at my second to last query and adjust thelimit 150000, 1
part so that offset matchespages*items_per_page
instead of 150000.
– Timekiller
Nov 19 at 18:26
1
1
More on 'remembering where you left off'.
– Rick James
Nov 12 at 16:45
More on 'remembering where you left off'.
– Rick James
Nov 12 at 16:45
@Timekiller where to find the id of first item? Do you mean $start variable of datatable?
– DojoDev
Nov 18 at 7:16
@Timekiller where to find the id of first item? Do you mean $start variable of datatable?
– DojoDev
Nov 18 at 7:16
First item overall or first item on page? If you mean overall, either
select min(id)
or use something like id >= 0
; if you mean first item on page, look at my second to last query and adjust the limit 150000, 1
part so that offset matches pages*items_per_page
instead of 150000.– Timekiller
Nov 19 at 18:26
First item overall or first item on page? If you mean overall, either
select min(id)
or use something like id >= 0
; if you mean first item on page, look at my second to last query and adjust the limit 150000, 1
part so that offset matches pages*items_per_page
instead of 150000.– Timekiller
Nov 19 at 18:26
add a comment |
The pattern SELECT whatever FROM vast_table ORDER BY something LIMIT 10 large_number
is a notorious performance antipattern. Why? Because it has to examine a great many rows just to return a few.
If your id
value is a primary key (or any indexed column) you can paginate by
SELECT whatever FROM vast_table WHERE id BETWEEN large_value AND large_value+9 ORDER BY id;
Or you might try
SELECT whatever FROM vast_table WHERE id >= large_value ORDER BY id LIMIT 10;
This doesn't paginate perfectly if your id
values have gaps in them. But it performs tolerably well.
Hello @O. Jones i already tried similar but as you mentioned it skips records.return $queryBuilder ->from(array('e' => static::class)) ->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u') ->columns('e.*') ->where('e.id > :ID:') ->orderBy('e.id asc') ->limit(10, $start) ->getQuery() ->execute(array('ID' => $start)) ->toArray();
– DojoDev
Nov 12 at 15:43
@DojoDev - Well, sounds like QueryBuilder is not a good tool for this use.
– Rick James
Nov 12 at 16:48
You shouldn't include your starting point inlimit(10, $start)
. And you probably wantwhere('e.id >= :ID:')
note>=
.
– O. Jones
Nov 12 at 19:33
add a comment |
The pattern SELECT whatever FROM vast_table ORDER BY something LIMIT 10 large_number
is a notorious performance antipattern. Why? Because it has to examine a great many rows just to return a few.
If your id
value is a primary key (or any indexed column) you can paginate by
SELECT whatever FROM vast_table WHERE id BETWEEN large_value AND large_value+9 ORDER BY id;
Or you might try
SELECT whatever FROM vast_table WHERE id >= large_value ORDER BY id LIMIT 10;
This doesn't paginate perfectly if your id
values have gaps in them. But it performs tolerably well.
Hello @O. Jones i already tried similar but as you mentioned it skips records.return $queryBuilder ->from(array('e' => static::class)) ->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u') ->columns('e.*') ->where('e.id > :ID:') ->orderBy('e.id asc') ->limit(10, $start) ->getQuery() ->execute(array('ID' => $start)) ->toArray();
– DojoDev
Nov 12 at 15:43
@DojoDev - Well, sounds like QueryBuilder is not a good tool for this use.
– Rick James
Nov 12 at 16:48
You shouldn't include your starting point inlimit(10, $start)
. And you probably wantwhere('e.id >= :ID:')
note>=
.
– O. Jones
Nov 12 at 19:33
add a comment |
The pattern SELECT whatever FROM vast_table ORDER BY something LIMIT 10 large_number
is a notorious performance antipattern. Why? Because it has to examine a great many rows just to return a few.
If your id
value is a primary key (or any indexed column) you can paginate by
SELECT whatever FROM vast_table WHERE id BETWEEN large_value AND large_value+9 ORDER BY id;
Or you might try
SELECT whatever FROM vast_table WHERE id >= large_value ORDER BY id LIMIT 10;
This doesn't paginate perfectly if your id
values have gaps in them. But it performs tolerably well.
The pattern SELECT whatever FROM vast_table ORDER BY something LIMIT 10 large_number
is a notorious performance antipattern. Why? Because it has to examine a great many rows just to return a few.
If your id
value is a primary key (or any indexed column) you can paginate by
SELECT whatever FROM vast_table WHERE id BETWEEN large_value AND large_value+9 ORDER BY id;
Or you might try
SELECT whatever FROM vast_table WHERE id >= large_value ORDER BY id LIMIT 10;
This doesn't paginate perfectly if your id
values have gaps in them. But it performs tolerably well.
answered Nov 12 at 15:26
O. Jones
59.2k971106
59.2k971106
Hello @O. Jones i already tried similar but as you mentioned it skips records.return $queryBuilder ->from(array('e' => static::class)) ->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u') ->columns('e.*') ->where('e.id > :ID:') ->orderBy('e.id asc') ->limit(10, $start) ->getQuery() ->execute(array('ID' => $start)) ->toArray();
– DojoDev
Nov 12 at 15:43
@DojoDev - Well, sounds like QueryBuilder is not a good tool for this use.
– Rick James
Nov 12 at 16:48
You shouldn't include your starting point inlimit(10, $start)
. And you probably wantwhere('e.id >= :ID:')
note>=
.
– O. Jones
Nov 12 at 19:33
add a comment |
Hello @O. Jones i already tried similar but as you mentioned it skips records.return $queryBuilder ->from(array('e' => static::class)) ->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u') ->columns('e.*') ->where('e.id > :ID:') ->orderBy('e.id asc') ->limit(10, $start) ->getQuery() ->execute(array('ID' => $start)) ->toArray();
– DojoDev
Nov 12 at 15:43
@DojoDev - Well, sounds like QueryBuilder is not a good tool for this use.
– Rick James
Nov 12 at 16:48
You shouldn't include your starting point inlimit(10, $start)
. And you probably wantwhere('e.id >= :ID:')
note>=
.
– O. Jones
Nov 12 at 19:33
Hello @O. Jones i already tried similar but as you mentioned it skips records.
return $queryBuilder ->from(array('e' => static::class)) ->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u') ->columns('e.*') ->where('e.id > :ID:') ->orderBy('e.id asc') ->limit(10, $start) ->getQuery() ->execute(array('ID' => $start)) ->toArray();
– DojoDev
Nov 12 at 15:43
Hello @O. Jones i already tried similar but as you mentioned it skips records.
return $queryBuilder ->from(array('e' => static::class)) ->leftJoin('CxFrameworkModelsCommonUserCxUser', 'e.cx_hc_user_id = u.id', 'u') ->columns('e.*') ->where('e.id > :ID:') ->orderBy('e.id asc') ->limit(10, $start) ->getQuery() ->execute(array('ID' => $start)) ->toArray();
– DojoDev
Nov 12 at 15:43
@DojoDev - Well, sounds like QueryBuilder is not a good tool for this use.
– Rick James
Nov 12 at 16:48
@DojoDev - Well, sounds like QueryBuilder is not a good tool for this use.
– Rick James
Nov 12 at 16:48
You shouldn't include your starting point in
limit(10, $start)
. And you probably want where('e.id >= :ID:')
note >=
.– O. Jones
Nov 12 at 19:33
You shouldn't include your starting point in
limit(10, $start)
. And you probably want where('e.id >= :ID:')
note >=
.– O. Jones
Nov 12 at 19:33
add a comment |
The issue was related to dates column datatype in my table. I was using int
datatype for dates fields and when I changed the datatype of my dates column to datetime
, the search results were in seconds.
Source where i found the solution @ http://dbscience.blogspot.com/2008/08/can-timestamp-be-slower-than-datetime.html
add a comment |
The issue was related to dates column datatype in my table. I was using int
datatype for dates fields and when I changed the datatype of my dates column to datetime
, the search results were in seconds.
Source where i found the solution @ http://dbscience.blogspot.com/2008/08/can-timestamp-be-slower-than-datetime.html
add a comment |
The issue was related to dates column datatype in my table. I was using int
datatype for dates fields and when I changed the datatype of my dates column to datetime
, the search results were in seconds.
Source where i found the solution @ http://dbscience.blogspot.com/2008/08/can-timestamp-be-slower-than-datetime.html
The issue was related to dates column datatype in my table. I was using int
datatype for dates fields and when I changed the datatype of my dates column to datetime
, the search results were in seconds.
Source where i found the solution @ http://dbscience.blogspot.com/2008/08/can-timestamp-be-slower-than-datetime.html
answered Nov 25 at 12:38
DojoDev
336
336
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53264899%2fhow-to-optimize-limit-query-to-access-data-faster-from-a-huge-table%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
dba.stackexchange.com/questions/66294/…
– Masivuye Cokile
Nov 12 at 15:07
dba.stackexchange.com/questions/75963/…
– Masivuye Cokile
Nov 12 at 15:07
1
stackoverflow.com/questions/4481388/…
– Masivuye Cokile
Nov 12 at 15:08
you need an index.
– Hogan
Nov 12 at 15:12
@MasivuyeCokile tried the above stackoverflow link but it isn't related to my scenario.
– DojoDev
Nov 12 at 15:14