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.
| 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 exampleProduct code Product nameson-dbc-32 Sony Digital Betacam 32son-bct-32 Sony Betacam SP 32If 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 endelse 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 imagenamefrom products TLEFT OUTER JOIN productimages ON T.productid = productimages.productidinner 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.productidwhere MatchPct = 1 and productimages.isprimary = 1 OR productimages.isprimary IS NULLor @MatchType <>1 and productimages.isprimary = 1 OR productimages.isprimary IS NULLorder by MatchPctReally 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" |
 |
|
|
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) |
 |
|
|
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 @ProductsSELECT 'son-dbc-32', 'Sony Digital Betacam 32' UNION ALLSELECT '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.PctFROM Search AS sWHERE s.Pct > 66.66ORDER BY Pct DESC, s.ProductCode, s.ProductName E 12°55'05.25"N 56°04'39.16" |
 |
|
|
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'. |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
|
|
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 7Invalid object name 'dbo.fnParseList'. |
 |
|
|
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" |
 |
|
|
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)ASBEGIN 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) ENDEND then this:DECLARE @Products TABLE (ProductCode VARCHAR(20), ProductName VARCHAR(80))INSERT @ProductsSELECT 'son-dbc-32', 'Sony Digital Betacam 32' UNION ALLSELECT '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.PctFROM Search AS sWHERE s.Pct > 66.66ORDER BY Pct DESC, s.ProductCode, s.ProductNameGot this:(2 row(s) affected)Msg 208, Level 16, State 1, Line 11Invalid object name 'dbo.fnParseList'. Stephen |
 |
|
|
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. |
 |
|
|
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" |
 |
|
|
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))ASBEGIN 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) RETURNEND |
 |
|
|
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" |
 |
|
|
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, productnameFROM productsWHERE 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? |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2007-11-14 : 14:51:15
|
SELECT productcode, productnameFROM productsWHERE 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" |
 |
|
|
|
|
|
|
|