Substring

從絕對位置提取不帶副檔名的文件名

  • May 7, 2020

我正在嘗試獲取包含在表的特定列的值中的文件名。我的表如下所示:

absolutel_path
\\Path\filename.extension

我需要filenameabsolute_path( )中提取文件名(在上面\\Path\filename.extension)。我應該使用哪個函式來獲取我的文件名(子字元串)?

編輯:

儘管我的第一個解決方案按要求回答了問題,但我看到了@DavidBoho 的回答,他提出了幾個要點。他建議如果文件名是,my_file.tar.gz那麼返回值應該是my_file.tar,並且如果文件根本沒有副檔名,我的解決方案將失敗。這裡的所有程式碼都可以在這個fiddle上找到。

給定表格和數據如下:

CREATE TABLE with_filename
(
 file_id INTEGER,
 file_name VARCHAR (256)
);

填充:

INSERT INTO with_filename
VALUES
(1, '/users/mcm1/ualaoip2/vmm/file1.pdf'),
(2, '/users/mcm1/ualaoip2/vmm/file2.py'),
(3, '/users/mcm1/ualaoip2/vmm/file3.pdf'),
(4, '/users/mcm1/ualaoip2/vmm/file4.c'),
(5, '/users/mcm1/ualaoip2/vmm/file5.java'),
(6, '/users/mcm1/ualaoip2/vmm/file6.class'),
(7, '/users/mcm1/ualaoip2/vmm/file7'),
(8, '/users/mcm1/ualaoip2/vmm/file8.tar.gz'),
(9, '/users/mcm1/my_prog.cpp');

我原來的解決方案:

SELECT LEFT(
     RIGHT(file_name, POSITION('/' IN REVERSE(file_name)) - 1),
                      POSITION('.' IN 
     RIGHT(file_name, POSITION('/' IN REVERSE(file_name)) - 1)) - 1
) AS my_file
FROM with_filename;

給出結果:

my_file
file1
file2
file3
file4
file5
file6
file       -- << should be file7
file8      -- << should be file8.tar
my_prog

閱讀@DavidBoho 的文章後,他使用該SPLIT_PART功能解決了文件 7 和 8 的問題 - 請參閱小提琴。我決定再看看我自己的 SQL,我想出了這個(也許更傳統?):

SELECT 
     REPLACE(SUBSTRING(file_name, (LENGTH(file_name) + 2) - POSITION('/' IN REVERSE(file_name))),
     RIGHT(file_name, POSITION('.' IN LEFT(REVERSE(file_name), POSITION('/' IN REVERSE(file_name)) - 1))),
     '') AS the_files
   FROM with_filename

結果:

the_files
file1
file2
file3
file4
file5
file6
file7
file8.tar
my_prog

也是正確答案!

當我在尋找解決方案時,我開始對正則表達式感興趣,因為它可以作為解決這個問題的一種手段。儘管我(我們)能夠/能夠使用“傳統” SQL 解決這個問題,但我很清楚正則表達式非常強大,即使 SQL 現在是圖靈完備的,但對於相對簡單的字元串操作問題,它可能會迅速變得非常複雜,所以我決定調查一下。

我找到了兩個正則表達式解決方案 - 公平地說,我不能聲稱自己已經這樣做了,這些解決方案是我在StackOverflow上提出的問題的結果。因此,正則表達式解決方案如下:

最好的是這個——

SELECT
   file_name,
   REGEXP_REPLACE(file_name, '^.*/([^/]*?)(\.[^/.]+)?$', '\1') AS filename
FROM with_filename;

還有第二個,但恕我直言(以及原作者的)它有點像黑客 - 它涉及兩個嵌套REGEXP_REPLACE的 s

SELECT
   file_name,
   REGEXP_REPLACE(REGEXP_REPLACE(file_name, '^.*/(.*)$', '\1'), '\.[^.]+$', '') AS filename
FROM with_filename

最後,可能有一個解決方案可能同時使用 UNNEST 和 STRING_TO_ARRAY 函式 - 我想出了這個程式碼:

SELECT fn,  
   LEFT(fn, POSITION('.' IN fn) - 1) AS lef
FROM with_filename w,
 UNNEST(STRING_TO_ARRAY(w.file_name, '/')) AS fn
GROUP BY fn
HAVING COUNT(fn) = 1
ORDER BY lef;

結果是:

fn  lef
file7   file       -- << should be file7
file1.pdf   file1
file2.py    file2
file3.pdf   file3
file4.c     file4
file5.java  file5
file6.class     file6
file8.tar.gz    file8  -- << should be file8.tar
my_prog.cpp     my_prog

我嘗試了很多不同的排列,但無法讓它發揮作用。將不勝感激任何輸入!:-)

另一個有趣的函式是 REGEXP_SPLIT_TO_TABLE。

SELECT 
 fn, 
 COUNT(fn)
FROM
(
 SELECT REGEXP_SPLIT_TO_TABLE(w.file_name, '/') AS fn
 FROM with_filename w
) AS sq
GROUP BY fn
HAVING COUNT(fn) = 1
ORDER BY fn

結果:

fn  count
file1.pdf   1
file2.py    1
file3.pdf   1
file4.c     1
file5.java  1
file6.class     1
file7   1
file8.tar.gz    1
my_prog.cpp     1

同樣,這可能值得追求——沒有時間。

範例 DML/DDL

CREATE TABLE with_filename
(
 file_id INTEGER,
 file_name VARCHAR (256)
);

INSERT INTO with_filename
VALUES
(1, '/users/mcm1/ualaoip2/vmm/file1.pdf'),
(2, '/users/mcm1/ualaoip2/vmm/file2.py'),
(3, '/users/mcm1/ualaoip2/vmm/file3.pdf'),
(4, '/users/mcm1/ualaoip2/vmm/file4.c'),
(5, '/users/mcm1/ualaoip2/vmm/file5.java'),
(6, '/users/mcm1/ualaoip2/vmm/file6.class'),
(7, '/users/mcm1/ualaoip2/vmm/file7'),
(8, '/users/mcm1/ualaoip2/vmm/file8.tar.gz'),
(9, '/users/mcm1/my_prog.cpp');

引用自:https://dba.stackexchange.com/questions/190982