无效长度参数 - 包含过程中的字符串搜索

时间:2014-04-14 01:14:27

标签: sql-server tsql sql-server-2012

我正在将示例查询转换为字符串搜索过程。

此示例将搜索这些表中的2个表和3列。

正在搜索的行可以包含短内容(只有几个单词),非常大的字符串(数百个单词)。

有些包含html,所以我在实际搜索之前删除了html。

在我的测试中,我只是对搜索进行了硬编码,然后编写了我收到错误的程序。

Invalid length parameter passed to the LEFT or SUBSTRING function.

哪一行来自这一行

,CHARINDEX(@searchString, TextColumn) as SearchPos

我试图解决这个问题

,ISNULL(NULLIF(CHARINDEX(@searchString, Sample2TextColumn2) - 1, -1), LEN(@searchString)) AS SearchPos

然后转换为使用参数搜索,计数也会被破坏。

我无法弄清楚是否因为参数是nvarchar(300),并且打破了计数,但它们返回0。

最后,我要返回的是找到搜索的字符串的开头,搜索字符串的计数或总次数,以及找到字符串的实际位置。

这是程序 - 我留下了硬编码的值,因此它会执行,但如果我在问题发生时用@searchString替换

ALTER Procedure [dbo].[procSearch]
(
    @searchString nvarchar(300) = null
)
AS
BEGIN TRY
SET NOCOUNT ON
SET @searchString = UPPER('"' +@searchString+ '*"');
DECLARE
   @searchData TABLE(                 
                  Id int NOT NULL,                              
                  Target nvarchar(max) NOT NULL,
                  BeginningOfString nvarchar(max) NOT NULL,
                  SearchPos int not null,
                  Counts int not null,
                      Rank int NOT NULL , 
                      Section nvarchar(100)NOT NULL,
                  NewString nvarchar(255) NOT NULL
                    );

;with CTE as 
    (SELECT       
      a.Sample2Id
     ,dbo.StripHTML(Sample2TextColumn2) as Target
     ,CASE When LEN(Sample2TextColumn2) <= 60
             Then Sample2TextColumn2 
             ELSE LEFT(Sample2TextColumn2, 60) + '...'
          END As BeginningOfString
     ,CHARINDEX('first', Sample2TextColumn2) as SearchPos
     --,ISNULL(NULLIF(CHARINDEX(@searchString, Sample2TextColumn2) - 1, -1), LEN(@searchString)) AS SearchPos
     ,(LEN(Sample2TextColumn2) - LEN(REPLACE(Sample2TextColumn2,LTRIM(RTRIM('first')), '')))/LEN(LTRIM(RTRIM('first'))) as Counts
     ,1 AS Rank 
     ,'Section 1' AS Section 
  FROM
       SampleTable2 a
        WHERE CONTAINS((Sample2Text, Sample2TextColumn2) , @searchString)
     )
    ,CTE2 as 
    (SELECT  
      b.Id 
     ,dbo.StripHTML(TextColumn) as Target 
     ,CASE When LEN(TextColumn) <= 60
             Then TextColumn 
             ELSE LEFT(TextColumn, 60) + '...'
          END As BeginningOfString
     ,CHARINDEX('first', TextColumn) as SearchPos
     ,(LEN(TextColumn) - LEN(REPLACE(TextColumn, LTRIM(RTRIM('first')), '')))/LEN(LTRIM(RTRIM('first'))) as Counts
     ,2 AS Rank 
     ,'Section 2' AS Section
  FROM
       SampleTable b
        WHERE CONTAINS(TextColumn , @searchString)
    )
    INSERT INTO @searchData
        select *, case when SearchPos > 60 then substring(Target, SearchPos - 60, 60) + @searchString + 
substring(Target, SearchPos + len(@searchString), 60) else substring(Target, 1, SearchPos-1) + @searchString + substring(Target, SearchPos + len(@searchString), 60)
 end as NewString
from cte 

union all
        select *, case when SearchPos > 60 then substring(Target, SearchPos - 60, 60) + @searchString + 
substring(Target, SearchPos + len(@searchString), 60) else substring(Target, 1, SearchPos-1) + @searchString + substring(Target, SearchPos + len(@searchString), 60)
 end as NewString
from cte2 

Select  ROW_NUMBER() OVER(ORDER BY GetDate() DESC) AS RowId
       , x.* 
       From @searchData x
       order by x.Counts desc
END TRY
BEGIN CATCH
    SELECT ERROR_NUMBER() AS ErrorNumber
          ,ERROR_SEVERITY() AS ErrorSeverity
          ,ERROR_STATE() AS ErrorState
          ,ERROR_PROCEDURE() AS ErrorProcedure
          ,ERROR_LINE() AS ErrorLine
          ,ERROR_MESSAGE() AS ErrorMessage;
END CATCH

用它来剥离html

ALTER function [dbo].[StripHTML]( @text varchar(max) ) returns varchar(max) as
begin
    declare @textXML xml
    declare @result varchar(max)
    set @textXML = REPLACE( @text, '&', '' );
    with doc(contents) as
    (
        select chunks.chunk.query('.') from @textXML.nodes('/') as chunks(chunk)
    )
    select @result = contents.value('.', 'varchar(max)') from doc
    return @result
end

样本数据

CREATE TABLE [dbo].[SampleTable](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [TextColumn] [nvarchar](1000) NOT NULL,
 CONSTRAINT [PK_SampleTable] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
/****** Object:  Table [dbo].[SampleTable2]    Script Date: 4/13/2014 7:59:48 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[SampleTable2](
    [Sample2Id] [int] IDENTITY(1,1) NOT NULL,
    [Sample2Text] [nvarchar](max) NOT NULL,
    [Sample2TextColumn2] [nvarchar](max) NOT NULL,
 CONSTRAINT [PK_SampleTable2] PRIMARY KEY CLUSTERED 
(
    [Sample2Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
SET IDENTITY_INSERT [dbo].[SampleTable] ON 

INSERT [dbo].[SampleTable] ([Id], [TextColumn]) VALUES (1, N'This is the first Test string')
INSERT [dbo].[SampleTable] ([Id], [TextColumn]) VALUES (2, N'This is the second one')
INSERT [dbo].[SampleTable] ([Id], [TextColumn]) VALUES (3, N'This is the first really long string of text that should be included in the result set.')
INSERT [dbo].[SampleTable] ([Id], [TextColumn]) VALUES (4, N'This is the second long string that will not be returned in the result set')
INSERT [dbo].[SampleTable] ([Id], [TextColumn]) VALUES (5, N'This is a really really long result set that should also be first included in the result set.  It has a seperate sentence in it as well.')
INSERT [dbo].[SampleTable] ([Id], [TextColumn]) VALUES (6, N'Now this is a really really first one.   It is so long that I have forgotten how long it really was.   Well it could be really long but first lets do this.  ')
INSERT [dbo].[SampleTable] ([Id], [TextColumn]) VALUES (7, N'<p>Hello, </p>

<p>This is a really long first string with html inside</p>

<p>This is another line </p>

<p>This is a first line</p>

<p>This one is another line that is really really long, first</p>

<p>First, !=First</p>

<p>I want to see if it will find all the first lines.</p>

<p>THIS IS A CAPITAL LETTER LINE, THE LINE WILL SKIP A LINE</p>

<p>HERE IS THE FIRST LINE </p>')
INSERT [dbo].[SampleTable] ([Id], [TextColumn]) VALUES (8, N'<p>This markup is copies from Microsoft Word Firstly</p>

<p>This Has some tabs.</p>

<p>First I want to know it it can count them.</p>

<p><b>The counts should return 4 firsts first</b></p>

<p>Well Maybe five first.</p>')
SET IDENTITY_INSERT [dbo].[SampleTable] OFF
SET IDENTITY_INSERT [dbo].[SampleTable2] ON 

INSERT [dbo].[SampleTable2] ([Sample2Id], [Sample2Text], [Sample2TextColumn2]) VALUES (1, N'This is the first text string in this table.

First it will find it all.  Maybe it will find a lot more, but I dunno, I am just typing firstly.

This should be found in the wildcard FIRst.  Maybe.  I am not sure.

This is one of the targets (First).

Maybe it will find ‘first’

First!!!!
', N'<p>This is the first text string in this table.</p>

<p>First it will find it all. Maybe it will find a lot more, but I dunno, I am just typing firstly.</p>

<p>This should be found in the wildcard FIRst. Maybe. I am not sure.</p>

<p>This is one of the targets (First).</p>

<p>Maybe it will find ‘first’</p>

<p>First!!!!</p>

<p><br />
</p>

<p><br />
</p>

<p>I just copied this text inside and converted it to html for the column</p>

<p><br />
</p>

<p>It might work, but im still not sure First -- first</p>
')
SET IDENTITY_INSERT [dbo].[SampleTable2] OFF

1 个答案:

答案 0 :(得分:2)

错误是由于表达式......

substring(Target, 1, SearchPos-1)

...在select两个union all语句中,INSERT INTO @searchData作为procSearch的一部分,直到SearchPos的实施结束。

这是因为0始终-1代码&amp;您提供的示例数据以及传递给length的{​​{1}} SUBSTRING个参数可以预测会产生您注意到的错误 - 正如MSDN's SUBSTRING documentation所解释的那样:

  

如果 length 为负数,则会生成错误并终止该语句。

要查看我对SearchPos值的含义,请暂时将所有内容从INSERT INTO @searchData注释到存储过程的结尾,并将其替换为select * from CTE, CTE2

此外,以下简单示例(从我推荐的临时select * from CTE, CTE2的结果中获取的值)从您提出的所有问题中提炼出问题......

declare @searchPos int = 0;
declare @target nvarchar(max) = 'This is the first text string in this table.First it will find it all. Maybe it will find a lot more, but I dunno, I am just typing firstly.This should be found in the wildcard FIRst. Maybe. I am not sure.This is one of the targets (First).Maybe it will find ‘first’First!!!!I just copied this text inside and converted it to html for the columnIt might work, but im still not sure First -- first';

select SUBSTRING(@target, 1, @searchPos - 1)

...并重现错误:

Msg 537, Level 16, State 5, Line 12
Invalid length parameter passed to the LEFT or SUBSTRING function.

请注意,我在您提供的'first'实施中将@searchString的所有procSearch替换为{{1}},并且能够重现错误。