oracle 如何提高SQL查询的性能?

2g32fytz  于 2023-06-05  发布在  Oracle
关注(0)|答案(2)|浏览(195)

我是SQL的初学者,我想使用SQL从Oracle数据库查询数据。我有一个表空间,它记录了许多汽车的位置。每个记录都有里程和时间。表空间有三列:“发送时间”、“里程”和“平台”。它们分别表示时间、里程(时间戳格式)和汽车的车牌号码。“SENDTIME”列中的值采用日期时间格式。我想知道一辆特定的汽车什么时候没有移动超过30秒和少于300秒。所以我写了一个SQL查询:

WITH gpsinfo_cte AS (
  SELECT plateno, sendtime, longitude, latitude, mileage, createdate,
    FIRST_VALUE(sendtime) OVER (PARTITION BY plateno, mileage ORDER BY sendtime) AS first_sendtime,
    LAST_VALUE(sendtime) OVER (PARTITION BY plateno, mileage ORDER BY sendtime) AS last_sendtime
  FROM GPSINFO 
  WHERE plateno = '京AEW302'
)
SELECT /*+ NO_MERGE(gpsinfo_cte) */ plateno, sendtime, longitude, latitude, mileage, createdate 
FROM gpsinfo_cte
WHERE (last_sendtime - first_sendtime) * 24 * 60 *60 < 300
AND (last_sendtime - first_sendtime) * 24 * 60 *60 > 30;

但在oracle数据库中运行速度较慢。根据Web搜索结果,我尝试使用EXPLAIN PLAN语句为查询生成执行计划,并将其存储在名为PLAN_TABLE的表中。下面是输出:

我仍然不知道如何改进性能。有人能帮忙吗?非常感谢!

flseospp

flseospp1#

这可能是最重要的:

FROM GPSINFO 
  WHERE plateno = '京AEW302'

假设您有很多(可能是数千,数百万)汽车,并且只要求一个车牌号码,您希望使用索引来访问该特定汽车的表行。你的执行计划表明这并没有发生:

TABLE ACCESS FULL GPSINFO

只需在plateno列上创建一个索引,就可以解决您的问题:

CREATE INDEX gpsinfo_plateno_idx1 on gpsinfo(plateno) compress 1

至于查询的其余部分,您将不得不处理逻辑以获得正确的结果,但只要您只处理一辆汽车,就不太可能有明显的性能问题。在逻辑方面,如果我理解正确的话,你想知道什么时候位置记录之间有30-300秒的差距,而它们之间没有里程。因此,您需要将一行与相邻行进行比较。使用LAG(或LEAD):

SELECT x.*,
       (sendtime - last_sentime) * 86400 seconds_elapsed
  FROM (SELECT x.*,
               LAG(sendtime) OVER (ORDER BY sendtime) last_sendtime,
               LAG(mileage) OVER (ORDER BY sendtime) last_mileage
          FROM gpsinfo x
         WHERE plateno = '京AEW302') x
 WHERE (sendtime - last_sentime) * 86400 BETWEEN 30 AND 300
   AND mileage = last_mileage

注意:如果你只需要一个plateno值,就没有必要在PARTITION BY子句中包含plateno,因为只有一个,所以这是多余的。你当然可以使用PARTITION BY mileage作为我这里展示的mileage = last_mileage逻辑的替代,但是mileage很可能有很多不同的值,并且在内部按这么多值分组意味着很多小的(单行)组,这不是很有内存/临时效率。然而,正如我所说,索引是你唯一的严重问题。

h43kikqp

h43kikqp2#

为了获得最快的结果,您需要预先计算这些值(第一次和最后一次发送时间)。这可以通过以下方式实现:

  • 负责添加记录的例程-在一个plateno的上下文中,它将更快地计算和保存第一次和最后一次
  • 桌上的扳机
  • 物化视图及其数据刷新方法

所有这些都需要一些开发时间,而且不会很难。
我也可以让你先试着计算plateno,然后提取它的细节。在sendtime的情况下,我们感兴趣的是第一个(最小)和最后一个(最大)值。因此,您可以用途:

WITH gpsinfo_cte AS (
  SELECT plateno
  FROM GPSINFO 
  WHERE plateno = '京AEW302'
  GROUP BY plateno
  HAVING (MAX(sendtime) - MIN(sendtime)) * 24 * 60 * 60 < 300
    AND (MAX(sendtime) - MIN(sendtime)) * 24 * 60 * 60 > 30
)
SELECT *
FROM GPSINFO
WHERE plateno IN (SELECT plateno FROM gpsinfo_cte);

如果这是工作,你可以在plateno和sendtime上添加索引,以进一步优化它。

相关问题