Database 如何处理耗时的SQL?

Database 如何处理耗时的SQL?,database,oracle,query-optimization,Database,Oracle,Query Optimization,我们有一个包含600万条记录的表,然后我们有一个SQL,它需要大约7分钟来查询结果。我认为SQL不能再优化了 查询时间导致我们的weblogic抛出max stuck thread异常 对我处理这个问题有什么建议吗 以下是问题,但我很难改变它 SELECT * FROM table1 WHERE trim(StudentID) IN ('354354','0') AND concat(concat(substr(table1.LogDate,7,10),'/'),substr(table1

我们有一个包含600万条记录的表,然后我们有一个SQL,它需要大约7分钟来查询结果。我认为SQL不能再优化了

查询时间导致我们的weblogic抛出max stuck thread异常

对我处理这个问题有什么建议吗

以下是问题,但我很难改变它

SELECT * FROM  table1 
WHERE trim(StudentID) IN ('354354','0') 
AND concat(concat(substr(table1.LogDate,7,10),'/'),substr(table1.LogDate,1,5)) 
       BETWEEN '2009/02/02' AND '2009/03/02' 
AND TerminalType='1' 
AND RecStatus='0' ORDER BY StudentID, LogDate DESC, LogTime
然而,我知道使用字符串来比较日期很费时,但有人在我无法更改表结构之前写过


LogDate被定义为一个字符串,格式为mm/dd/yyyy,因此我们需要对其进行子字符串和concate处理,以便在。。。而且。。。我认为很难在这里进行优化。

如果没有关于正在执行的查询类型以及是否使用索引的进一步信息,就很难给出任何具体信息

但这里有一些一般的提示

  • 确保对经常筛选/排序的列使用索引
  • 如果只是某个查询的速度太慢,那么您可以通过在数据库更改时自动生成结果来阻止自己执行该查询。例如,您通常可以将计数存储在某个位置,而不是
    count()
  • 在将数据插入表之前/插入时,通过对数据自动调用
    trim()
    ,尝试从查询中删除
    trim()
    。这样,您只需使用索引查找
    StudentID

    此外,
    date
    过滤器应该可以在数据库中本地使用。在不知道哪个数据库的情况下,这可能会更困难,但类似的方法可能会起作用:
    LogDate介于'2009-02-02'和'2009-02-02'之间。

    如果同时在所有这些列上添加索引(即
    StudentID
    LogDate
    TerminalType
    RecStatus
    EmployeeID
    ),则其速度应该会非常快

    但是查询时间导致我们的weblogic抛出max stuck thread异常

    如果查询需要7分钟且无法加快速度,则必须停止实时运行此查询。能否将应用程序更改为查询定期刷新的缓存结果表

    在此之前,您可以实现一个闩锁(在Java中),一次只允许一个线程执行此查询。第二个线程将立即失败并出现错误(而不是导致整个系统崩溃)。这可能不会让此查询的用户感到高兴,但至少它可以保护所有其他线程

    我更新了查询,你能给我一些建议吗

    这些字符串操作使得索引几乎不可能。你确定你至少不能摆脱“修剪”吗?实际数据中真的有多余的空白吗?如果是这样,你可以缩小范围,只使用一个学生id,这应该会大大加快速度


    您希望在(学生id、日志日期)上创建一个复合索引,并且希望仍然可以使用索引范围扫描(针对给定的学生id)来解决复杂的日志日期问题。

    如果您的数据库支持,您可能希望尝试一种新的方法


    如果不是这样的话,可能值得考虑自己实现类似的功能,通过安排一个作业来运行一个查询,该查询将执行昂贵的修剪和合并,并用结果刷新一个表,这样您就可以针对更好的表运行一个查询并避免昂贵的内容。或者使用触发器来维护这样一个表。

    你提供的信息很少,我的直觉是以下条款给了我们一个线索:

         ... WHERE trim(StudentID) IN ('354354','0') 
    
    如果您有大量未识别学生的记录(即studentID=0),则studentID索引将非常不平衡


    在600万条记录中,有多少条记录的studentId=0?

    在不知道您使用的是什么数据库以及您的表结构的情况下,很难提出任何改进建议,但可以通过使用索引、提示等来改进查询

    在您的查询中,将显示以下部分
    
    在“2009/02/02”和“2009/02/02”之间的年份(年份(表1.LogDate,7,10),“/”),年份(表1.LogDate,1,5))
    

    太有趣了。在2009/02/02和2009/02/02之间?伙计,你想做什么

    你能把你的桌子结构贴在这里吗

    600万张唱片也不是什么大事

    SELECT * ... WHERE trim(StudentID) IN ('354354','0')
    
    如果这是正常构造,那么您需要一个。因为如果没有它,您将强制DB服务器执行完全表扫描


    根据经验,您应该尽可能避免使用
    WHERE
    子句中的函数。
    trim(stundertId)
    substr(表1.LogDate,7,10)
    禁止DB服务器使用任何索引或对查询应用任何优化。尽可能多地使用本机数据类型,例如
    DATE
    而不是
    VARCHAR
    ,因为
    LogDate
    StudentID
    也应在客户端软件中进行适当管理,例如在
    INSE之前修剪数据RT
    /
    更新

    此查询很可能正在执行完整的文件扫描,因为您所在的环境不太可能利用任何索引

    是日期字段还是文本字段?如果它是日期字段,那么不要做子字符串和CONTAT。只要说“092-02-02”和“09-02-03”之间的“日志日期”或日期范围是什么。如果将其定义为文本字段,则应认真考虑将其重新定义为日期字段。(如果您的日期确实是文本,并且是在mm/dd/yyyy上写的,那么如果日期跨越一年以上,那么您的订单…LOGDATE DESC将不会给出有用的结果。)

    有必要对StudentID进行修剪吗?最好在将数据放入数据库之前清理数据,然后在每次检索数据时尝试清理数据

    如果LogDate被定义为一个日期,并且您可以在输入时修剪studentid,那么
    SELECT * FROM  table1 
    WHERE  StudentID  IN (:SearchStudentId,0) 
    AND  table1.LogDate = :SearchDate
    AND TerminalType='1' 
    AND RecStatus='0' 
    ORDER BY EmployeeID, LogDate DESC, LogTime
    
    SELECT * FROM  table1 
    WHERE  StudentID  IN (:SearchStudentId,:StudentId0) 
    AND  table1.LogDate BETWEEN :SearchDate AND :SearchDate+0.99999 
    AND TerminalType='1' 
    AND RecStatus='0' 
    ORDER BY EmployeeID, LogDate DESC, LogTime
    
    StudentID IN (lpad('354354',10),lpad('0',10))