Oracle:使用 SQL 或 PL/SQL 提取文件扩展名的最快方法

Posted

技术标签:

【中文标题】Oracle:使用 SQL 或 PL/SQL 提取文件扩展名的最快方法【英文标题】:Oracle: Fastest Way to Extract Filename Extension Using SQL or PL/SQL 【发布时间】:2014-01-18 20:55:51 【问题描述】:

我需要获取文件名的扩展名。扩展可以是任意长度(不仅仅是 3),也可以不存在,在这种情况下我需要返回 null。我知道我可以轻松地编写一个 PL/SQL 函数来执行此操作,然后只需在查询中调用该函数,但我希望我能以某种方式内联完成所有操作。而且我真的不在乎解决方案有多长,我需要的是最快解决方案。速度很重要,因为这最终会在一张非常大的桌子上运行。这就是我目前所拥有的......

/*
The same method is being used in all 5 examples.
It works for all of them except the first one.
The first one I need to return null
*/

SELECT substr(filename,instr(filename,'.',-1)+1,length(filename)-instr(filename,'.',-1))
  FROM (select 'no_extension_should_return_null' filename from dual);
--returns: no_extension_should_return_null

SELECT substr(filename,instr(filename,'.',-1)+1,length(filename)-instr(filename,'.',-1))
  FROM (select 'another.test.1' filename from dual);
--returns: 1

SELECT substr(filename,instr(filename,'.',-1)+1,length(filename)-instr(filename,'.',-1))
  FROM (select 'another.test.doc' filename from dual);
--returns: doc

SELECT substr(filename,instr(filename,'.',-1)+1,length(filename)-instr(filename,'.',-1))
  FROM (select 'another.test.docx' filename from dual);
--returns: docx

SELECT substr(filename,instr(filename,'.',-1)+1,length(filename)-instr(filename,'.',-1))
  FROM (select 'another.test.stupidlong' filename from dual);
--returns: stupidlong

那么有没有一种快速的方法来完成这个内联,或者我应该把它写在一个 PL/SQL 函数中?

这就是我正在使用的...

select * from v$version;
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
CORE    11.2.0.2.0  Production
TNS for 64-bit Windows: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production

更新 我正在将此代码移动到一个函数中,并将设置一个测试来调用它一百万次以查看该函数是否会减慢它的速度,我认为它不会产生影响,因为它只是字符串操作。

更新 感谢您到目前为止的答案。我最终制作了一个 PL/SQL 函数来满足我的需要......

create or replace function extrip(filename varchar2) return varchar2 as
begin
    if ( instr(filename,'.',-1) = 0 ) then
        return null;
    end if;

    return substr(filename,instr(filename,'.',-1)+1,length(filename)-instr(filename,'.',-1));
end;

然后我对一个有 200 万行的表进行了两次测试。当我查看两者的解释计划时,它们是 100% 相同的。怎么可能?

select regexp_substr(filename, '\.[^\.]*$') ext from testTable;

select extrip(filename) ext from testTable;

更新 我为这两个都添加了order by ext,然后重新运行了测试,结果有所不同。正则表达式耗时 9 秒,函数耗时 17 秒。我想没有 TOAD 的命令只是重新调整前 X 个记录。所以@Brian McGinity 是对的。我仍然需要正则表达式方法来返回点“。”不过。

【问题讨论】:

【参考方案1】:

当你完成 100% sql 时,它会运行得最快。

substr/instr 是 oracle 中的本地编译函数。

如果你把它放在一个 plsql 函数中,由于 sql 和 plsql 之间的上下文切换,它会运行得更慢:

由于上下文切换,这比较慢:

select extrip( filename ) from million_row_table 

你所拥有的更快。

更新:

试试这个:

select s,
       substr(s,   nullif( instr(s,'.', -1) +1, 1) )
from ( 
     select 'no_extension_should_return_null' s from dual union
     select 'another.test.1'                    from dual union
     select 'another.test.doc'                  from dual union
     select 'another.test.docx'                 from dual union
     select 'another.test.stupidlng'            from dual 
     )

【讨论】:

查看我添加到问题中的 UPDATE。我进行了测试,该功能似乎对性能没有影响。我是否没有正确查看解释计划(我使用 TOAD)? 我在测试中添加了一个订单,看来你是对的。函数耗时 17 秒,正则表达式耗时 9 秒。 谢谢。这在 1 秒内有效!最终版本:select substr(filename, nullif( instr(filename,'.', -1) +1, 1) ) ext from testTable order by ext;【参考方案2】:

你需要使用正则表达式。

试试

select regexp_substr(filename, '\.[^\.]*$')
from
    (select 'no_extension_should_return_null' filename from dual);

我没有 Oracle 数据库来测试它,但这应该非常接近。

查看Oracle docs on regexp_substr 和Using regular expressions in Oracle database 了解更多信息。

更新

从文件扩展名中删除句点:

select substr(regexp_substr(filename, '\.[^\.]*$'), 2)
from
    (select 'abc.def' filename from dual);

【讨论】:

谢谢,我会研究一下正则表达式方法,但是对于像another.test.docx这样的多个句点的文件名,这个答案会失败 谢谢!我投了赞成票。我需要最快的,所以一旦我有几种方法可以尝试,我会针对数百万条记录运行所有方法,看看哪个是最快的。 其实我不需要“。”成为结果的一部分。我刚刚针对一个 200 万行的表运行了这个示例。看起来非常快。【参考方案3】:
SELECT NULLIF(substr(filename,instr(filename,'.',-1)+1,length(filename)-instr(filename,'.',-1)) from (select 'no_extension_should_return_null' filename from dual) t1, SELECT filename from t1);

抱歉没有预言机来测试它,不过我相信你明白了。

【讨论】:

+1 。 . .尽管格式错误且答案未经测试,但这可能是最快的方法——这正是 OP 所要求的。 我无法让它按原样运行,某处出现语法错误。一会儿我会玩它,看看它是怎么做的。 NULLIF 是票。我正确地重新格式化了它,它在 2 秒内运行,就像针对 200 万行表的其他测试一样。最终结果:SELECT NULLIF(substr(filename,instr(filename,'.',-1)+1,length(filename)-instr(filename,'.',-1)),filename) ext from testTable order by ext;【参考方案4】:

也许最简单的就是使用

regexp_substr(文件名, '[^\.]*$')

它适用于具有多个句点的文件名并且不返回句点。


对于没有扩展名的文件名可以使用下一个

选择 案子 当文件名像 '%.%' 然后 regexp_substr(filename, '[^.]*$') 结束分机 来自双重

【讨论】:

唯一的问题是文件名没有扩展名。因此,例如,regexp_substr('test', '[^\.]*$') 将返回 'test'。【参考方案5】:

是的,据我了解,您可以使用 DECODE 功能,查询如下:

SELECT substr(filename,instr(filename,'.',-1)+1,length(filename)- DECODE(INSTR(filename,'.',-1),0,LENGTH(filename),INSTR(filename,'.',-1))) from (select 'no_extension_should_return_null' filename from dual);

【讨论】:

以上是关于Oracle:使用 SQL 或 PL/SQL 提取文件扩展名的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

Oracle:使用 SQL 或 PL/SQL 查找动态 SQL 中的错误位置

使用 SQL 或 PL/SQL 解析 XML 文档以提取字段值

在 oracle 中使用正则表达式查找 POBOX - PL/SQL

在 PL/SQL 中解析 XML 或 JSON

ORACLE PL/SQL:使用集合的动态 SQL 选择

Oracle PL/SQL 中的字符编码问题