【问题标题】:Milion Record Sql Order By and count rowsMilion Record Sql Order By 并计算行数
【发布时间】:2016-10-28 15:58:29
【问题描述】:

我有一个包含数百万条记录的数据库。 该表的结构如下:


表名:记录

归档1:名称(varchar)(主键)

字段2:记录(int/bigint)

示例:

姓名 |记录

达林 | 256

亚伦 | 3

达里尔 | 12

...


我需要知道名为“namex”的用户在排序记录中的位置。

目前我实现了这个解决方案:

...

$name=namex;
$query= mysqli_query($mysqli,"SELECT Name FROM record  ORDER BY Record DESC");

$x=0;
$rank=0;
if ($query->num_rows > 0) {
    // output data of each row
    while($row = $query->fetch_assoc()) {
        if($row["Name"]==$name){
            $rank=$x+1;
            echo "Rank : $rank<br>";

            break;
        }


        $x++;
    }
}

...

有了它和数据库中的 100 万条记录,答案大约会在 4 秒内出现。

我尝试在字段 Record 上放置表索引,但性能保持不变。

如何减少执行时间?

【问题讨论】:

  • SELECT COUNT(Name) as Rank from record WHERE Name &lt;= $name也许...当然你需要参数化查询以防止SQL注入的风险
  • 你真的应该使用where 子句。从字面上看,您的行为相当于开车去沃尔玛,购买商店的整个库存,将它们全部带回家,然后挑选并扔掉除了您想要的巧克力棒之外的所有东西。
  • @MarkBaker 除了他有 ORDER BY Record,而不是 Name
  • 谢谢,我解决了:mysqli_query($mysqli,"SELECT COUNT(Name) as rank FROM record Where Record >= '$record'");
  • 看起来不错。但是如果两个用户的“记录”值相同呢?

标签: mysql sql performance


【解决方案1】:

由于我不知道您使用的是什么 DBMS(在标签中您同时使用 mysql 和 sql-server...),您可以创建一个视图(对于 SQL 服务器必须是 indexed view)或为 mysql 实现/模拟一种物化视图(具有更好的性能)。该视图很好地通过一些 DBMS 获得更好的性能。对于 MySQL 可能没有区别。

在视图中显示排名位置如下查询(mysql示例):

CREATE VIEW ranked_record AS
SELECT 
    record.Name,
    @curRank := @curRank + 1 AS rank
FROM 
    record,
    (SELECT @curRank := 0) r
ORDER BY Record DESC;

或 SQL 服务器:

CREATE VIEW ranked_record AS
SELECT 
    record.Name,
    row_number() over(ORDER BY record)
FROM 
    record;

然后运行您的查询:

SELECT name , rank FROM ranked_record WHERE name LIKE 'some name'

更新:

在 John cmets 之后,我从使用变量的视图中意识到了错误。这是不可能的,因为"feature/bug" of/from MySQL

因此,您可以选择将其用作subquery in FROM clause

SELECT 
    name, 
    rank
FROM (
        SELECT 
            record.Name,
            @curRank := @curRank + 1 AS rank
        FROM 
            record,
            (SELECT @curRank := 0) r
    ) AS ranked_record
WHERE 
    name LIKE 'some name';

或者创建一个函数来计算视图内的排名 (like this example):

CREATE FUNCTION `func_inc_var_session`() RETURNS int(11)
begin
  SET @var := IFNULL(@var,0) + 1;
  return @var;

结束;

然后,像以前一样创建视图,只使用函数而不是变量:

CREATE VIEW ranked_record AS
SELECT 
    record.Name,
    func_inc_var_session() as rank
FROM 
    record
ORDER BY Record DESC;

【讨论】:

  • 我收到此错误:“#1351 - 视图的 SELECT 包含变量或参数”
  • @John 我的错,我会用正确的版本更新答案。
  • MySQL 子查询往往很糟糕(时间,我的意思是......),不是吗?
  • @Kevin_Kinsey 由于 MySQL 没有任何类型的视图缓存(如具体化),因此子查询和视图之间没有任何区别。但是,在这种情况下,视图会在查询执行中再增加一两个步骤,视图可能会更慢
  • 啊,我明白了。 CREATE VIEW 正在运行中......谢谢。
【解决方案2】:

我们可以在 sql 查询中编写它,而不是在 PHP 中获取记录并循环它。

select row = row_number() over( order by Record ) , * from record where name like 'namex'

【讨论】:

  • OP 错误地标记了问题,它与 ms sql server 无关。请删除此答案。
  • 这是否给出了正确的row_number?还是它总是给出“1”?
【解决方案3】:

我真的不明白你为什么要循环每条记录,知道“名称”列是你的 PK(唯一值)。

$name="Darrin";
$query= mysqli_query($db_connection, "SELECT COUNT(1) as rank FROM record WHERE Name = '".mysqli_real_escape_string($name)."' ORDER BY Record DESC");

$row = mysqli_fetch_row($query);
if ($query->num_rows > 0) {
   echo "Rank : $row["rank"]<br>";
}

【讨论】:

  • 这是一个糟糕的方法。 where 子句绝对是正确的方法,但这对 sql 注入很开放。这需要是一个参数化查询。
  • 感谢您的评论。您能否提供在这句话上注入 SQL 代码的概念证明?我认为函数 mysql_real_escape_string 已经阻止了这种攻击。
  • 真正的问题是这并不能解决问题,据我所知。
  • 没有。转义字符串不会阻止这种情况。除了额外的单引号之外,还有更多险恶的东西可以传递。您可以传入二进制文件,它将被执行。看看这里。 stackoverflow.com/questions/22304930/…
  • 谢谢@Sean Lange :),我真的对这种概念很感兴趣,但是在您提供的链接中,没有人提及通过 mysqli_real_escape_string 进行二进制代码注入的任何内容。我认识到对于通用函数,使用参数化查询更有效,因此我们可以按类型验证数据,但在这种情况下,我们正在处理字符串。只是要求学习一些新东西。
猜你喜欢
  • 1970-01-01
  • 2018-11-29
  • 1970-01-01
  • 1970-01-01
  • 2015-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多