Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2005 Forums
 Transact-SQL (2005)
 Search engine...

Author  Topic 

steve_c
Yak Posting Veteran

54 Posts

Posted - 2007-11-14 : 07:02:30
Hi all - I've been drafted in to improve an app which is going fine apart from this Stored Proc - I'm not an expert at SQL by any means and looking at this query really makes my head spin...

It's a product search query. It *kind* of works... There are 2 fields in the product table. Product name and Product code. You should be able to search on either using the same string. For example

Product code Product name
son-dbc-32 Sony Digital Betacam 32
son-bct-32 Sony Betacam SP 32

If you search for son-dbc-32 it brings up the correct product. However, if I search for 'Sony Digital Betacam 32' then both products come up. This is the query: (it also has a ranking function)

-- Results are returned in order of relevance



declare @i1 int;
declare @i2 int;

declare @Word varchar(100);
declare @Words table (Word varchar(200) not null);
declare @WordCount as integer;

set nocount on
-- Parse the SearchString to extract all words:
if (@MatchType != 2)
begin
set @SearchString = ' ' + @SearchString + ' ';
set @i1 = 1;
while (@i1 != 0)
begin

set @i2=charindex(' ', @SearchString, @i1+1)
if (@i2 != 0)
begin
set @Word = rtrim(ltrim(substring(@SearchString, @i1+1, @i2-@i1)))
if @Word != '' insert into @Words select @Word
end

set @i1 = @i2
end
end
else
insert into @Words select ltrim(rtrim(@SearchString))

-- Get the total # of words:
set @WordCount = (select count(*) from @Words)

-- Return Results in order of relevance:
select

a.MatchPct, T.*,
productimages.isprimary,
T.productname + ' (' + T.productcode + ') ' AS test,
ISNULL('uploads/products/p' + CONVERT (varchar, productimages.productid) + '/' + productimages.imagename, 'images/noimage.gif') AS imagename

from

products T
LEFT OUTER JOIN productimages ON T.productid = productimages.productid
inner join

(
select
productcode, productname, productid, Count(*) * 1.0 / @WordCount as MatchPct
from
products T
inner join

@Words W on ' ' + T.productname + ' ' + T.productcode + ' ' like '%[^a-z]' + Word + '[^a-z]%'

group by

productname, productid , productcode

) a on T.productid = a.productid

where

MatchPct = 1 and productimages.isprimary = 1 OR productimages.isprimary IS NULL
or @MatchType <>1 and productimages.isprimary = 1 OR productimages.isprimary IS NULL

order by

MatchPct


Really helpful for any pointers.

Thanks,

Stephen.

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2007-11-14 : 07:29:42
Is ProductCode primary key?



E 12°55'05.25"
N 56°04'39.16"
Go to Top of Page

steve_c
Yak Posting Veteran

54 Posts

Posted - 2007-11-14 : 07:35:33
productid is a primary key. Product code is an index (no duplicates)
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2007-11-14 : 08:09:24
Something like this?
DECLARE	@Products TABLE (ProductCode VARCHAR(20), ProductName VARCHAR(80))

INSERT @Products
SELECT 'son-dbc-32', 'Sony Digital Betacam 32' UNION ALL
SELECT 'son-bct-32', 'Sony Betacam SP 32'

DECLARE @SearchString VARCHAR(80)

SET @SearchString = 'son dbc 32'

;WITH Yak (ProductCode, ProductName, Hit)
AS (
SELECT p.ProductCode,
p.ProductName,
MAX(CASE WHEN pSearch.Data IN (pCode.Data, pName.Data) THEN 1 ELSE 0 END)
FROM @Products AS p
CROSS APPLY dbo.fnParseList('-', REPLACE(p.ProductCode, ' ', '-')) AS pCode
CROSS APPLY dbo.fnParseList('-', REPLACE(p.ProductName, ' ', '-')) AS pName
CROSS APPLY dbo.fnParseList('-', REPLACE(@SearchString, ' ', '-')) AS pSearch
GROUP BY p.ProductCode,
p.ProductName,
pCode.Data,
pName.Data
), Search (ProductCode, ProductName, Pct)
AS (
SELECT ProductCode,
ProductName,
CONVERT(DECIMAL(5, 2), 100.0 * SUM(Hit) / COUNT(*)) AS Pct
FROM Yak
GROUP BY ProductCode,
ProductName
)

SELECT s.ProductCode,
s.ProductName,
s.Pct
FROM Search AS s
WHERE s.Pct > 66.66
ORDER BY Pct DESC,
s.ProductCode,
s.ProductName



E 12°55'05.25"
N 56°04'39.16"
Go to Top of Page

steve_c
Yak Posting Veteran

54 Posts

Posted - 2007-11-14 : 08:18:24
Hi thanks :)

Am getting the error:

Invalid object name 'dbo.fnParseList'.
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2007-11-14 : 08:42:13
Find second version of dbo.fnParseList here
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=76033


E 12°55'05.25"
N 56°04'39.16"
Go to Top of Page

steve_c
Yak Posting Veteran

54 Posts

Posted - 2007-11-14 : 08:48:30
Hi - I've added this function (appears under Scalar-valued-functions) but am still getting the same error :(

Msg 208, Level 16, State 1, Line 7
Invalid object name 'dbo.fnParseList'.
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2007-11-14 : 08:55:18
Did you run the complete code (including the line CREATE FUNCTION...) in the same databas as where the stored procedure us running?



E 12°55'05.25"
N 56°04'39.16"
Go to Top of Page

steve_c
Yak Posting Veteran

54 Posts

Posted - 2007-11-14 : 09:00:20
Yup - I ran this:

CREATE FUNCTION dbo.fnParseString
(
@Section SMALLINT,
@Delimiter CHAR,
@Text VARCHAR(8000)
)
RETURNS VARCHAR(8000)
AS

BEGIN
DECLARE @NextPos SMALLINT,
@LastPos SMALLINT,
@Found SMALLINT

SELECT @NextPos = 0,
@Found = 0

IF @Section > 0
SELECT @Text = REVERSE(@Text)

WHILE @NextPos <= DATALENGTH(@Text) AND @Found < ABS(@Section)
SELECT @LastPos = @NextPos,
@NextPos = CASE
WHEN CHARINDEX(@Delimiter, @Text, @LastPos + 1) = 0 THEN DATALENGTH(@Text) + 1
ELSE CHARINDEX(@Delimiter, @Text, @LastPos + 1)
END,
@Found = @Found + 1

IF @Found <> ABS(@Section) OR @Found = 0 OR @Section = 0
SELECT @Text = NULL
ELSE
SELECT @Text = SUBSTRING(@Text, @LastPos + 1, @NextPos - @LastPos - 1)

RETURN CASE WHEN @Section < 0 THEN @Text ELSE REVERSE(@Text) END
END


then this:

DECLARE	@Products TABLE (ProductCode VARCHAR(20), ProductName VARCHAR(80))

INSERT @Products
SELECT 'son-dbc-32', 'Sony Digital Betacam 32' UNION ALL
SELECT 'son-bct-32', 'Sony Betacam SP 32'

DECLARE @SearchString VARCHAR(80)

SET @SearchString = 'son dbc 32'

;WITH Yak (ProductCode, ProductName, Hit)
AS (
SELECT p.ProductCode,
p.ProductName,
MAX(CASE WHEN pSearch.Data IN (pCode.Data, pName.Data) THEN 1 ELSE 0 END)
FROM @Products AS p
CROSS APPLY dbo.fnParseList('-', REPLACE(p.ProductCode, ' ', '-')) AS pCode
CROSS APPLY dbo.fnParseList('-', REPLACE(p.ProductName, ' ', '-')) AS pName
CROSS APPLY dbo.fnParseList('-', REPLACE(@SearchString, ' ', '-')) AS pSearch
GROUP BY p.ProductCode,
p.ProductName,
pCode.Data,
pName.Data
), Search (ProductCode, ProductName, Pct)
AS (
SELECT ProductCode,
ProductName,
CONVERT(DECIMAL(5, 2), 100.0 * SUM(Hit) / COUNT(*)) AS Pct
FROM Yak
GROUP BY ProductCode,
ProductName
)

SELECT s.ProductCode,
s.ProductName,
s.Pct
FROM Search AS s
WHERE s.Pct > 66.66
ORDER BY Pct DESC,
s.ProductCode,
s.ProductName


Got this:



(2 row(s) affected)
Msg 208, Level 16, State 1, Line 11
Invalid object name 'dbo.fnParseList'.


Stephen
Go to Top of Page

tm
Posting Yak Master

160 Posts

Posted - 2007-11-14 : 10:07:36
Rename the function to dbo.fnParseList and rerun the function. OR re-reference dbo.fnParseString in your stored procedure instead of dbo.fnParseList.
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2007-11-14 : 10:11:49
You can't !

dbo.fnParseString returns a single VARCHAR with the item you want.
dbo.fnParseList returns a full RESULTSET with all items.

If I wrote dbo.fnParseList, you should run dbo.fnParseList, not dbo.fnParseString.

Look for the second version of dbo.fnParseList, as I wrote before.



E 12°55'05.25"
N 56°04'39.16"
Go to Top of Page

steve_c
Yak Posting Veteran

54 Posts

Posted - 2007-11-14 : 10:17:43
Peso - is it this one?:

CREATE FUNCTION dbo.fnParseList
(
@Delimiter CHAR,
@Text TEXT
)
RETURNS @Result TABLE (RowID SMALLINT IDENTITY(1, 1) PRIMARY KEY, Data VARCHAR(8000))
AS

BEGIN
DECLARE @NextPos INT,
@LastPos INT

SELECT @NextPos = CHARINDEX(@Delimiter, @Text, 1),
@LastPos = 0

WHILE @NextPos > 0
BEGIN
INSERT @Result
(
Data
)
SELECT SUBSTRING(@Text, @LastPos + 1, @NextPos - @LastPos - 1)

SELECT @LastPos = @NextPos,
@NextPos = CHARINDEX(@Delimiter, @Text, @NextPos + 1)
END

IF SCOPE_IDENTITY() > 0
INSERT @Result
(
Data
)
SELECT SUBSTRING(@Text, @LastPos + 1, DATALENGTH(@Text) - @LastPos)

RETURN
END
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2007-11-14 : 10:59:06
Yes.



E 12°55'05.25"
N 56°04'39.16"
Go to Top of Page

steve_c
Yak Posting Veteran

54 Posts

Posted - 2007-11-14 : 14:34:18
Hi Peso - I've enabled full text indexing on this table now - and have the following query:

SELECT productcode, productname
FROM products
WHERE CONTAINS(productname, '" sony digital betacam bct-d32 "')

This works perfectly - is there any way I can change it so that it looks for the search param in either productcode or productname?
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2007-11-14 : 14:51:15
SELECT productcode, productname
FROM products
WHERE CONTAINS(productname, '" sony digital betacam bct-d32 "')
or CONTAINS(productcode, '" sony digital betacam bct-d32 "')



E 12°55'05.25"
N 56°04'39.16"
Go to Top of Page
   

- Advertisement -