7

我正在使用数据表来显示来自多个 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> &nbsp;".$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> &nbsp;".$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> &nbsp;".$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> &nbsp;", 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> &nbsp;", 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> &nbsp;", 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> &nbsp;".$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> &nbsp;".$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> &nbsp;".$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> &nbsp;", 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> &nbsp;", 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> &nbsp;", 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 一起正常工作。我希望看到一个可以处理这样的查询的解决方案。

4

3 回答 3

5

首先,如果您想优化 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 子句的那个,并且您正在按它进行分组。使用idas 主索引和第二个索引 on status,这应该不会太糟糕。

2)您通过我假设是外键链接其他四个表。users其中两个表从未实际使用过:(很可能)根本不需要加入events。你应该去掉那些,顺便说一下,它也删除了文本列(用户名)上的不太好的连接。确保剩余的表doctorspatients它们各自的id列上有它们的主键。

3)您在两个表tests_orderedtests. 所做的只是为您提供一个连接的名称字符串,但它确实增加了 GROUP BY 子句的复杂性。从这里开始有两种方法:尝试优化这些连接或将它们完全从选择中删除。

3) 解决方案 A要优化这些连接,请确保您有一个 ontests_ordered.order_id和 on的索引tests_ordered.test_id和一个 on 的主索引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列的唯一性。

于 2013-03-01T00:40:24.210 回答
0

作为对@azzit nice answer的补充,我可以给你一些我们在数据表服务器端查询中使用的提示:

  • 避免group concat,这总是会使您的查询非常慢,甚至可能使用临时表,这是您应该避免的事情。因此,请等待您的分页结果并根据主查询分页后返回的实际标识符启动额外数据(如此处的测试排序数据)
  • 如果您必须过滤 tests_ordered 元素上的主查询,则上面使用的解决方案可能会出现问题。但实际上解决方案是添加EXISTS子选择来过滤您对这些 1->n 相关表的主查询(并且使用存在子查询,您将不需要在主查询中使用这些表,仅在应用过滤器时进行子选择)
  • 使用所需的连接和条件进行一次计数查询,并尝试在未修改过滤器的情况下记录结果(您可以忽略按修改排序)。
  • 注意order by您不应该允许所有列与 order by 一起使用。如果使用未索引的列,Order by 将使您的查询非常慢。尤其是您不应该允许对“多个值”列进行排序,例如 tests_ordered。
  • 如果您有大量的连接并且您认为您的查询是以正确的顺序编写的,请在选择上使用STRAIGHT_JOIN以避免在优化器计算中损失几秒钟(连接数量非常大)。
  • 对可能为空 (null) 的列使用左连接,但如果此数据未按顺序使用、过滤器或不在可见列中,请尽量避免将它们放入查询中。
  • 使用explain测试您的查询,不要犹豫添加过滤器的变体和排序依据。您将看到使用 FROM_UNIXTIME 之类的函数可以防止使用索引。
  • 用于多个值列的策略始终可以应用于所有列,执行最小查询以获取分页结果标识符(仅列中的标识符,添加条件或排序请求的连接,为具有条件的多个值查询添加子选择),计算全局结果并对其应用分页。然后在 10/25/50/100 获得的结果上,根据行标识符加载所有单元格数据。
于 2013-03-06T12:59:37.807 回答
0

1) 一些提示可能是删除您可以在表示层中使用 PHP 的所有连接和不必要的函数,并且只选择列值。

2) 仅选择在您的架构中绝对重要的最重要的列,然后您可以为其他属性运行简单的 1 行选择。(即使查询数量可能会增加,但实际上可以提高性能,因为多个连接往往非常慢)

3)如果没有任何帮助,您可能不得不导致某些形式的非规范化,您必须将某些关系值复制到多个表上以减少连接数量。

4)缓存、缓存、cahce——在数据库层面,在php层面,基本上无处不在……

5)此外,如果您实际上只需要使用最近的 100 000 000 行,那么它在很大程度上取决于您的数据,例如那些 700 000 000 行,然后开发某种归档功能以减少操作表中的记录数量,并且仅当一条记录在存档中,然后搜索大存档表,这可能很慢,但会发生得更少......

这些只是几个非常普遍的问题。

于 2013-02-28T19:32:12.730 回答