【问题标题】:Fetch data with single and fast SQL query使用单一且快速的 SQL 查询获取数据
【发布时间】:2009-01-19 17:11:01
【问题描述】:

我有以下数据:

ExamEntry   Student_ID     Grade
  11           1             80
  12           2             70
  13           3             20
  14           3             68
  15           4             75

我想找到所有通过考试的学生。在这种情况下,如果考试很少 一个学生参加的,我需要找到最后的结果。

所以,在这种情况下,我会让所有学生都通过。

我可以通过一个快速查询找到它吗?我是这样做的:

  1. 通过以下方式查找条目列表 按 Student_ID 从数据组中选择 max(ExamEntry)

  2. 查找结果:

从 ExamEntry 在 ( ) 中的数据中选择 ExamEntry。

但这非常慢 - 我收到大约 1000 个条目,这 2 步过程需要 10 秒。

有没有更好的办法?

谢谢。

【问题讨论】:

  • 我建议尝试不同的方法并回复我们:)

标签: sql performance


【解决方案1】:

如果您的查询速度非常慢,表中有 1000 条记录,则说明有问题。 对于现代数据库系统,包含 1000 个条目的表被认为非常小。
很可能,您没有为您的表提供(主)键?

假设如果至少有一个成绩高于所需的最低分数,则学生会通过,那么适当的查询将是:

SELECT 
  Student_ID
, MAX(Grade) AS maxGrade
FROM table_name
GROUP BY Student_ID
HAVING maxGrade > MINIMUM_GRADE_NEEDED

如果您确实需要最新成绩高于最低成绩:

SELECT 
  Student_ID
, Grade
FROM table_name
WHERE ExamEntry IN ( 
    SELECT 
      MAX(ExamEntry) 
    FROM table_name 
    GROUP BY Student_ID
)
HAVING Grade > MINIMUM_GRADE_NEEDED

【讨论】:

    【解决方案2】:
    SELECT student_id, MAX(ExamEntry)
    FROM data
    WHERE Grade > :threshold
    GROUP BY student_id
    

    像这样?

    【讨论】:

      【解决方案3】:

      我会假设您有一个学生表和测试表,并且您向我们展示的表是 test_result 表...(如果您没有类似的结构,您应该重新访问您的架构)

      select s.id, s.name, t.name, max(r.score)
      from student s
      left outer join test_result r on r.student_id = s.id
      left outer join test t on r.test_id = t.id
      group by s.id, s.name, t.name
      

      所有带有 id 的字段都应该被索引。

      如果您的域中真的只有一个测试(类型)......那么查询将是

      select s.id, s.name, max(r.score)
      from student s
      left outer join test_result r on r.student_id = s.id
      group by s.id, s.name
      

      【讨论】:

        【解决方案4】:

        我使用了这里给出的提示,在这里我发现查询的运行速度比我的第一个快近 3 个订单(0.03 秒而不是 10 秒):

        SELECT ExamEntry, Student_ID, Grade from data,
               ( SELECT max(ExamEntry) as ExId GROUP BY Student_ID) as newdata
        WHERE `data`.`ExamEntry`=`newdata`.`ExId` AND Grade > 60;
        

        谢谢大家!

        【讨论】:

          【解决方案5】:

          如前所述,索引是加快查询速度的强大工具。然而,索引的顺序非常重要。

          按 (ExamEntry) 然后 (Student_ID) 然后 (Grade) 顺序排列的索引对于查找学生通过的考试几乎没有用处。

          如果您只想查找已通过哪些考试,则相反顺序的索引将非常适合。这将使查询引擎能够快速识别已通过考试的行,并对其进行处理。

          在 MS SQL Server 中,这可以通过...完成...

          CREATE INDEX [IX_results] ON [dbo].[results] 
          (
              [Grade],
              [Student_ID],
              [ExamEntry]
          )
          ON [PRIMARY]
          

          (我建议阅读更多关于索引的内容,看看还有哪些其他选项,例如 ClusterdIndexes 等)

          使用该索引,以下查询将能够非常快速地忽略“失败”的考试,而只显示曾经通过考试的学生...

          (这假设如果您超过 60 岁,即使您随后再次参加考试并获得 27 岁,也将被视为及格。)

          SELECT
              Student_ID
          FROM
              [results]
          WHERE
              Grade >= 60
          GROUP BY
              Student_ID
          

          如果您确实需要最近的值,那么您需要将索引的顺序改回类似...

          CREATE INDEX [IX_results] ON [dbo].[results] 
          (
              [Student_ID],
              [ExamEntry],
              [Grade]
          )
          ON [PRIMARY]
          

          这是因为我们首先感兴趣的是任何给定学生的最新考试条目。可以使用以下查询来实现...

          SELECT
             *
          FROM
             [results]
          WHERE
             [results].ExamEntry = (
                                    SELECT
                                        MAX([student_results].ExamEntry)
                                    FROM
                                        [results] AS [student_results]
                                    WHERE
                                        [student_results].Student_ID = [results].student_id
                                   )
             AND [results].Grade > 60
          

          这样的子查询可能看起来很慢,尤其是因为它似乎是为 [results] 中的每一行执行的。

          然而,事实并非如此......
          - 主查询和子查询都引用同一个表
          - 查询引擎通过索引扫描每个唯一的 Student_ID
          - 为那个 Student_ID
          执行子查询 - 查询引擎已经在索引的那部分
          - 所以不需要新的索引查找

          编辑:

          有评论指出,在 1000 条记录中,索引不相关。需要注意的是,问题说明有 1000 条记录返回,而不是表包含 1000 条记录。对于一个基本查询,只要声明的时间,我敢打赌表中有超过 1000 条记录。也许这可以澄清?

          编辑:

          我刚刚调查了 3 个查询,每个查询有 999 条记录(333 名学生每人 3 个考试结果)

          方法 1:WHERE a.ExamEntry = (SELECT MAX(b.ExamEntry) FROM results [a] WHERE a.Student_ID = b.student_id)

          方法 2:WHERE a.ExamEntry IN (SELECT MAX(ExamEntry) FROM resuls GROUP BY Student_ID)

          方法 3:使用 INNER JOIN 代替 IN 子句

          找到了以下时间:

          Method    QueryCost(No Index)   QueryCost(WithIndex)
             1               23%                    9%
             2               38%                   46%
             3               38%                   46%
          

          因此,无论索引如何,查询 1 都更快,但索引也肯定会使方法 1 大大加快。

          这样做的原因是索引允许查找,否则您需要扫描。线性定律和平方定律的区别。

          【讨论】:

          • 在 1000 条记录中,任何索引都无关紧要。
          • 声明是返回1000条记录,而不是表包含1000条记录。
          【解决方案6】:

          感谢您的回答!

          我认为 Dems 可能最接近我的需要,但我会详细说明这个问题。

          1. 只计算最新的成绩。如果学生第一次通过,再次参加但不及格,他就完全失败了。他/她本可以参加 3 或 4 次考试,但仍然只有最后一次重要。
          2. 我使用 MySQL 服务器。我在 Linux 和 Windows 安装中遇到的问题。
          3. 我的数据集现在大约有 2K 条目,并且以每次新考试约 1K 的速度增长。
          4. 特定考试的查询也返回 ~ 1K 条目,当 ~ 1K 是参加的学生人数(通过 SELECT DISTINCT STUDENT_ID 从结果中接收;),则几乎所有都通过了,有些则失败了。

          5. 我在我的代码中执行以下查询: SELECT ExamEntry, Student_ID from Exams WHERE ExamEntry in (SELECT MAX(ExamEntry) from Exams GROUP BY Student_ID)。由于子查询返回大约 1K 条目,因此主查询似乎循环扫描它们,使所有查询运行很长时间,并且服务器负载为 50%(在 Windows 上为 100%)。

          6. 感觉有更好的方法:-),只是还没有找到。

          【讨论】:

          • 如果您发布您的架构、显示您的表和索引并查询 EXPLAIN 分析(您知道如何执行此操作吗?),您将有更好的运气获得有用的答案。您已经能够让我们中的许多人对信息不足进行猜测。
          • le dorfier:不,请。我一个月前开始使用数据库,因此感谢您的帮助。你能举一个关于 EXPLAIN 语法的例子吗?谢谢
          • Alex,您是否尝试过任何建议的查询?有或没有索引?有什么明显的改进吗?
          【解决方案7】:
          select examentry,student_id,grade 
          from data 
          where examentry in 
            (select max(examentry) 
             from data 
             where grade > 60 
             group by student_id)
          

          【讨论】:

          • tuinstoel - 如果您阅读我的帖子,您会发现这个确切的查询首先是问题所在。这种查询在我的机器上运行超过 10 秒。我的查询运行了 0.03 秒。
          • 我没有安装 MySQL,所以无法检查。我们的统计数据是最新的吗?
          【解决方案8】:

          不要使用

          where grade > 60
          

          但是

          where grade between 60 and 100
          

          应该会更快

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-11-29
            • 2013-08-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-01-17
            相关资源
            最近更新 更多