ค้นหาดัชนีของการเกิดครั้งสุดท้ายของสตริงย่อยโดยใช้ T-SQL


128

มีวิธีที่ตรงไปตรงมาในการค้นหาดัชนีของสตริงที่เกิดขึ้นล่าสุดโดยใช้ SQL หรือไม่? ตอนนี้ฉันใช้ SQL Server 2000 โดยพื้นฐานแล้วฉันต้องการฟังก์ชันที่System.String.LastIndexOfเมธอด. NET มีให้ googling เล็กน้อยเปิดเผยสิ่งนี้ - Function To Retrieve Last Index - แต่จะไม่ได้ผลหากคุณส่งผ่านนิพจน์คอลัมน์ "text" โซลูชันอื่น ๆ ที่พบในที่อื่นใช้ได้ตราบเท่าที่ข้อความที่คุณกำลังค้นหามีความยาว 1 อักขระ

ฉันอาจจะต้องทำอาหารขึ้นมา ถ้าฉันทำเช่นนั้นฉันจะโพสต์ไว้ที่นี่เพื่อให้คุณสามารถดูและใช้ประโยชน์ได้

คำตอบ:


32

คุณถูก จำกัดรายการฟังก์ชันเล็กน้อยสำหรับชนิดข้อมูลข้อความ

ทั้งหมดที่ฉันสามารถแนะนำได้คือเริ่มต้นด้วยPATINDEXแต่ทำงานย้อนกลับจากจุดDATALENGTH-1, DATALENGTH-2, DATALENGTH-3อื่น ๆ จนกว่าคุณจะได้ผลลัพธ์หรือจบลงที่ศูนย์ (DATALENGTH-DATALENGTH)

นี่เป็นสิ่งที่SQL Server 2000ไม่สามารถจัดการได้จริงๆ

แก้ไขสำหรับคำตอบอื่น ๆ : REVERSE ไม่อยู่ในรายการฟังก์ชันที่สามารถใช้กับข้อมูลข้อความใน SQL Server 2000


1
ใช่มันค่อนข้างน่าอึดอัด ดูเหมือนว่ามันควรจะเรียบง่าย แต่มันไม่ใช่!
Raj

... นี่คือเหตุผลที่ SQL 2005 มี varchar (สูงสุด) เพื่อให้สามารถใช้งานได้ตามปกติ
gbn

1
อา! ดังนั้น "varchar (สูงสุด)" จึงเป็นสิ่งที่ SQL 2005 ซึ่งอธิบายว่าเหตุใดจึงไม่ทำงานเมื่อฉันลองใช้ SQL 2000
Raj

DATALENGTH ไม่สามารถให้ผลลัพธ์ที่ถูกต้องสำหรับฉันแม้ว่า LENGTH จะใช้ได้ผลก็ตาม
Tequila

@Tequila และอื่น ๆ : DATALENGTHส่งคืนจำนวนไบต์ที่ไม่ใช่อักขระ ดังนั้นDATALENGTHส่งกลับ 2 x จำนวนอักขระในสตริงสำหรับNVARCHARสตริง LENแต่กลับจำนวนของตัวอักษรลบช่องว่างต่อท้าย ฉันไม่เคยใช้DATALENGTHสำหรับการคำนวณความยาวอักขระเว้นแต่การเว้นวรรคต่อท้ายจะมีความสำคัญและฉันรู้แน่นอนว่าประเภทข้อมูลของฉันสอดคล้องกันไม่ว่าจะเป็นVARCHARหรือNVARCHAR
rbsdca

175

ตรงไปตรงมา? ไม่ แต่ฉันใช้การย้อนกลับ อย่างแท้จริง

ในกิจวัตรก่อนหน้านี้เพื่อค้นหาการเกิดครั้งสุดท้ายของสตริงที่กำหนดฉันใช้ฟังก์ชัน REVERSE () ตาม CHARINDEX ตามด้วย REVERSE อีกครั้งเพื่อคืนค่าลำดับเดิม ตัวอย่างเช่น:

SELECT
   mf.name
  ,mf.physical_name
  ,reverse(left(reverse(physical_name), charindex('\', reverse(physical_name)) -1))
 from sys.master_files mf

แสดงวิธีแยกชื่อไฟล์ฐานข้อมูลจริงจาก "ชื่อจริง" ไม่ว่าจะซ้อนกันลึกแค่ไหนในโฟลเดอร์ย่อย สิ่งนี้จะค้นหาเพียงอักขระเดียว (แบ็กสแลช) แต่คุณสามารถสร้างสิ่งนี้สำหรับสตริงการค้นหาที่ยาวขึ้น

ข้อเสียเพียงอย่างเดียวคือฉันไม่รู้ว่าสิ่งนี้จะใช้ได้ดีแค่ไหนกับประเภทข้อมูล TEXT ฉันใช้ SQL 2005 มาสองสามปีแล้วและฉันไม่คุ้นเคยกับการทำงานกับ TEXT อีกต่อไป - แต่ฉันจำได้ว่าคุณสามารถใช้ LEFT และ RIGHT กับมันได้หรือไม่

ฟิลิป


1
ขอโทษ - ฉันค่อนข้างแน่ใจว่าฉันไม่เคยย้อนกลับไปเมื่อฉันทำงานกับ 2000 และตอนนี้ฉันไม่สามารถเข้าถึงการติดตั้ง SQL 2000 ใด ๆ
Philip Kelley

ยอดเยี่ยม! ไม่เคยคิดที่จะโจมตีปัญหานี้ด้วยวิธีนี้!
Jared

4
ทำได้ดีนี่! ฉันแก้ไขตามความต้องการของตัวเอง: email.Substring (0, email.lastIndexOf ('@')) == SELECT LEFT (email, LEN (email) -CHARINDEX ('@', REVERSE (email)))
Fredrik Johansson

1
ฉลาด ๆ แบบนี้ทำไมการเขียนโปรแกรมถึงสนุกจัง!
คริส

ทำไมไม่ใช้ทางขวาแทนที่จะใช้ทางซ้ายของต้นฉบับแทนที่จะเป็นการย้อนกลับพิเศษ
Phil

108

วิธีที่ง่ายที่สุดคือ ....

REVERSE(SUBSTRING(REVERSE([field]),0,CHARINDEX('[expr]',REVERSE([field]))))

3
+1 เนื่องจากไม่ทำให้เกิดข้อผิดพลาดเช่น 'พารามิเตอร์ความยาวไม่ถูกต้องส่งผ่านไปยังฟังก์ชัน LEFT หรือ SUBSTRING' หากไม่พบรายการที่ตรงกัน
Xilmiki

12
หากคุณ[expr]มีความยาวมากกว่า 1 สัญลักษณ์คุณต้องย้อนกลับด้วย!
Andrius Naruševičius

60

หากคุณใช้ Sqlserver 2005 ขึ้นไปการใช้REVERSEฟังก์ชันหลาย ๆ ครั้งจะส่งผลเสียต่อประสิทธิภาพโค้ดด้านล่างจะมีประสิทธิภาพมากกว่า

DECLARE @FilePath VARCHAR(50) = 'My\Super\Long\String\With\Long\Words'
DECLARE @FindChar VARCHAR(1) = '\'

-- Shows text before last slash
SELECT LEFT(@FilePath, LEN(@FilePath) - CHARINDEX(@FindChar,REVERSE(@FilePath))) AS Before
-- Shows text after last slash
SELECT RIGHT(@FilePath, CHARINDEX(@FindChar,REVERSE(@FilePath))-1) AS After
-- Shows the position of the last slash
SELECT LEN(@FilePath) - CHARINDEX(@FindChar,REVERSE(@FilePath)) AS LastOccuredAt

1
อาจดูเหมือนชัดเจนในการมองย้อนกลับไป แต่ถ้าคุณกำลังค้นหาสตริงแทนที่จะเป็นอักขระเดี่ยวคุณต้องทำ: LEN (@FilePath) - CHARINDEX (REVERSE (@FindString), REVERSE (@FilePath))
pkExec

14
DECLARE @FilePath VARCHAR(50) = 'My\Super\Long\String\With\Long\Words'
DECLARE @FindChar VARCHAR(1) = '\'

SELECT LEN(@FilePath) - CHARINDEX(@FindChar,REVERSE(@FilePath)) AS LastOccuredAt

8

คำถามเก่า แต่ยังใช้ได้ดังนั้นนี่คือสิ่งที่ฉันสร้างขึ้นตามข้อมูลที่ผู้อื่นให้ไว้ที่นี่

create function fnLastIndexOf(@text varChar(max),@char varchar(1))
returns int
as
begin
return len(@text) - charindex(@char, reverse(@text)) -1
end

7

สิ่งนี้ได้ผลดีสำหรับฉัน

REVERSE(SUBSTRING(REVERSE([field]), CHARINDEX(REVERSE('[expr]'), REVERSE([field])) + DATALENGTH('[expr]'), DATALENGTH([field])))


4

อืมฉันรู้ว่านี่เป็นเธรดเก่า แต่ตารางนับสามารถทำได้ใน SQL2000 (หรือฐานข้อมูลอื่น ๆ ):

DECLARE @str CHAR(21),
        @delim CHAR(1)
 SELECT @str = 'Your-delimited-string',
        @delim = '-'

SELECT
    MAX(n) As 'position'
FROM
    dbo._Tally
WHERE
    substring(@str, _Tally.n, 1) = @delim

ตารางนับเป็นเพียงตารางของตัวเลขที่เพิ่มขึ้น

substring(@str, _Tally.n, 1) = @delimได้รับตำแหน่งของแต่ละตัวคั่นแล้วคุณก็จะได้รับตำแหน่งสูงสุดในชุดว่า

ตารางนับยอดเยี่ยมมาก หากคุณไม่เคยใช้มาก่อนมีบทความดีๆเกี่ยวกับSQL Server Central (Reg ฟรีหรือใช้ Bug Me Not ( http://www.bugmenot.com/view/sqlservercentral.com ))

* แก้ไข: ลบออกn <= LEN(TEXT_FIELD)เนื่องจากคุณไม่สามารถใช้ LEN () ในประเภท TEXT ได้ ตราบเท่าที่substring(...) = @delimยังคงอยู่แม้ว่าผลลัพธ์จะยังคงถูกต้อง


ดี ฉันคิดว่านี่เป็นวิธีแก้ปัญหาเดียวกับคำตอบที่ได้รับการยอมรับจาก gbn คุณแค่ใช้ตารางเพื่อเก็บจำนวนเต็ม 1, 2, 3 ฯลฯ ที่หักออกจาก DATALENGTH และอ่านจากอักขระตัวแรกไปข้างหน้าแทนที่จะเป็นอักขระตัวสุดท้ายกลับ
Michael Petito

2

ย้อนกลับทั้งสตริงและสตริงย่อยของคุณจากนั้นค้นหาเหตุการณ์แรก


จุดดี. ตอนนี้ฉันไม่มี 2,000 และฉันจำไม่ได้ว่าจะทำได้ไหมเมื่อฉันทำ
AK

2

คำตอบอื่น ๆ บางคำตอบกลับสตริงจริงในขณะที่ฉันต้องการทราบดัชนี int จริงมากกว่า และคำตอบที่ดูเหมือนจะซับซ้อนเกินไป ใช้คำตอบอื่น ๆ เป็นแรงบันดาลใจฉันทำสิ่งต่อไปนี้ ...

ก่อนอื่นฉันสร้างฟังก์ชัน:

CREATE FUNCTION [dbo].[LastIndexOf] (@stringToFind varchar(max), @stringToSearch varchar(max))
RETURNS INT
AS
BEGIN
    RETURN (LEN(@stringToSearch) - CHARINDEX(@stringToFind,REVERSE(@stringToSearch))) + 1
END
GO

จากนั้นในแบบสอบถามของคุณคุณสามารถทำได้:

declare @stringToSearch varchar(max) = 'SomeText: SomeMoreText: SomeLastText'

select dbo.LastIndexOf(':', @stringToSearch)

ข้างต้นควรส่งกลับ 23 (ดัชนีสุดท้ายของ ':')

หวังว่านี่จะทำให้ใครบางคนง่ายขึ้น!


2

ฉันรู้ว่านี่เป็นคำถามเก่าแก่หลายปี แต่ ...

ในวันที่Access 2010คุณสามารถใช้InStrRev()การทำเช่นนี้ หวังว่านี่จะช่วยได้


2

คำตอบนี้ใช้ MS SQL Server 2008 (ฉันไม่มีสิทธิ์เข้าถึง MS SQL Server 2000) แต่วิธีที่ฉันเห็นตาม OP มี 3 สถานการณ์ที่ต้องพิจารณา จากสิ่งที่ฉันพยายามไม่มีคำตอบที่นี่ครอบคลุมทั้ง 3 ข้อ:

  1. ส่งกลับดัชนีสุดท้ายของอักขระค้นหาในสตริงที่กำหนด
  2. ส่งคืนดัชนีสุดท้ายของสตริงย่อยการค้นหา (มากกว่าเพียงอักขระเดียว) ในสตริงที่กำหนด
  3. หากอักขระค้นหาหรือสตริงย่อยไม่อยู่ในการส่งคืนสตริงที่กำหนด 0

ฟังก์ชั่นที่ฉันสร้างขึ้นใช้พารามิเตอร์ 2 ตัว:

@String NVARCHAR(MAX) : สตริงที่จะค้นหา

@FindString NVARCHAR(MAX) : อักขระเดี่ยวหรือสตริงย่อยเพื่อรับดัชนีสุดท้ายของ in @String

ส่งคืนค่าINTที่เป็นดัชนีเชิงบวกของ@FindStringin @Stringหรือ0ความหมายที่@FindStringไม่อยู่ใน@String

นี่คือคำอธิบายว่าฟังก์ชันทำอะไร:

  1. เริ่มต้น@ReturnValเพื่อ0ระบุว่าไม่ได้@FindStringอยู่ใน@String
  2. ตรวจสอบดัชนีของ@FindStringอิน@Stringโดยใช้CHARINDEX()
  3. หากดัชนีของ@FindStringใน@Stringถูก0, @ReturnValเป็นซ้ายเป็น0
  4. หากดัชนีของ@FindStringใน@Stringเป็น> 0, @FindStringอยู่ใน@Stringจึงคำนวณดัชนีสุดท้ายของ@FindStringใน@Stringโดยใช้REVERSE()
  5. ส่งกลับ@ReturnValซึ่งเป็นจำนวนบวกที่เป็นดัชนีสุดท้ายของ @FindStringin @Stringหรือ0แสดงว่า@FindStringไม่อยู่ใน@String

นี่คือสคริปต์สร้างฟังก์ชัน (คัดลอกและวางพร้อม):

CREATE FUNCTION [dbo].[fn_LastIndexOf] 
(@String NVARCHAR(MAX)
, @FindString NVARCHAR(MAX))
RETURNS INT
AS 
BEGIN
    DECLARE @ReturnVal INT = 0
    IF CHARINDEX(@FindString,@String) > 0
        SET @ReturnVal = (SELECT LEN(@String) - 
        (CHARINDEX(REVERSE(@FindString),REVERSE(@String)) + 
        LEN(@FindString)) + 2)  
    RETURN @ReturnVal
END

นี่เป็นเพียงเล็กน้อยที่ทดสอบฟังก์ชันได้อย่างสะดวก:

DECLARE @TestString NVARCHAR(MAX) = 'My_sub2_Super_sub_Long_sub1_String_sub_With_sub_Long_sub_Words_sub2_'
, @TestFindString NVARCHAR(MAX) = 'sub'

SELECT dbo.fn_LastIndexOf(@TestString,@TestFindString)

ฉันเรียกใช้สิ่งนี้บน MS SQL Server 2008 เท่านั้นเพราะฉันไม่สามารถเข้าถึงเวอร์ชันอื่นได้ แต่จากสิ่งที่ฉันได้ตรวจสอบแล้วสิ่งนี้ควรจะดีสำหรับปี 2008+ เป็นอย่างน้อย

สนุก.


1

ฉันรู้ว่ามันจะไม่มีประสิทธิภาพ แต่คุณได้พิจารณาคัดเลือกtextฟิลด์varcharเพื่อที่คุณจะสามารถใช้โซลูชันที่มาจากเว็บไซต์ที่คุณพบได้หรือไม่ ฉันรู้ว่าโซลูชันนี้จะสร้างปัญหาเนื่องจากคุณสามารถตัดทอนเร็กคอร์ดได้หากความยาวในtextฟิลด์มากเกินความยาวของvarchar (ไม่ต้องพูดถึงมันจะไม่มีประสิทธิภาพมากนัก)

เนื่องจากข้อมูลของคุณอยู่ในtextฟิลด์ (และคุณกำลังใช้ SQL Server 2000) ตัวเลือกของคุณจึงมี จำกัด


ใช่การแคสต์เป็น "varchar" ไม่ใช่ตัวเลือกเนื่องจากข้อมูลที่ประมวลผลบ่อยเกินค่าสูงสุดที่สามารถเก็บไว้ใน "varchar" ได้ ขอบคุณสำหรับคำตอบของคุณ!
Raj

1

หากคุณต้องการรับดัชนีของช่องว่างสุดท้ายในสตริงคำคุณสามารถใช้นิพจน์นี้ RIGHT (name, (CHARINDEX ('', REVERSE (name), 0)) เพื่อส่งคืนคำสุดท้ายในสตริงนี้ จะมีประโยชน์หากคุณต้องการแยกวิเคราะห์นามสกุลของชื่อเต็มที่มีชื่อย่อของชื่อและ / หรือชื่อกลาง


1

@indexOf = <whatever characters you are searching for in your string>

@LastIndexOf = LEN([MyField]) - CHARINDEX(@indexOf, REVERSE([MyField]))

ยังไม่ได้ทดสอบอาจปิดทีละรายการเนื่องจากดัชนีเป็นศูนย์ แต่ทำงานในSUBSTRINGฟังก์ชันเมื่อตัดจาก@indexOfอักขระไปยังจุดสิ้นสุดของสตริงของคุณ

SUBSTRING([MyField], 0, @LastIndexOf)


1

รหัสนี้ใช้งานได้แม้ว่าสตริงย่อยจะมีอักขระมากกว่า 1 ตัว

DECLARE @FilePath VARCHAR(100) = 'My_sub_Super_sub_Long_sub_String_sub_With_sub_Long_sub_Words'
DECLARE @FindSubstring VARCHAR(5) = '_sub_'

-- Shows text before last substing
SELECT LEFT(@FilePath, LEN(@FilePath) - CHARINDEX(REVERSE(@FindSubstring), REVERSE(@FilePath)) - LEN(@FindSubstring) + 1) AS Before
-- Shows text after last substing
SELECT RIGHT(@FilePath, CHARINDEX(REVERSE(@FindSubstring), REVERSE(@FilePath)) -1) AS After
-- Shows the position of the last substing
SELECT LEN(@FilePath) - CHARINDEX(REVERSE(@FindSubstring), REVERSE(@FilePath)) AS LastOccuredAt

0

ฉันต้องการค้นหาตำแหน่งสุดท้ายที่ n ของแบ็กสแลชในเส้นทางโฟลเดอร์ นี่คือทางออกของฉัน

/*
http://stackoverflow.com/questions/1024978/find-index-of-last-occurrence-of-a-sub-string-using-t-sql/30904809#30904809
DROP FUNCTION dbo.GetLastIndexOf
*/
CREATE FUNCTION dbo.GetLastIndexOf
(
  @expressionToFind         VARCHAR(MAX)
  ,@expressionToSearch      VARCHAR(8000)
  ,@Occurrence              INT =  1        -- Find the nth last 
)
RETURNS INT
AS
BEGIN

    SELECT  @expressionToSearch = REVERSE(@expressionToSearch)

    DECLARE @LastIndexOf        INT = 0
            ,@IndexOfPartial    INT = -1
            ,@OriginalLength    INT = LEN(@expressionToSearch)
            ,@Iteration         INT = 0

    WHILE (1 = 1)   -- Poor man's do-while
    BEGIN
        SELECT @IndexOfPartial  = CHARINDEX(@expressionToFind, @expressionToSearch)

        IF (@IndexOfPartial = 0) 
        BEGIN
            IF (@Iteration = 0) -- Need to compensate for dropping out early
            BEGIN
                SELECT @LastIndexOf = @OriginalLength  + 1
            END
            BREAK;
        END

        IF (@Occurrence > 0)
        BEGIN
            SELECT @expressionToSearch = SUBSTRING(@expressionToSearch, @IndexOfPartial + 1, LEN(@expressionToSearch) - @IndexOfPartial - 1)
        END

        SELECT  @LastIndexOf = @LastIndexOf + @IndexOfPartial
                ,@Occurrence = @Occurrence - 1
                ,@Iteration = @Iteration + 1

        IF (@Occurrence = 0) BREAK;
    END

    SELECT @LastIndexOf = @OriginalLength - @LastIndexOf + 1 -- Invert due to reverse
    RETURN @LastIndexOf 
END
GO

GRANT EXECUTE ON GetLastIndexOf TO public
GO

นี่คือกรณีทดสอบของฉันที่ผ่าน

SELECT dbo.GetLastIndexOf('f','123456789\123456789\', 1) as indexOf -- expect 0 (no instances)
SELECT dbo.GetLastIndexOf('\','123456789\123456789\', 1) as indexOf -- expect 20
SELECT dbo.GetLastIndexOf('\','123456789\123456789\', 2) as indexOf -- expect 10
SELECT dbo.GetLastIndexOf('\','1234\6789\123456789\', 3) as indexOf -- expect 5

0

ในการรับส่วนก่อนการเกิดครั้งสุดท้ายของตัวคั่น (ใช้งานได้NVARCHARเนื่องจากการDATALENGTHใช้งานเท่านั้น):

DECLARE @Fullstring NVARCHAR(30) = '12.345.67890.ABC';

DECLARE @Delimiter CHAR(1) = '.';

SELECT SUBSTRING(@Fullstring, 1, DATALENGTH(@Fullstring)/2 - CHARINDEX(@Delimiter, REVERSE(@Fullstring)));

0

คำตอบนี้ตรงตามข้อกำหนดของ OP โดยเฉพาะจะช่วยให้เข็มมีมากกว่าหนึ่งอักขระและไม่สร้างข้อผิดพลาดเมื่อไม่พบเข็มในกองหญ้า สำหรับฉันแล้วดูเหมือนว่าคำตอบอื่น ๆ ส่วนใหญ่ (ทั้งหมด?) ไม่ได้จัดการกับกรณีขอบเหล่านั้น นอกเหนือจากนั้นฉันได้เพิ่มอาร์กิวเมนต์ "ตำแหน่งเริ่มต้น" ที่มาจากฟังก์ชัน CharIndex ของเซิร์ฟเวอร์ MS SQL ดั้งเดิม ฉันพยายามจำลองข้อกำหนดสำหรับ CharIndex ทุกประการยกเว้นการประมวลผลจากขวาไปซ้ายแทนที่จะเป็นซ้ายไปขวา เช่นฉันคืนค่า null ถ้าเข็มหรือกองหญ้าเป็นโมฆะและฉันจะคืนค่าศูนย์หากไม่พบเข็มในกองหญ้า สิ่งหนึ่งที่ฉันไม่สามารถแก้ไขได้คือด้วยฟังก์ชันในตัวพารามิเตอร์ที่สามเป็นทางเลือก ด้วยฟังก์ชันที่กำหนดโดยผู้ใช้ SQL Server จะต้องระบุพารามิเตอร์ทั้งหมดในการเรียกเว้นแต่ฟังก์ชันจะถูกเรียกโดยใช้ "EXEC" . แม้ว่าพารามิเตอร์ที่สามจะต้องรวมอยู่ในรายการพารามิเตอร์ แต่คุณสามารถระบุคีย์เวิร์ด "default" เป็นตัวยึดตำแหน่งได้โดยไม่ต้องระบุค่า (ดูตัวอย่างด้านล่าง) เนื่องจากการลบพารามิเตอร์ที่สามออกจากฟังก์ชันนี้ทำได้ง่ายกว่าหากไม่ต้องการมากกว่าที่จะเพิ่มหากจำเป็นฉันจึงรวมไว้ที่นี่เป็นจุดเริ่มต้น

create function dbo.lastCharIndex(
 @needle as varchar(max),
 @haystack as varchar(max),
 @offset as bigint=1
) returns bigint as begin
 declare @position as bigint
 if @needle is null or @haystack is null return null
 set @position=charindex(reverse(@needle),reverse(@haystack),@offset)
 if @position=0 return 0
 return (len(@haystack)-(@position+len(@needle)-1))+1
end
go

select dbo.lastCharIndex('xyz','SQL SERVER 2000 USES ANSI SQL',default) -- returns 0
select dbo.lastCharIndex('SQL','SQL SERVER 2000 USES ANSI SQL',default) -- returns 27
select dbo.lastCharIndex('SQL','SQL SERVER 2000 USES ANSI SQL',1) -- returns 27
select dbo.lastCharIndex('SQL','SQL SERVER 2000 USES ANSI SQL',11) -- returns 1

0

ฉันเจอหัวข้อนี้ในขณะที่ค้นหาวิธีแก้ปัญหาที่คล้ายกันของฉันซึ่งมีข้อกำหนดเหมือนกัน แต่เป็นฐานข้อมูลประเภทอื่นที่ยังไม่มี REVERSEฟังก์ชัน

ในกรณีของฉันนี่เป็นฐานข้อมูลOpenEdge (Progress)ซึ่งมีไวยากรณ์ที่แตกต่างกันเล็กน้อย สิ่งนี้ทำให้INSTRฉันสามารถใช้งานฟังก์ชันที่ฐานข้อมูลประเภท Oracle ส่วนใหญ่เสนอส่วนใหญ่ของออราเคิลพิมพ์ฐานข้อมูลนำเสนอ

ดังนั้นฉันจึงคิดรหัสต่อไปนี้:

SELECT 
  INSTR(foo.filepath, '/',1, LENGTH(foo.filepath) - LENGTH( REPLACE( foo.filepath, '/',  ''))) AS IndexOfLastSlash 
FROM foo

อย่างไรก็ตามสำหรับสถานการณ์เฉพาะของฉัน (เป็นฐานข้อมูลOpenEdge (Progress) ) สิ่งนี้ไม่ได้ส่งผลให้เกิดลักษณะการทำงานที่ต้องการเนื่องจากการแทนที่อักขระด้วยอักขระว่างเปล่าจะให้ความยาวเท่ากับสตริงเดิม สิ่งนี้ไม่สมเหตุสมผลสำหรับฉัน แต่ฉันสามารถข้ามปัญหาด้วยรหัสด้านล่าง:

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

ตอนนี้ฉันเข้าใจแล้วว่ารหัสนี้ไม่สามารถแก้ปัญหาสำหรับT-SQL ได้เนื่องจากไม่มีทางเลือกอื่นสำหรับINSTRฟังก์ชันที่นำเสนอไฟล์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

เพื่อหลีกเลี่ยงความชัดเจนเมื่อREVERSEฟังก์ชันพร้อมใช้งานคุณไม่จำเป็นต้องสร้างฟังก์ชันสเกลาร์นี้และคุณจะได้ผลลัพธ์ที่ต้องการดังนี้:

SELECT
  LEN(foo.filepath) - CHARINDEX('/', REVERSE(foo.filepath))+1 AS LastIndexOfSlash 
FROM foo

0

จัดการ lookinng สำหรับบางสิ่งบางอย่าง> 1 ถ่านยาว อย่าลังเลที่จะเพิ่มขนาดพาร์มหากต้องการ

ไม่สามารถต้านทานการโพสต์

drop function if exists lastIndexOf
go 
create function lastIndexOf(@searchFor varchar(100),@searchIn varchar(500))
returns int
as
begin 

if LEN(@searchfor) > LEN(@searchin) return 0 
declare @r varchar(500), @rsp varchar(100)
select @r = REVERSE(@searchin)
select @rsp = REVERSE(@searchfor)
return len(@searchin) - charindex(@rsp, @r) - len(@searchfor)+1
end 

และการทดสอบ

select dbo.lastIndexof('greg','greg greg asdflk; greg sadf' )  -- 18
select dbo.lastIndexof('greg','greg greg asdflk; grewg sadf' )  --5
select dbo.lastIndexof(' ','greg greg asdflk; grewg sadf' ) --24
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.