寻找标量函数来查找字符串中最后一个字符

时间:2014-04-11 07:56:13

标签: sql db2

FOO的列FILEPATH类型为VARCHAR(512)。它的条目是绝对路径:

FILEPATH
------------------------------------------------------------
file://very/long/file/path/with/many/slashes/in/it/foo.xml
file://even/longer/file/path/with/more/slashes/in/it/baz.xml
file://something/completely/different/foo.xml
file://short/path/foobar.xml

此表中有大约50,000条记录,我想知道所有不同的文件名,而不是文件路径:

foo.xml
baz.xml
foobar.xml

这看起来很简单,但我找不到允许我搜索字符串中最后一个字符的DB2标量函数。我在监督什么吗?

我可以通过递归查询来做到这一点,但这对于这样一个简单的任务来说似乎有点过头了(哦,奇迹)非常慢:

WITH PATHFRAGMENTS (POS, PATHFRAGMENT) AS (
  SELECT
    1,
    FILEPATH
  FROM FOO

UNION ALL

  SELECT
    POSITION('/', PATHFRAGMENT, OCTETS) AS POS, 
    SUBSTR(PATHFRAGMENT, POSITION('/', PATHFRAGMENT, OCTETS)+1) AS PATHFRAGMENT
  FROM PATHFRAGMENTS
)
SELECT DISTINCT PATHFRAGMENT FROM PATHFRAGMENTS WHERE POS = 0

4 个答案:

答案 0 :(得分:8)

我认为您正在寻找的是LOCATE_IN_STRING() scalar function。如果您使用负起始值,则信息中心必须说明这一点:

  

如果整数的值小于零,则搜索从   LENGTH(源字符串)+开始+ 1并继续每个位置   字符串的开头。

将其与LENGTH()RIGHT()标量函数相结合,您就可以获得所需内容:

SELECT
    RIGHT(
         FILEPATH
        ,LENGTH(FILEPATH) - LOCATE_IN_STRING(FILEPATH,'/',-1)
    )
FROM FOO

答案 1 :(得分:1)

实现此目的的一种方法是利用DB2 XQuery引擎的强大功能。以下对我有用(而且速度很快):

SELECT DISTINCT XMLCAST(
     XMLQuery('tokenize($P, ''/'')[last()]' PASSING FILEPATH AS "P")
   AS VARCHAR(512) )
FROM FOO

在这里,我使用tokenize将文件路径拆分为一系列标记,然后选择最后一个标记。其余的只是从SQL转换为XML类型再返回。

答案 2 :(得分:0)

您可以在一个声明中执行此操作:

select distinct reverse(substring(reverse(FILEPATH), 1, charindex('/', reverse(FILEPATH))-1))
from filetable

答案 3 :(得分:0)

我知道OP的问题已经解决但我决定发布以下信息,希望能帮助像我这样的人来到这里。

我在搜索我的类似问题的解决方案时遇到了这个问题,该解决方案具有完全相同的要求但是对于另一种也缺少REVERSE函数的数据库。

在我的情况下,这是针对 OpenEdge(Progress)数据库,其语法略有不同。这使得INSTR函数SELECT SUBSTRING( foo.filepath, INSTR(foo.filepath, '/',1, LENGTH(foo.filepath) - LENGTH( REPLACE( foo.filepath, '/', '')))+1, LENGTH(foo.filepath)) FROM foo 可以使用most Oracle typed databases offer

所以我想出了以下代码:

SELECT 
  SUBSTRING( 
    foo.filepath, 
    INSTR(foo.filepath, '/',1, LENGTH( REPLACE( foo.filepath, '/',  'XX')) - LENGTH(foo.filepath))+1, 
    LENGTH(foo.filepath))
FROM foo

但是,对于我的特定情况(作为 OpenEdge(进度)数据库),这不会导致所需的行为,因为用空字符替换字符会给出与原始字符串相同的长度。这对我来说没有多大意义,但我能够通过以下代码绕过问题:

INSTR

现在我明白这段代码无法解决 T-SQL 的问题,因为除了提供Occurence属性的 -- Drop the function if it already exists IF OBJECT_ID('INSTR', 'FN') IS NOT NULL DROP FUNCTION INSTR GO -- User-defined function to implement Oracle INSTR in SQL Server CREATE FUNCTION INSTR (@str VARCHAR(8000), @substr VARCHAR(255), @start INT, @occurrence INT) RETURNS INT AS BEGIN DECLARE @found INT = @occurrence, @pos INT = @start; WHILE 1=1 BEGIN -- Find the next occurrence SET @pos = CHARINDEX(@substr, @str, @pos); -- Nothing found IF @pos IS NULL OR @pos = 0 RETURN @pos; -- The required occurrence found IF @found = 1 BREAK; -- Prepare to find another one occurrence SET @found = @found - 1; SET @pos = @pos + 1; END RETURN @pos; END GO 函数之外别无选择。< / p>

为了彻底,我将添加创建此标量函数所需的代码,以便它可以像我在上面的示例中一样使用。

REVERSE

为避免显而易见,当SELECT SUBSTRING( foo.filepath, LEN(foo.filepath) - CHARINDEX('\', REVERSE(foo.filepath))+2, LEN(foo.filepath)) FROM foo 函数可用时,您不需要创建此标量函数,您只需得到所需的结果:

filter