簡體   English   中英

如何在 PostgreSQL 中獲取字符串中正則表達式匹配的位置?

[英]How to get position of regexp match in string in PostgreSQL?

我有一個包含書名的表格,我想選擇標題與正則表達式匹配的書籍,並按正則表達式匹配在標題中的位置對結果進行排序。

單字搜索很容易。 例如

TABLE book
id   title
1    The Sun
2    The Dead Sun
3    Sun Kissed

在將查詢發送到數據庫之前,我將把.*放在客戶端搜索詞中的單詞之間,所以我會在這里編寫帶有准備好的正則表達式的 SQL。

SELECT book.id, book.title FROM book
    WHERE book.title ~* '.*sun.*'
    ORDER BY COALESCE(NULLIF(position('sun' in book.title), 0), 999999) ASC;

RESULT
id   title
3    Sun Kissed
1    The Sun
2    The Dead Sun

但是如果搜索詞有多個詞,我想將包含搜索詞中所有詞的標題與它們之間的任何詞進行匹配,並像以前一樣按位置排序,所以我需要一個返回正則表達式位置的函數,我沒有在官方 PostgreSQL 文檔中找到合適的。

TABLE books
id   title
4    Deep Space Endeavor
5    Star Trek: Deep Space Nine: The Never Ending Sacrifice
6    Deep Black: Space Espionage and National Security

SELECT book.id, book.title FROM book
    WHERE book.title ~* '.*deep.*space.*'
    ORDER BY ???REGEXP_POSITION_FUNCTION???('.*deep.*space.*' in book.title);

DESIRED RESULT
id   title
4    Deep Space Endeavor
6    Deep Black: Space Espionage and National Security
5    Star Trek: Deep Space Nine: The Never Ending Sacrifice

我沒有找到任何類似於???REGEXP_POSITION_FUNCTION???的函數,你有什么想法嗎?

執行此操作的一種方法(許多方法):刪除從匹配開始的字符串的其余部分並測量截斷字符串的長度:

SELECT id, title
FROM   book
WHERE  title ILIKE '%deep%space%'
ORDER  BY length(regexp_replace(title, 'deep.*space.*', '','i'));

在 WHERE 子句中使用ILIKE ,因為這通常更快(在這里也是如此)。
還要注意regexp_replace()函數( 'i' )的第四個參數,使其不區分大小寫。

備擇方案

根據評論中的要求。
同時演示如何首先對匹配項進行排序(和NULLS LAST )。

SELECT id, title
      ,substring(title FROM '(?i)(^.*)deep.*space.*') AS sub1
      ,length(substring(title FROM '(?i)(^.*)deep.*space.*')) AS pos1

      ,substring(title FROM '(?i)^.*(?=deep.*space.*)') AS sub2
      ,length(substring(title FROM '(?i)^.*(?=deep.*space.*)')) AS pos2

      ,substring(title FROM '(?i)^.*(deep.*space.*)') AS sub3
      ,position((substring(title FROM '(?i)^.*(deep.*space.*)')) IN title) AS p3

      ,regexp_replace(title, 'deep.*space.*', '','i') AS reg4
      ,length(regexp_replace(title, 'deep.*space.*', '','i')) AS pos4
FROM   book
ORDER  BY title ILIKE '%deep%space%' DESC NULLS LAST
         ,length(regexp_replace(title, 'deep.*space.*', '','i'));

您可以在此處此處的手冊中找到上述所有內容的文檔。

-> SQLfiddle演示了所有內容。

另一種方法是首先獲取模式的文字匹配,然后找到文字匹配的位置:

strpos(input, (regexp_match(input, pattern, 'i'))[1]);

或者在這種情況下:

SELECT   id, title
FROM     book
ORDER BY strpos(book.title, (regexp_match(book.title, '.*deep.*space.*', 'i'))[1]);

但是,有幾個警告:

  1. 這不是很有效,因為它會掃描輸入字符串兩次。

  2. 這將忽略環視(lookbehind,lookahead)約束,因為文字匹配可以在模式匹配之前出現多次。 例如:對於輸入'aba'和模式'(?<=b)a'strpos將返回1 (對於第一個'a' ),盡管實際位置應該是3 (對於第二個'a' )。

順便說一句,您可能應該使用貪婪的量詞並盡可能地縮小字符類而不是.*以提高性能(例如'deep [\\w\\s]*? space'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM