我正在将示例查询转换为字符串搜索过程。
此示例将搜索这些表中的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
答案 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}},并且能够重现错误。