DataTables:使用多个表、分组和 html 内容加速服务器端处理?
Posted
技术标签:
【中文标题】DataTables:使用多个表、分组和 html 内容加速服务器端处理?【英文标题】:DataTables: Speed up server side processing with multiple tables, grouping, and html content? 【发布时间】:2013-02-11 10:30:50 【问题描述】:我正在使用数据表来显示来自多个 mysql 表(实际上是 7 个)的数据。目前确实没有太多数据,但我看到“显示 7 个条目中的 1 到 7 个(从 642,660,480 个总条目中过滤)。”只显示7个条目需要20秒。一旦我真正开始向数据库添加大量内容,我肯定这将无法使用。
我确信有更好的方法来完成我正在尝试做的事情,但这是我能够让它发挥作用的唯一方法。
这是我的服务器端脚本:
$q1 = "'";
$q2 = '"';
$order_id = "CONCAT( ".$q2."<input type='hidden' id='order_id' value='".$q2.", o.id, ".$q2."'><a href='order_details.php?id=".$q2.", o.id, ".$q2."'><img src='https://pas.greysignal.com/img/search.png' border='0'></a> ".$q2.", o.id )";
$patient_name = "CONCAT( ".$q2."<input type='hidden' id='patient_name' value='".$q2.", p.first_name, ' ', p.last_name, ".$q2."'><input type='hidden' id='patient_id' value='".$q2.", p.id, ".$q2."'><input type='hidden' id='patient_ssn' value='".$q2.", p.ssn, ".$q2."'><a href='patient_details.php?id=".$q2.", p.id, ".$q2."'><img src='https://pas.greysignal.com/img/search.png' border='0'></a> ".$q2.", p.first_name, ' ', p.last_name )";
$doc_name = "CONCAT( ".$q2."<input type='hidden' id='doctor_name' value='".$q2.", d.first_name, ' ', d.last_name, ".$q2."'><input type='hidden' id='doctor_id' value='".$q2.", d.id, ".$q2."'><a href='doctor_details.php?id=".$q2.", d.id, ".$q2."'><img src='https://pas.greysignal.com/img/search.png' border='0'></a> ".$q2.", d.first_name, ' ', d.last_name )";
$order_date = "FROM_UNIXTIME(o.created_timestamp, '%m/%e/%Y')";
$tests = "GROUP_CONCAT(t.name SEPARATOR ', ')";
$aColumns = array($order_id, $order_date, $doc_name, $patient_name, $tests, 'o.status');
/* Indexed column (used for fast and accurate table cardinality) */
$sIndexColumn = "o.id";
/* DB table to use */
$sTable = "`orders` o, `patients` p, `doctors` d, `tests_ordered` tst, `tests` t, `users` u, `events` e";
$sWhere = "WHERE p.id = o.patient_id AND d.id = o.doctor_id AND tst.order_id = o.id AND t.id = tst.test_id AND u.username = o.assigned_username AND e.event_id = o.event_id";
$order_status = isset($_GET['status']) ? $_GET['status'] : 'all';
if($order_status == 'all')
else
$sWhere .= " AND (o.status='Complete' OR o.status='$order_status')";
$sGroupBy = "GROUP BY o.id";
/* Database connection information */
$gaSql['user'] = DB_USER;
$gaSql['password'] = DB_PASSWORD;
$gaSql['db'] = DB_NAME;
$gaSql['server'] = DB_SERVER;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* If you just want to use the basic configuration for DataTables with PHP server-side, there is
* no need to edit below this line
*/
/*
* MySQL connection
*/
$gaSql['link'] = mysql_pconnect( $gaSql['server'], $gaSql['user'], $gaSql['password'] ) or
die( 'Could not open connection to server' );
mysql_select_db( $gaSql['db'], $gaSql['link'] ) or
die( 'Could not select database '. $gaSql['db'] );
/*
* Paging
*/
$sLimit = "";
if ( isset( $_GET['iDisplayStart'] ) && $_GET['iDisplayLength'] != '-1' )
$sLimit = "LIMIT ".mysql_real_escape_string( $_GET['iDisplayStart'] ).", ".
mysql_real_escape_string( $_GET['iDisplayLength'] );
/*
* Ordering
*/
if ( isset( $_GET['iSortCol_0'] ) )
$sOrder = "ORDER BY ";
for ( $i=0 ; $i<intval( $_GET['iSortingCols'] ) ; $i++ )
if ( $_GET[ 'bSortable_'.intval($_GET['iSortCol_'.$i]) ] == "true" )
$sOrder .= $aColumns[ intval( $_GET['iSortCol_'.$i] ) ]."
".mysql_real_escape_string( $_GET['sSortDir_'.$i] ) .", ";
$sOrder = substr_replace( $sOrder, "", -2 );
if ( $sOrder == "ORDER BY" )
$sOrder = "";
/*
* Filtering
* NOTE this does not match the built-in DataTables filtering which does it
* word by word on any field. It's possible to do here, but concerned about efficiency
* on very large tables, and MySQL's regex functionality is very limited
*/
if ( $_GET['sSearch'] != "" )
$sWhere .= " AND (";
for ( $i=0 ; $i<count($aColumns) ; $i++ )
if($i!=4) //skip tests column
$sWhere .= $aColumns[$i]." LIKE '%".mysql_real_escape_string( $_GET['sSearch'] )."%' OR ";
$sWhere = substr_replace( $sWhere, "", -3 );
$sWhere .= ')';
/* Individual column filtering */
for ( $i=0 ; $i<count($aColumns) ; $i++ )
if($i!=4) //skip tests column
if ( $_GET['bSearchable_'.$i] == "true" && $_GET['sSearch_'.$i] != '' )
if ( $sWhere == "" )
$sWhere = "WHERE ";
else
$sWhere .= " AND ";
$sWhere .= $aColumns[$i]." LIKE '%".mysql_real_escape_string($_GET['sSearch_'.$i])."%' ";
/*
* SQL queries
* Get data to display
*/
$sQuery = "
SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))."
FROM $sTable
$sWhere
$sGroupBy
$sOrder
$sLimit
";
//echo $sQuery;
//die();
$rResult = mysql_query( $sQuery, $gaSql['link'] ) or die(mysql_error());
/* Data set length after filtering */
$sQuery = "
SELECT FOUND_ROWS()
";
$rResultFilterTotal = mysql_query( $sQuery, $gaSql['link'] ) or die(mysql_error());
$aResultFilterTotal = mysql_fetch_array($rResultFilterTotal);
$iFilteredTotal = $aResultFilterTotal[0];
/* Total data set length */
$sQuery = "
SELECT COUNT(".$sIndexColumn.")
FROM $sTable
";
$rResultTotal = mysql_query( $sQuery, $gaSql['link'] ) or die(mysql_error());
$aResultTotal = mysql_fetch_array($rResultTotal);
$iTotal = $aResultTotal[0];
//added to hide filtering
//$iTotal = $iFilteredTotal;
/*
* Output
*/
$output = array(
"sEcho" => intval($_GET['sEcho']),
"iTotalRecords" => $iTotal,
"iTotalDisplayRecords" => $iFilteredTotal,
"aaData" => array()
);
while ( $aRow = mysql_fetch_array( $rResult ) )
$row = array();
for ( $i=0 ; $i<count($aColumns) ; $i++ )
if ( $aColumns[$i] != ' ' )
/* General output */
$row[] = $aRow[$i];
$output['aaData'][] = $row;
echo json_encode( $output );
这是服务器端脚本正在生成的查询:
SELECT SQL_CALC_FOUND_ROWS
CONCAT( "<input type='hidden' id='order_id' value='", o.id, "'><a href='order_details.php?id=", o.id, "'><img src='search.png' border='0'></a> ", o.id ),
FROM_UNIXTIME(o.created_timestamp, '%m/%e/%Y'),
CONCAT( "<input type='hidden' id='doctor_name' value='", d.first_name, ' ', d.last_name, "'><input type='hidden' id='doctor_id' value='", d.id, "'><a href='doctor_details.php?id=", d.id, "'><img src='search.png' border='0'></a> ", d.first_name, ' ', d.last_name ),
CONCAT( "<input type='hidden' id='patient_name' value='", p.first_name, ' ', p.last_name, "'><input type='hidden' id='patient_id' value='", p.id, "'><input type='hidden' id='patient_ssn' value='", p.ssn, "'><a href='patient_details.php?id=", p.id, "'><img src='search.png' border='0'></a> ", p.first_name, ' ', p.last_name ), GROUP_CONCAT(t.name SEPARATOR ', '),
o.status
FROM `orders` o, `patients` p, `doctors` d, `tests_ordered` tst, `tests` t, `users` u, `events` e
WHERE p.id = o.patient_id AND d.id = o.doctor_id AND tst.order_id = o.id AND t.id = tst.test_id AND u.username = o.assigned_username AND e.event_id = o.event_id AND (o.status='Complete' OR o.status='Draft')
GROUP BY o.id
我正在尝试确定在不破坏数据表的搜索和排序功能的情况下我可以做些什么来优化它。我已经尽我所知为所有表创建了索引并设置了主键。有没有办法使用JOIN?
这是 EXPLAIN 语句的输出:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE u index NULL PRIMARY 32 NULL 5 Using index; Using temporary; Using filesort
1 SIMPLE o ALL PRIMARY,patient_id,doctor_id,event_id,assigned_use... NULL NULL NULL 6 Using where
1 SIMPLE d eq_ref PRIMARY PRIMARY 4 pasdbadmin.o.doctor_id 1
1 SIMPLE e eq_ref PRIMARY PRIMARY 4 pasdbadmin.o.event_id 1 Using index
1 SIMPLE tst ref order_id,test_id order_id 4 pasdbadmin.o.id 1
1 SIMPLE t eq_ref PRIMARY PRIMARY 4 pasdbadmin.tst.test_id 1
1 SIMPLE p eq_ref PRIMARY PRIMARY 4 pasdbadmin.o.patient_id 1
更新:
问题似乎是在查询中使用包含用户和事件表的问题(实际上都没有使用)。这是执行速度更快的修改后的代码:
$q1 = "'";
$q2 = '"';
$order_id = "CONCAT( ".$q2."<input type='hidden' id='order_id' value='".$q2.", o.id, ".$q2."'><a href='order_details.php?id=".$q2.", o.id, ".$q2."'><img src='https://pas.greysignal.com/img/search.png' border='0'></a> ".$q2.", o.id )";
$patient_name = "CONCAT( ".$q2."<input type='hidden' id='patient_name' value='".$q2.", p.first_name, ' ', p.last_name, ".$q2."'><input type='hidden' id='patient_id' value='".$q2.", p.id, ".$q2."'><input type='hidden' id='patient_ssn' value='".$q2.", p.ssn, ".$q2."'><a href='patient_details.php?id=".$q2.", p.id, ".$q2."'><img src='https://pas.greysignal.com/img/search.png' border='0'></a> ".$q2.", p.first_name, ' ', p.last_name )";
$doc_name = "CONCAT( ".$q2."<input type='hidden' id='doctor_name' value='".$q2.", d.first_name, ' ', d.last_name, ".$q2."'><input type='hidden' id='doctor_id' value='".$q2.", d.id, ".$q2."'><a href='doctor_details.php?id=".$q2.", d.id, ".$q2."'><img src='https://pas.greysignal.com/img/search.png' border='0'></a> ".$q2.", d.first_name, ' ', d.last_name )";
$order_date = "FROM_UNIXTIME(o.created_timestamp, '%m/%e/%Y')";
$tests = "GROUP_CONCAT(t.name SEPARATOR ', ')";
$aColumns = array($order_id, $order_date, $doc_name, $patient_name, $tests, 'o.status');
/* Indexed column (used for fast and accurate table cardinality) */
$sIndexColumn = "o.id";
/* DB table to use */
$sTable = "`orders` o, `patients` p, `doctors` d, `tests_ordered` tst, `tests` t";
$sWhere = "WHERE p.id = o.patient_id AND d.id = o.doctor_id AND tst.order_id = o.id AND t.id = tst.test_id";
$order_status = isset($_GET['status']) ? $_GET['status'] : 'all';
if($order_status == 'all')
else
$sWhere .= " AND (o.status='Complete' OR o.status='$order_status')";
$sJoin = "";
$sGroupBy = "GROUP BY o.id";
/* Database connection information */
$gaSql['user'] = DB_USER;
$gaSql['password'] = DB_PASSWORD;
$gaSql['db'] = DB_NAME;
$gaSql['server'] = DB_SERVER;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* If you just want to use the basic configuration for DataTables with PHP server-side, there is
* no need to edit below this line
*/
/*
* MySQL connection
*/
$gaSql['link'] = mysql_pconnect( $gaSql['server'], $gaSql['user'], $gaSql['password'] ) or
die( 'Could not open connection to server' );
mysql_select_db( $gaSql['db'], $gaSql['link'] ) or
die( 'Could not select database '. $gaSql['db'] );
/*
* Paging
*/
$sLimit = "";
if ( isset( $_GET['iDisplayStart'] ) && $_GET['iDisplayLength'] != '-1' )
$sLimit = "LIMIT ".mysql_real_escape_string( $_GET['iDisplayStart'] ).", ".
mysql_real_escape_string( $_GET['iDisplayLength'] );
/*
* Ordering
*/
if ( isset( $_GET['iSortCol_0'] ) )
$sOrder = "ORDER BY ";
for ( $i=0 ; $i<intval( $_GET['iSortingCols'] ) ; $i++ )
if ( $_GET[ 'bSortable_'.intval($_GET['iSortCol_'.$i]) ] == "true" )
$sOrder .= $aColumns[ intval( $_GET['iSortCol_'.$i] ) ]."
".mysql_real_escape_string( $_GET['sSortDir_'.$i] ) .", ";
$sOrder = substr_replace( $sOrder, "", -2 );
if ( $sOrder == "ORDER BY" )
$sOrder = "";
/*
* Filtering
* NOTE this does not match the built-in DataTables filtering which does it
* word by word on any field. It's possible to do here, but concerned about efficiency
* on very large tables, and MySQL's regex functionality is very limited
*/
if ( $_GET['sSearch'] != "" )
$sWhere .= " AND (";
for ( $i=0 ; $i<count($aColumns) ; $i++ )
if($i!=4) //skip tests column
$sWhere .= $aColumns[$i]." LIKE '%".mysql_real_escape_string( $_GET['sSearch'] )."%' OR ";
$sWhere = substr_replace( $sWhere, "", -3 );
$sWhere .= ')';
/* Individual column filtering */
for ( $i=0 ; $i<count($aColumns) ; $i++ )
if($i!=4) //skip tests column
if ( $_GET['bSearchable_'.$i] == "true" && $_GET['sSearch_'.$i] != '' )
if ( $sWhere == "" )
$sWhere = "WHERE ";
else
$sWhere .= " AND ";
$sWhere .= $aColumns[$i]." LIKE '%".mysql_real_escape_string($_GET['sSearch_'.$i])."%' ";
/*
* SQL queries
* Get data to display
*/
$sQuery = "
SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))."
FROM $sTable
$sWhere
$sJoin
$sGroupBy
$sOrder
$sLimit
";
$filename = __DIR__.DIRECTORY_SEPARATOR."sql_log.txt";
file_put_contents($filename, $sQuery, FILE_APPEND);
$rResult = mysql_query( $sQuery, $gaSql['link'] ) or die(mysql_error());
/* Data set length after filtering */
$sQuery = "
SELECT FOUND_ROWS()
";
$rResultFilterTotal = mysql_query( $sQuery, $gaSql['link'] ) or die(mysql_error());
$aResultFilterTotal = mysql_fetch_array($rResultFilterTotal);
$iFilteredTotal = $aResultFilterTotal[0];
/* Total data set length */
$sQuery = "
SELECT COUNT(".$sIndexColumn.")
FROM $sTable
";
$rResultTotal = mysql_query( $sQuery, $gaSql['link'] ) or die(mysql_error());
$aResultTotal = mysql_fetch_array($rResultTotal);
$iTotal = $aResultTotal[0];
//added to hide filtering
//$iTotal = $iFilteredTotal;
/*
* Output
*/
$output = array(
"sEcho" => intval($_GET['sEcho']),
"iTotalRecords" => $iTotal,
"iTotalDisplayRecords" => $iFilteredTotal,
"aaData" => array()
);
while ( $aRow = mysql_fetch_array( $rResult ) )
$row = array();
for ( $i=0 ; $i<count($aColumns) ; $i++ )
if ( $aColumns[$i] != ' ' )
/* General output */
$row[] = $aRow[$i];
$output['aaData'][] = $row;
echo json_encode( $output );
如果我使用 JOINS,更新后的查询将是:
SELECT SQL_CALC_FOUND_ROWS
CONCAT( "<input type='hidden' id='order_id' value='", o.id, "'><a href='order_details.php?id=", o.id, "'><img src='search.png' border='0'></a> ", o.id ),
FROM_UNIXTIME(o.created_timestamp, '%m/%e/%Y'),
CONCAT( "<input type='hidden' id='doctor_name' value='", d.first_name, ' ', d.last_name, "'><input type='hidden' id='doctor_id' value='", d.id, "'><a href='doctor_details.php?id=", d.id, "'><img src='search.png' border='0'></a> ", d.first_name, ' ', d.last_name ),
CONCAT( "<input type='hidden' id='patient_name' value='", p.first_name, ' ', p.last_name, "'><input type='hidden' id='patient_id' value='", p.id, "'><input type='hidden' id='patient_ssn' value='", p.ssn, "'><a href='patient_details.php?id=", p.id, "'><img src='search.png' border='0'></a> ", p.first_name, ' ', p.last_name ),
GROUP_CONCAT(t.name SEPARATOR ', '),
o.status
FROM `orders` o
JOIN `doctors` d ON d.id = o.doctor_id
JOIN `patients` p ON p.id = o.patient_id
JOIN `tests_ordered` tst ON tst.order_id = o.id
JOIN `tests` t ON t.id = tst.test_id
WHERE o.status='Complete' OR o.status='Draft'
GROUP BY o.id
这样做的问题是,由于列数组等原因,在使用排序和过滤时,DataTables 的设计不能与 JOINS 一起正常工作。我希望看到一个可以处理这样的查询的解决方案.
【问题讨论】:
将EXPLAIN
添加到选择的前面,看看它说了什么
已编辑以显示 EXPLAIN 的结果
嗨,我也面临同样的问题 + 我的搜索也显示了额外的记录,因为 JOIN 和 where 条件..请你帮我..***.com/questions/25015124/…
【参考方案1】:
首先,如果您想优化 SQL 语句,首先要摆脱其中的 html 垃圾。如果不出意外,它会混淆语句的实际结构。如果必须,您可以在优化结束时将其放回原处,尽管我会认真投反对票:您已经让 PHP 来进行标记。为了清楚起见,我养成了使用 JOIN 子句的习惯,我相应地改写了整个 Thing。
这个过程给我的是这样的:
SELECT SQL_CALC_FOUND_ROWS,
o.id, o.created_timestamp, o.status,
d.id, d.first_name, d.last_name,
p.id, p.first_name, p.last_name, p.ssn
GROUP_CONCAT(t.name SEPARATOR ', '),
FROM `orders` o
JOIN `doctors` d ON d.id = o.doctor_id
JOIN `patients` p ON p.id = o.patient_id
JOIN `users` u ON u.username = o.assigned_username
JOIN `events` e ON e.event_id = o.event_id
JOIN `tests_ordered` tst ON tst.order_id = o.id
JOIN `tests` t ON t.id = tst.test_id
WHERE o.status='Complete' OR o.status='Draft'
GROUP BY o.id
这里有几点需要注意:
1) 你的主表是orders
。这也是您使用 WHERE 子句的那个,并且您正在按它进行分组。以id
作为主索引,在status
上有第二个索引,这应该不会太糟糕。
2) 你通过我假设是外键链接其他四个表。其中两个表从未实际使用过:(很可能)根本不需要加入users
和events
。你应该去掉那些,顺便说一下,它也删除了文本列(用户名)上的不太好的连接。确保其余表 doctors
和 patients
在其各自的 id
列上有主键。
3) 您在tests_ordered
和tests
两个表上有一个更复杂的连接。所做的只是为您提供一个连接的名称字符串,但它确实增加了 GROUP BY 子句的复杂性。从这里开始有两种方法:尝试优化这些连接或将它们完全从选择中删除。
3) 解决方案 A 要优化这些连接,请确保在 tests_ordered.order_id
和 tests_ordered.test_id
上有一个索引,在 tests.id
上有一个主索引。您的声明应如下所示:
SELECT SQL_CALC_FOUND_ROWS,
o.id, o.created_timestamp, o.status,
d.id, d.first_name, d.last_name,
p.id, p.first_name, p.last_name, p.ssn
GROUP_CONCAT(t.name SEPARATOR ', '),
FROM `orders` o
JOIN `doctors` d ON d.id = o.doctor_id
JOIN `patients` p ON p.id = o.patient_id
JOIN `tests_ordered` tst ON tst.order_id = o.id
JOIN `tests` t ON t.id = tst.test_id
WHERE o.status='Complete' OR o.status='Draft'
GROUP BY o.id
3) 解决方案 B 删除整个 tests/tests_ordered 内容,并将其放在单独的选择中。您的主要选择现在将如下所示:
SELECT SQL_CALC_FOUND_ROWS,
o.id, o.created_timestamp, o.status,
d.id, d.first_name, d.last_name,
p.id, p.first_name, p.last_name, p.ssn
FROM `orders` o
JOIN `doctors` d ON d.id = o.doctor_id
JOIN `patients` p ON p.id = o.patient_id
WHERE o.status='Complete' OR o.status='Draft'
但是您要么必须为每行运行一个额外的 SELECT 以获取连接的 t.name,要么只为当前页面上的所有订单 ID 执行一个 SELECT。后者看起来像这样:
SELECT o.id, GROUP_CONCAT(t.name SEPARATOR ', '),
FROM `orders` o
JOIN `tests_ordered` tst ON tst.order_id = o.id
JOIN `tests` t ON t.id = tst.test_id
WHERE o.in IN ( <put the 10 ids on your current page here, separated by commas> )
GROUP BY o.id
解决方案 A 应该在一台不错的机器上运行得很快。解决方案 B 在任何机器上都应该运行得非常快。索引外键上的直接 JOINS 很便宜。
4) 上述选择都不应该在包含少于 6.42 亿个订单的数据库上返回 6.42 亿行,我假设您不会。既然 MySQL 告诉你它可能要构建一个完整的笛卡尔积,这也说明了经验的速度。这意味着您的一个直接外键连接出了问题。最有可能的问题是 users
上的连接 - 首先是一个无用的连接,但无论如何:检查其 username
列的唯一性。
【讨论】:
感谢您引导我朝着正确的方向前进。问题出在未使用的表上,删除用户和事件表解决了这个问题。只需这样做即可将查询时间从 20+ 秒降至 0.0035 秒。我更喜欢使用连接,而不是在查询中使用太多 html,但是 DataTables 无法正常工作。我会尽快发布固定代码。 顺便说一句,您发布的所有 SQL 语句都没有实际工作......轻微的语法错误。 SQL_CALC_FOUND_ROWS 之后不需要逗号。使用连接,查询看起来就像我更新后的帖子一样。 缺少您的数据库结构的副本,我从脑后编写了 SQL 语句。所以是的,他们很可能有轻微的语法错误——请随时相应地编辑我的帖子。关于查询中的 HTML,您是对的,您需要在某处添加它 - 但在 SQL 中这样做只是一种奇怪(且昂贵)的方式,您需要 PHP。【参考方案2】:1) 一些提示可能是删除所有可以在表示层中使用 PHP 的连接和不必要的函数,并且只选择列值。
2) 仅选择在您的架构中绝对重要的最重要的列,然后您可以为其他属性运行简单的 1 行选择。 (即使查询数量可能会增加,但实际上可以提高性能,因为多个连接往往非常慢)
3) 如果没有任何帮助,您可能不得不导致某些形式的非规范化,您必须将某些关系值复制到多个表中以减少连接数量。
4)缓存、缓存、cahce——在数据库层面,在php层面,基本上无处不在……
5) 此外,如果您实际上只需要使用最近的 100 000 000 行,那么它在很大程度上取决于您的数据,例如那些 700 000 000 行,然后开发某种归档功能来减少操作表中的记录数并且只有在存档中有记录时,才通过大存档表进行搜索,这可能会很慢,但会很少发生......
这些只是一些非常普遍的问题。
【讨论】:
【参考方案3】:作为对@azzit 好答案的补充,我可以给你一些我们在数据表服务器端查询中使用过的提示:
避免group concat,这总是会使您的查询非常慢,甚至可能使用临时表,这是您应该避免的事情。因此,请等待您的分页结果并根据主查询分页后返回的实际标识符启动额外数据(如此处的测试排序数据) 如果您必须过滤有关 tests_ordered 元素的主查询,上述解决方案可能会出现问题。但事实上,解决方案是添加 EXISTS 子选择来过滤您对这些 1->n 相关表的主查询(并且使用存在子查询,您在主查询中不需要这些表,仅在子选择时已应用过滤器) 使用所需的联接和条件进行一次 count 查询,并尝试在未修改过滤器的情况下记录结果(您可以忽略 order by modify)。 注意order by,您不应允许所有列都与order by 一起使用。如果使用未索引的列,Order by 将使您的查询非常慢。尤其是您不应该允许对“多个值”列进行排序,例如 tests_ordered。 如果您有大量的连接,并且您认为您的查询以正确的顺序编写,请在选择上使用 STRAIGHT_JOIN 以避免在优化器计算中损失几秒钟(连接数量非常大)。 对可能为空 (null) 的列使用左连接,但如果此数据未按顺序、过滤器或不在可见列中使用,请尽量避免将它们放入查询中。 使用 explain 测试您的查询,不要犹豫添加过滤器的变体和排序依据。您将看到使用 FROM_UNIXTIME 之类的函数可以防止使用索引。 用于多值列的策略始终可以应用于所有列,执行最小查询以获取分页结果标识符(仅列中的标识符,添加条件或排序请求的连接,为具有多个值的查询添加子选择条件),计算全局结果并对其应用分页。然后在 10/25/50/100 获得的结果上,根据行标识符加载所有单元格数据。【讨论】:
以上是关于DataTables:使用多个表、分组和 html 内容加速服务器端处理?的主要内容,如果未能解决你的问题,请参考以下文章
用DataTables做了一个行分组的效果,顺便学习了ajxs的用法
JQuery DataTables:如何显示/隐藏多个表的行详细信息?