从oracle只获取第一行 - 它更快吗?

时间:2010-06-13 13:40:00

标签: oracle plsql

这个问题的主要目标是优化和更快的运行时间。

在Stored Proc中进行大量处理后,我最终返回如下计数:

          OPEN cv_1 FOR
             SELECT COUNT(*) num_of_members
               FROM HOUSEHOLD_MEMBER a,
                    HOUSEHOLD b
                WHERE RTRIM(LTRIM(a.mbr_last_name)) LIKE v_MBR_LAST_NAME || '%'
                        AND a.number = '01'
                        AND a.code = v_CODE
                        AND a.ssn_head = v_SSN_HEAD
                        AND TO_CHAR( a.mbr_dob, 'MM/DD/YYYY') = v_DOB;

但在我调用SP的代码中,不需要实际计数。它只关心计数大于1.

问题:

  1. 当计数为>时,如何将此更改为仅返回1或0?计数时0和0> 1.
  2. 这样做会更快而不是返回整个计数吗?

5 个答案:

答案 0 :(得分:2)

如果您不需要实际数据,并且只想知道是否至少有1,您可以使用ROWNUM对其进行优化:

OPEN cv_1 FOR
    SELECT 1 has_at_least_one_member
    FROM HOUSEHOLD_MEMBER a,
         HOUSEHOLD b
    WHERE RTRIM(LTRIM(a.mbr_last_name)) LIKE v_MBR_LAST_NAME || '%'
    AND a.number = '01'
    AND a.code = v_CODE
    AND a.ssn_head = v_SSN_HEAD
    AND TO_CHAR( a.mbr_dob, 'MM/DD/YYYY') = v_DOB
    AND ROWNUM = 1;

Oracle会优化此功能,以便在找到匹配项后立即停止处理。

在一个不相关的说明中,我建议(根据duffymo的说明)删除围绕a.mbr_dob的TO_CHAR并在v_DOB周围添加TO_DATE。这样,如果a.mbr_dob上恰好有索引,Oracle就有机会使用它。

答案 1 :(得分:1)

我不会想到在count(*)上引入过滤器会对性能有所帮助,但正如已经说过的那样,你可以用HAVING子句来做这件事。

你最大的瓶颈可能在于你的加入。

WHERE RTRIM(LTRIM(a.mbr_last_name)) LIKE v_MBR_LAST_NAME || '%'

这条线路无济于事。无论何时开始执行字段功能,都无法使用正确的索引,因此如果您可以避免修剪名称,这可能有所帮助。

AND TO_CHAR( a.mbr_dob, 'MM/DD/YYYY') = v_DOB;

这看起来像是最大的问题。如果v_DOB可以转换为查询之前的日期,那将有所帮助。或者,我认为将语句切换为使用TO_DATE可能有所帮助。

优化这一点的最佳方法是获得解释计划:

EXPLAIN PLAN FOR
  ...

答案 2 :(得分:1)

我的经验是,从游标中获取单行通常比检索COUNT()更快。如果您需要知道匹配数据存在但不关心行数,这将非常有用。在这种情况下,代码可以重写为

DECLARE
  bRow_found  BOOLEAN := FALSE;
BEGIN
  FOR aRow IN (SELECT a.mbr_last_name
                 FROM HOUSEHOLD_MEMBER a, 
                      HOUSEHOLD b 
                 WHERE RTRIM(LTRIM(a.mbr_last_name)) LIKE v_MBR_LAST_NAME || '%' 
                       AND a.number = '01' 
                       AND a.code = v_CODE 
                       AND a.ssn_head = v_SSN_HEAD 
                       AND TO_CHAR( a.mbr_dob, 'MM/DD/YYYY') = v_DOB)
  LOOP
    bRow_found := TRUE;
    EXIT;
  END LOOP;
END;

分享并享受。

答案 3 :(得分:0)

您可以使用HAVING检查COUNT是否大于1。

CURSOR c_example IS
SELECT COUNT(*)
...
HAVING COUNT(*) > 1;
l_dummy PLS_INTEGER;


OPEN c_example;
FETCH c_example INTO l_dummy;
IF c_example%NOTFOUND THEN
  CLOSE c_example;
  RETURN FALSE
ELSE
  CLOSE c_example;
  RETURN TRUE
END IF;

答案 4 :(得分:0)

“第一”是什么意思?没有WHERE子句,您不知道关系数据库存储行的顺序。

您是否有数据表明您存在性能问题,或者您是否过早优化?

我不相信你的想法会导致任何性能提升。我会去别处看看。

从这里开始:

AND TO_CHAR( a.mbr_dob, 'MM/DD/YYYY') = v_DOB;

我认为如果你解析这个,你会发现TO_CHAR会强制Oracle使用TABLE SCAN。在这种情况下,出生日期的任何指数都是无用的。

这会向我建议您已将出生日期列建模为字符串/ VARCHAR。为什么你会这样做?使用DATE列并传入适当的日期变量。您的表现会有所改善,您也会有实际的家庭数量。这对我来说似乎很有用。