คุณนับจำนวนครั้งของสตริงย่อยที่แน่นอนใน SQL varchar อย่างไร


คำตอบ:


245

วิธีแรกที่ควรคำนึงถึงคือทำโดยอ้อมด้วยการแทนที่เครื่องหมายจุลภาคด้วยสตริงว่างและเปรียบเทียบความยาว

Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))

13
ที่ตอบคำถามตามที่เขียนไว้ในข้อความ แต่ไม่ได้เขียนไว้ในชื่อเรื่อง ในการทำให้มันใช้งานได้มากกว่าหนึ่งตัวละครเพียงแค่เพิ่ม / len (searchterm) รอบตัว โพสต์คำตอบในกรณีนี้มันมีประโยชน์สำหรับใครบางคน
Andrew Barrett

บางคนชี้ให้ฉันเห็นว่าสิ่งนี้ไม่ได้ผลตามที่คาดหวังเสมอไป พิจารณาสิ่งต่อไปนี้: เลือก LEN ('a, b, c, d,') - LEN (แทนที่ ('a, b, c, d,', ',', ',' ')) ด้วยเหตุผลที่ฉันยังไม่เข้าใจ ช่องว่างระหว่าง d และคอลัมน์สุดท้ายทำให้สิ่งนี้ส่งคืน 5 แทน 4 ฉันจะโพสต์คำตอบอื่นซึ่งแก้ไขปัญหานี้ในกรณีที่เป็นประโยชน์กับทุกคน
bubbleking

5
อาจใช้ DATALENGTH แทน LEN จะดีกว่าเนื่องจาก LEN ส่งคืนขนาดของสตริงที่ถูกตัด
rodrigocl

2
DATALENGTH () / 2 ก็ยุ่งยากเช่นกันเนื่องจากขนาดถ่านที่ไม่ชัดเจน ดู stackoverflow.com/a/11080074/1094048 สำหรับวิธีที่ง่ายและแม่นยำในการรับความยาวสตริง
pkuderov

@rodrigocl ทำไมไม่ห่อLTRIMรอบสตริงดังนี้SELECT LEN(RTRIM(@string)) - LEN(REPLACE(RTRIM(@string), ',', ''))?
อเล็กซ์เบลโล

67

ส่วนขยายด่วนของคำตอบของ cmsjr ที่ทำงานกับสตริงที่มีอักขระมากกว่าตัว

CREATE FUNCTION dbo.CountOccurrencesOfString
(
    @searchString nvarchar(max),
    @searchTerm nvarchar(max)
)
RETURNS INT
AS
BEGIN
    return (LEN(@searchString)-LEN(REPLACE(@searchString,@searchTerm,'')))/LEN(@searchTerm)
END

การใช้งาน:

SELECT * FROM MyTable
where dbo.CountOccurrencesOfString(MyColumn, 'MyString') = 1

16
การปรับปรุงเล็กน้อยจะใช้ DATALENGTH () / 2 แทน LEN () LEN จะไม่สนใจช่องว่างต่อท้ายใด ๆ ดังนั้น dbo.CountOccurancesOfString( 'blah ,', ',')จะส่งคืน 2 แทน 1 และdbo.CountOccurancesOfString( 'hello world', ' ')จะล้มเหลวโดยหารด้วยศูนย์
Rory

5
ความเห็นของ Rory มีประโยชน์ ฉันพบว่าฉันสามารถแทนที่ LEN ด้วย DATALENGTH ในฟังก์ชันของ Andrew และรับผลลัพธ์ที่ต้องการ ดูเหมือนว่าการหารด้วย 2 นั้นไม่จำเป็นสำหรับวิธีการคำนวณทางคณิตศาสตร์
การ์แลนด์สมเด็จพระสันตะปาปา

@AndrewBarrett: สิ่งที่ผนวกเมื่อหลายสายมีความยาวเท่ากัน?
user2284570

2
DATALENGTH()/2ยังเป็นเรื่องยุ่งยากเนื่องจากขนาดถ่านที่ไม่ชัดเจน ดูstackoverflow.com/a/11080074/1094048สำหรับวิธีที่ง่ายและแม่นยำ
pkuderov

26

คุณสามารถเปรียบเทียบความยาวของสตริงกับสิ่งที่ลบเครื่องหมายจุลภาค:

len(value) - len(replace(value,',',''))

8

ด้วยวิธีการแก้ปัญหาของ @ Andrew คุณจะได้รับประสิทธิภาพที่ดีขึ้นมากโดยใช้ฟังก์ชั่นตารางมูลค่าที่ไม่ได้ดำเนินการและใช้ CROSS:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*  Usage:
    SELECT t.[YourColumn], c.StringCount
    FROM YourDatabase.dbo.YourTable t
        CROSS APPLY dbo.CountOccurrencesOfString('your search string',     t.[YourColumn]) c
*/
CREATE FUNCTION [dbo].[CountOccurrencesOfString]
(
    @searchTerm nvarchar(max),
    @searchString nvarchar(max)

)
RETURNS TABLE
AS
    RETURN 
    SELECT (DATALENGTH(@searchString)-DATALENGTH(REPLACE(@searchString,@searchTerm,'')))/NULLIF(DATALENGTH(@searchTerm), 0) AS StringCount

ฉันใช้ฟังก์ชั่นเดียวกันนี้ในฐานข้อมูลมรดกหลายแห่งของฉันมันช่วยได้อย่างมากกับฐานข้อมูลเก่าและการออกแบบที่ไม่เหมาะสมจำนวนมาก ประหยัดเวลาได้มากและรวดเร็วมากแม้ในชุดข้อมูลขนาดใหญ่
Caimen

6

คำตอบโดย @csmjr มีปัญหาในบางกรณี

คำตอบของเขาคือทำสิ่งนี้:

Declare @string varchar(1000)
Set @string = 'a,b,c,d'
select len(@string) - len(replace(@string, ',', ''))

วิธีนี้ใช้ได้ผลในสถานการณ์ส่วนใหญ่อย่างไรก็ตามให้ลองเรียกใช้สิ่งนี้:

DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(@string) - LEN(REPLACE(@string, ',', ''))

ด้วยเหตุผลบางอย่าง REPLACE จะกำจัดจุลภาคสุดท้าย แต่ยังมีช่องว่างอยู่ก่อนหน้า (ไม่แน่ใจว่าทำไม) ผลลัพธ์นี้จะส่งกลับค่าเป็น 5 เมื่อคุณคาดหวัง 4 นี่คือวิธีอื่นในการทำสิ่งนี้ซึ่งจะใช้ได้แม้ในสถานการณ์พิเศษนี้:

DECLARE @string VARCHAR(1000)
SET @string = 'a,b,c,d ,'
SELECT LEN(REPLACE(@string, ',', '**')) - LEN(@string)

โปรดทราบว่าคุณไม่จำเป็นต้องใช้เครื่องหมายดอกจัน การแทนที่สองอักขระใด ๆ จะทำ แนวคิดคือคุณเพิ่มความยาวของสตริงด้วยอักขระหนึ่งตัวสำหรับแต่ละตัวอย่างของอักขระที่คุณกำลังนับแล้วลบความยาวของต้นฉบับ มันเป็นวิธีที่ตรงกันข้ามกับคำตอบดั้งเดิมซึ่งไม่ได้มาพร้อมกับผลข้างเคียงที่แปลก ๆ


5
"ด้วยเหตุผลบางอย่าง REPLACE กำจัดเครื่องหมายจุลภาคสุดท้าย แต่ยังเว้นช่องว่างไว้ก่อนหน้า (ไม่แน่ใจว่าทำไม)" แทนที่จะไม่กำจัดเครื่องหมายจุลภาคสุดท้ายและช่องว่างก่อนหน้านั้นจริง ๆ แล้วมันคือฟังก์ชัน LEN ที่ละเว้นช่องว่างสีขาวซึ่งเป็นจุดสิ้นสุดของสตริงเนื่องจากช่องว่างนั้น
Imranullah Khan

2
Declare @string varchar(1000)

DECLARE @SearchString varchar(100)

Set @string = 'as as df df as as as'

SET @SearchString = 'as'

select ((len(@string) - len(replace(@string, @SearchString, ''))) -(len(@string) - 
        len(replace(@string, @SearchString, ''))) % 2)  / len(@SearchString)

สิ่งนี้จะส่งกลับจำนวนจริงน้อยลง 1 ครั้ง
The Integrator

1

คำตอบที่ยอมรับนั้นถูกต้องขยายไปใช้ 2 หรือมากกว่าตัวอักษรในสตริงย่อย:

Declare @string varchar(1000)
Set @string = 'aa,bb,cc,dd'
Set @substring = 'aa'
select (len(@string) - len(replace(@string, @substring, '')))/len(@substring)

1

หากเรารู้ว่ามีข้อ จำกัด เกี่ยวกับ LEN และพื้นที่ทำไมเราไม่สามารถแทนที่พื้นที่ก่อน? ถ้าอย่างนั้นเราก็รู้ว่าไม่มีที่ว่างที่จะทำให้สับสน LEN

len(replace(@string, ' ', '-')) - len(replace(replace(@string, ' ', '-'), ',', ''))


0

Darrel Lee ฉันคิดว่ามีคำตอบที่ดีงาม แทนที่CHARINDEX()ด้วยPATINDEX()และคุณสามารถทำบางอย่างที่อ่อนแอregexค้นหาที่ตามสตริงได้เช่นกัน ...

เช่นบอกว่าคุณใช้สิ่งนี้เพื่อ@pattern:

set @pattern='%[-.|!,'+char(9)+']%'

ทำไมคุณถึงอยากทำบางสิ่งที่คลั่งไคล้เช่นนี้

สมมติว่าคุณกำลังโหลดสตริงข้อความแบบมีตัวคั่นลงในตารางการแสดงซึ่งฟิลด์ที่เก็บข้อมูลนั้นเป็นอะไรบางอย่างเช่น varchar (8000) หรือ nvarchar (สูงสุด) ...

บางครั้งการทำ ELT (Extract-Load-Transform) ง่ายกว่า / เร็วกว่าด้วยข้อมูลมากกว่า ETL (Extract-Transform-Load) และวิธีหนึ่งในการทำเช่นนี้คือการโหลดเร็กคอร์ดที่คั่นตามที่อยู่ในตาราง staging โดยเฉพาะถ้า คุณอาจต้องการวิธีที่ง่ายกว่าในการดูบันทึกพิเศษแทนที่จะจัดการกับพวกเขาเป็นส่วนหนึ่งของแพ็คเกจ SSIS ... แต่นั่นเป็นสงครามศักดิ์สิทธิ์สำหรับกระทู้ต่าง ๆ


0

ต่อไปนี้ควรใช้เคล็ดลับสำหรับการค้นหาทั้งอักขระเดี่ยวและหลายอักขระ:

CREATE FUNCTION dbo.CountOccurrences
(
   @SearchString VARCHAR(1000),
   @SearchFor    VARCHAR(1000)
)
RETURNS TABLE
AS
   RETURN (
             SELECT COUNT(*) AS Occurrences
             FROM   (
                       SELECT ROW_NUMBER() OVER (ORDER BY O.object_id) AS n
                       FROM   sys.objects AS O
                    ) AS N
                    JOIN (
                            VALUES (@SearchString)
                         ) AS S (SearchString)
                         ON
                         SUBSTRING(S.SearchString, N.n, LEN(@SearchFor)) = @SearchFor
          );
GO

---------------------------------------------------------------------------------------
-- Test the function for single and multiple character searches
---------------------------------------------------------------------------------------
DECLARE @SearchForComma      VARCHAR(10) = ',',
        @SearchForCharacters VARCHAR(10) = 'de';

DECLARE @TestTable TABLE
(
   TestData VARCHAR(30) NOT NULL
);

INSERT INTO @TestTable
     (
        TestData
     )
VALUES
     ('a,b,c,de,de ,d e'),
     ('abc,de,hijk,,'),
     (',,a,b,cde,,');

SELECT TT.TestData,
       CO.Occurrences AS CommaOccurrences,
       CO2.Occurrences AS CharacterOccurrences
FROM   @TestTable AS TT
       OUTER APPLY dbo.CountOccurrences(TT.TestData, @SearchForComma) AS CO
       OUTER APPLY dbo.CountOccurrences(TT.TestData, @SearchForCharacters) AS CO2;

ฟังก์ชั่นสามารถทำให้เข้าใจง่ายขึ้นเล็กน้อยโดยใช้ตารางตัวเลข (dbo.Nums):

   RETURN (
             SELECT COUNT(*) AS Occurrences
             FROM   dbo.Nums AS N
                    JOIN (
                            VALUES (@SearchString)
                         ) AS S (SearchString)
                         ON
                         SUBSTRING(S.SearchString, N.n, LEN(@SearchFor)) = @SearchFor
          );

0

ใช้รหัสนี้มันทำงานได้อย่างสมบูรณ์ ฉันได้สร้างฟังก์ชั่น sql ที่ยอมรับพารามิเตอร์สองตัวพารามิเตอร์แรกคือสตริงยาวที่เราต้องการค้นหาและสามารถยอมรับความยาวสตริงได้สูงสุด 1500 ตัวอักษร (แน่นอนว่าคุณสามารถขยายหรือเปลี่ยนเป็นประเภทข้อมูลข้อความได้ ) และพารามิเตอร์ที่สองคือสตริงย่อยที่เราต้องการคำนวณจำนวนการปรากฎ (ความยาวของมันขึ้นอยู่กับ 200 ตัวอักษรแน่นอนคุณสามารถเปลี่ยนมันเป็นสิ่งที่คุณต้องการ) และเอาท์พุทเป็นจำนวนเต็มแทนจำนวนความถี่ของ ..... สนุกกับมัน


CREATE FUNCTION [dbo].[GetSubstringCount]
(
  @InputString nvarchar(1500),
  @SubString NVARCHAR(200)
)
RETURNS int
AS
BEGIN 
        declare @K int , @StrLen int , @Count int , @SubStrLen int 
        set @SubStrLen = (select len(@SubString))
        set @Count = 0
        Set @k = 1
        set @StrLen =(select len(@InputString))
    While @K <= @StrLen
        Begin
            if ((select substring(@InputString, @K, @SubStrLen)) = @SubString)
                begin
                    if ((select CHARINDEX(@SubString ,@InputString)) > 0)
                        begin
                        set @Count = @Count +1
                        end
                end
                                Set @K=@k+1
        end
        return @Count
end

0

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

CREATE FUNCTION [dbo].[CountOccurrency]
(
@Input nvarchar(max),
@Search nvarchar(max)
)
RETURNS int AS
BEGIN
    declare @SearhLength as int = len('-' + @Search + '-') -2;
    declare @conteinerIndex as int = 255;
    declare @conteiner as char(1) = char(@conteinerIndex);
    WHILE ((CHARINDEX(@conteiner, @Search)>0) and (@conteinerIndex>0))
    BEGIN
        set @conteinerIndex = @conteinerIndex-1;
        set @conteiner = char(@conteinerIndex);
    END;
    set @Input = @conteiner + @Input + @conteiner
    RETURN (len(@Input) - len(replace(@Input, @Search, ''))) / @SearhLength
END 

การใช้

select dbo.CountOccurrency('a,b,c,d ,', ',')

0
Declare @MainStr nvarchar(200)
Declare @SubStr nvarchar(10)
Set @MainStr = 'nikhildfdfdfuzxsznikhilweszxnikhil'
Set @SubStr = 'nikhil'
Select (Len(@MainStr) - Len(REPLACE(@MainStr,@SubStr,'')))/Len(@SubStr)

0

ใน SQL 2017 หรือสูงกว่าคุณสามารถใช้สิ่งนี้:

declare @hits int = 0
set @hits = (select value from STRING_SPLIT('F609,4DFA,8499',','));
select count(@hits)

0

รหัส T-SQL นี้จะค้นหาและพิมพ์ pattern @p ทั้งหมดในประโยค @s คุณสามารถทำการประมวลผลใด ๆ ในประโยคหลังจากนั้น

declare @old_hit int = 0
declare @hit int = 0
declare @i int = 0
declare @s varchar(max)='alibcalirezaalivisualization'
declare @p varchar(max)='ali'
 while @i<len(@s)
  begin
   set @hit=charindex(@p,@s,@i)
   if @hit>@old_hit 
    begin
    set @old_hit =@hit
    set @i=@hit+1
    print @hit
   end
  else
    break
 end

ผลลัพธ์คือ: 1 6 13 20



-1

คุณสามารถใช้ขั้นตอนการจัดเก็บต่อไปนี้เพื่อดึงค่า

IF  EXISTS (SELECT * FROM sys.objects 
WHERE object_id = OBJECT_ID(N'[dbo].[sp_parsedata]') AND type in (N'P', N'PC'))
    DROP PROCEDURE [dbo].[sp_parsedata]
GO
create procedure sp_parsedata
(@cid integer,@st varchar(1000))
as
  declare @coid integer
  declare @c integer
  declare @c1 integer
  select @c1=len(@st) - len(replace(@st, ',', ''))
  set @c=0
  delete from table1 where complainid=@cid;
  while (@c<=@c1)
    begin
      if (@c<@c1) 
        begin
          select @coid=cast(replace(left(@st,CHARINDEX(',',@st,1)),',','') as integer)
          select @st=SUBSTRING(@st,CHARINDEX(',',@st,1)+1,LEN(@st))
        end
      else
        begin
          select @coid=cast(@st as integer)
        end
      insert into table1(complainid,courtid) values(@cid,@coid)
      set @c=@c+1
    end

บรรทัด 4 ของโพรซีเดอร์ที่เก็บนี้ตั้งค่า@c1เป็นคำตอบที่เขาต้องการ ส่วนที่เหลือของการใช้งานคืออะไรเมื่อพิจารณาว่าจำเป็นต้องมีตารางที่มีอยู่ก่อนที่จะเรียกtable1ใช้งานมี delimeter แบบกำหนดรหัสแบบยากและไม่สามารถใช้แบบอินไลน์เหมือนคำตอบที่ยอมรับได้เมื่อสองเดือนก่อน
Nick.McDermaid

-1

การทดสอบการเปลี่ยน / การทดสอบน่ารัก แต่อาจไม่มีประสิทธิภาพ (โดยเฉพาะในแง่ของหน่วยความจำ) ฟังก์ชั่นที่เรียบง่ายพร้อมลูปจะทำงานได้

CREATE FUNCTION [dbo].[fn_Occurences] 
(
    @pattern varchar(255),
    @expression varchar(max)
)
RETURNS int
AS
BEGIN

    DECLARE @Result int = 0;

    DECLARE @index BigInt = 0
    DECLARE @patLen int = len(@pattern)

    SET @index = CHARINDEX(@pattern, @expression, @index)
    While @index > 0
    BEGIN
        SET @Result = @Result + 1;
        SET @index = CHARINDEX(@pattern, @expression, @index + @patLen)
    END

    RETURN @Result

END

บนโต๊ะขนาดเห็นใด ๆ โดยใช้ฟังก์ชั่นขั้นตอนคือไกลไม่มีประสิทธิภาพมากขึ้น
Nick.McDermaid

จุดดี. Len โทรภายในตัวเครื่องเร็วกว่าฟังก์ชั่นที่กำหนดหรือไม่?
Darrel Lee

มีการบันทึกจำนวนมากใช่ แม้ว่าจะแน่ใจว่าคุณจะต้องทดสอบในชุดระเบียนขนาดใหญ่ที่มีสตริงขนาดใหญ่ ไม่เคยเขียนอะไรขั้นตอนใน SQL ถ้าคุณสามารถหลีกเลี่ยงได้ (เช่นลูป)
Nick.McDermaid

-3

บางทีคุณไม่ควรเก็บข้อมูลด้วยวิธีนี้ มันเป็นวิธีปฏิบัติที่ไม่ดีที่จะเก็บรายการที่คั่นด้วยเครื่องหมายจุลภาคในเขตข้อมูล ไอทีไม่มีประสิทธิภาพในการสืบค้น นี่ควรเป็นตารางที่เกี่ยวข้อง


+1 เมื่อคิดถึงสิ่งนั้น มันเป็นสิ่งที่ฉันมักจะเริ่มต้นด้วยเมื่อมีคนใช้ข้อมูลที่คั่นด้วยเครื่องหมายจุลภาคในเขตข้อมูล
Guffa

6
ส่วนหนึ่งของวัตถุประสงค์ของคำถามนี้คือการใช้ข้อมูลที่มีอยู่เช่นนั้นและแยกออกจากกันอย่างเหมาะสม
Orion Adrian

7
พวกเราบางคนได้รับฐานข้อมูลเก่าที่ทำและเราไม่สามารถทำอะไรกับมันได้
eddieroger

@Mulmoth แน่นอนมันเป็นคำตอบ คุณแก้ไขปัญหาไม่ใช่อาการ ปัญหาคือกับการออกแบบฐานข้อมูล
HLGEM

1
@ HLGEM คำถามอาจชี้ไปที่ปัญหา แต่สามารถเข้าใจได้ทั่วไปมากขึ้น คำถามนั้นถูกต้องตามกฎหมายอย่างสมบูรณ์สำหรับฐานข้อมูลปกติดีมาก
Zeemee
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.