SQL Server Full Text Search Very Slow - sql-server

I have a stored procedure that searches a table which has about 200000+ rows with full text FREETEXT.
Here is the basics of it:
declare #searchKey varchar(150)
if #searchKey Is Null OR LEN(#searchKey)=0
Set #searchKey='""';
Set #searchKey='car';
declare #perPage int
Set #perPage=40
declare #pageNo int
Set #pageNo=1
declare #startIndex int,#endIndex int;
Set #startIndex=#perPage*#pageNo-#perPage+1;
Set #endIndex=#perPage*#pageNo;
Select totalItems
--i pull other colums as well
from (
Select Row_Number() over(order by CreateDate DESC) As rowNumber
,COUNT(*) OVER() as totalItems
--other columns are pulled as well
from MyTable P
Where
#searchKey='""'
OR FreeText((P.Title,P.Description),#searchKey)
) tempData
--where rowNumber>=#startIndex AND rowNumber<=#endIndex
where
rowNumber>=CASE WHEN #startIndex>0 AND #endIndex>0 THEN #startIndex ELSE rowNumber END
AND rowNumber<=CASE WHEN #startIndex>0 AND #endIndex>0 THEN #endIndex ELSE rowNumber END
order by rowNumber
The problem is its running slower then i would like it. Its taking about 3 seconds to load the page. Same page was loading in less then 1 sec when i was using like operator.

In my experience, full text index functions do not work well in a clause that contains an "OR" operator. I have had to get the same behavior by adjusting my query to use a UNION. Try this and see if you can get better performance.
declare #searchKey varchar(150)
if #searchKey Is Null OR LEN(#searchKey)=0
Set #searchKey='""';
Set #searchKey='car';
declare #perPage int
Set #perPage=40
declare #pageNo int
Set #pageNo=1
declare #startIndex int,#endIndex int;
Set #startIndex=#perPage*#pageNo-#perPage+1;
Set #endIndex=#perPage*#pageNo;
Select totalItems
--i pull other colums as well
from (
Select Row_Number() over(order by CreateDate DESC) As rowNumber
,COUNT(*) OVER() as totalItems
--other columns are pulled as well
from
(
select * from
MyTable A
Where
#searchKey='""'
UNION
select * from MyTable B
where FreeText((B.Title,B.Description),#searchKey)
) as innerTable
) tempData
--where rowNumber>=#startIndex AND rowNumber<=#endIndex
where
rowNumber>=CASE WHEN #startIndex>0 AND #endIndex>0 THEN #startIndex ELSE rowNumber END
AND rowNumber<=CASE WHEN #startIndex>0 AND #endIndex>0 THEN #endIndex ELSE rowNumber END
order by rowNumber

Related

PyQT: SQL Query runs, but doesn't display output in QTableView for a larger query

I am creating an application in PyQt where in on clicking a button, the query runs and returns the output in QTableView. This is the code with which I am trying to achieve this:
self.table2 = QTableView()
projectModel1.setQuery("select * from xyz")
self.table2.setModel(projectModel1)
My problem is that this seems to work perfectly for a relatively small query, but when I replace the query with a larger query which creates a table in my database, it returns no output (let's call this large query as 'Query A'). But when I check my database, I see that the table was created, which means 'Query A' ran but didn't return the result. And also doesn't allow any other query after 'Query A' to run.
I have tried running the query in SQL and the 'Query A' works fine, so that is not the issue.
What I want to know is that is there some restriction like that on the query size? And why is that even the queries after this query don't run?
This is the 'Query A' to understand the complexity of the query:
Declare #d as datetime, #w as INT, #y as INT, #x as INT, #z as Int, #yy as INT, #maxweek as Int, #ww as Int, #dd as datetime, #t as varchar(20) SET #d= getdate() set #t ='/12/31' SET #w = (datepart(DY, datediff(d, 0, #d) / 7 * 7 + 3)+6) / 7 SET #w=#w-4 SET #y =year(getdate()) SET #x=#y*100+#w if #w <= 0 Begin set #yy = year(getdate())-1 set #dd = cast(cast(#yy as Varchar(10))+ #t + ' 11:11:11' as datetime) set #maxweek = (datepart(DY, datediff(d, 0, #dd) / 7 * 7 + 3)+6) / 7 set #ww= #maxweek + #w set #w= 1 set #y=#yy set #z=#yy*100+#ww set #x=#y*100+#w end else set #z=#x
SELECT s.Name, s.CountryId, AVG(s.Timecalc) as MedianBusLOI FROM (
SELECT imp.name ,Week, i.countryId,
datediff(ss, r.Begin, r.End) As Timecalc,
ROW_NUMBER() OVER (PARTITION BY imp.name ,Week, dataleverantor ORDER BY datediff(ss, r.Begin, r.End) ASC, Foreignid ASC) AS RowAsc,
ROW_NUMBER() OVER (PARTITION BY imp.name ,Week, dataleverantor ORDER BY datediff(ss, r.SurveyBegin, r.SurveyEnd) DESC, Foreignid DESC) AS RowDesc
FROM People r inner join Imports imp on imp.id=r.importid
inner join interview i on i.id = r.InternalRespondentId
WHERE imp.name is not null and r.year*100 + r.week >= #x and r.year*100 + r.week >= #z ) s
WHERE s.RowAsc IN (s.RowDesc, s.RowDesc - 1, s.RowDesc + 1) GROUP BY s.name, countryId
All that this query does is that it calculates the median time taken by a person to complete filling a form. The query has no issues, it works perfectly.

SQL Server stored procedure with ROW_NUMBER

I am try to create a stored procedure that takes an index where to start, max rows to show and a location. It them returns a list of HouseID and the location, but I also want it to include the "name of the house" from another table called dbo.House which has a HouseId to link it to the Location. How would I go about adding the second table.
Thanks,
CREATE PROCEDURE dbo.basicHouseSearch
#StartIndex int,
#MaxRows int,
#HouseLocation NVarChar(50)
AS
BEGIN
SET NOCOUNT ON;
Select
Location.HouseID, CityTown
FROM
(SELECT
ROW_NUMBER() OVER (ORDER by Location.HouseID) as RowNumber,
Location.HouseID,
CityTown
FROM dbo.Location) Location
WHERE
RowNumber >= #StartIndex
AND ROWNUMBER < (#StartIndex + #MaxRows)
END
GO
I re wrote your code so it uses OFFSET/FETCH (I think that it's clearer that way):
SELECT L.HouseID,
L.CityTown,
H.Name [Name of the house]
FROM dbo.Location L
LEFT JOIN dbo.House H
ON L.HouseID = H.HouseID
ORDER BY L.HouseID
OFFSET #StartIndex ROWS FETCH NEXT #MaxRows ONLY
(Requires Sql Server 2012 or later)
I re wrote your code so it uses a CTE (I think that it's clearer that way):
;WITH CTE AS
(
SELECT RowNumber = ROW_NUMBER() OVER (ORDER by L.HouseID),
L.HouseID,
L.CityTown,
H.Name [Name of the house]
FROM dbo.Location L
LEFT JOIN dbo.House H
ON L.HouseID = H.HouseID
)
SELECT *
FROM CTE
WHERE RowNumber >= #StartIndex
AND RowNumber < (#StartIndex + #MaxRows)

Why do my results not stay consistent?

I have the following stored procedure that I working on. I have noticed that every 5th or 6th time I refresh my results there are new values in there. Which considering that the data is in a static environment and no one is making any changes to the data at this time I really can't understand. Can someone please enlighten me as to why I would see different results even though I am running this procedure with the exact same parameters. I even tried it in query analyzer and still see the same strange results.
I am running in Sql 2008.
Here is the proc:
ALTER PROCEDURE [dbo].[SelectSearchBy_Category]
#userId INT,
#page INT,
#results INT,
#category NVARCHAR(50),
#searchTerm NVARCHAR(200) = NULL
AS
BEGIN
SET NOCOUNT ON
SET ROWCOUNT #results
DECLARE #categoryId INT
IF (#category IS NOT NULL) BEGIN
SET #categoryId = ( SELECT categoryId FROM Category WHERE categoryDescription = #category )
END
DECLARE #rowEnd INT
DECLARE #rowStart INT
SET #rowEnd = (#page * #results)
SET #rowStart = #rowEnd - #results
;WITH OrderedItems AS
(
SELECT
i.itemId,
title,
i.[description],
i.url,
i.categoryId,
i.ratingId,
i.requirements,
ISNULL(i.rating, 0) AS tating,
ISNULL(i.raters, 0) AS raters,
i.urlFriendlyPath,
ROW_NUMBER() OVER
(
ORDER BY i.dateAdded, (ISNULL(i.rating, 0) * ISNULL(i.raters, 0))
) AS RowNumber
FROM
[dbo].[Item] i
LEFT JOIN
UserItemIgnore uii ON uii.itemId = i.itemId AND uii.userId = #userId
INNER JOIN
ItemLanguage il ON il.itemId = i.itemId
WHERE
(#searchTerm IS NULL OR a.title LIKE '%' + #searchTerm + '%') AND
i.categoryId = #categoryId AND
il.languageId = 1 AND
uii.itemId IS NULL
)
SELECT *
FROM OrderedItems
WHERE RowNumber BETWEEN #rowStart AND #rowEnd
END
You will probably have consistent results if you put an order by clause in your OrderedItems temporary table definition.
Try using
ROW_NUMBER() OVER (ORDER BY i.dateAdded,
(ISNULL(i.rating, 0) * ISNULL(i.raters, 0)),
i.itemId)
i.itemId will act as a tie breaker to ensure that the results of ROW_NUMBER are deterministic in the event you have rows with equal ranks for i.dateAdded, (ISNULL(i.rating, 0) * ISNULL(i.raters, 0))

using cursors and displaying row numbers

I have declared the following cursor and have used a local variable #RowNo to print the number of each each row.
declare TheCursor cursor
for select productName from products
declare #RowNo int
declare #productName nvarchar(50)
set #RowNo = 1
open TheCursor
fetch next from TheCursor into #productName
print #RowNo
print #productName
set #RowNo = #RowNo+1
set #productName=''
while ##FETCH_STATUS=0
begin
fetch next from TheCursor into #productName
print #RowNo
print #productName
set #RowNo = #RowNo+1
set #productName=''
end
close TheCursor
deallocate TheCursor
I am trying to find any other way to assign / define a number to each row and display it in the console. I have found the function Row_number() and used it like select ROW_NUMBER() over (order by (select 0)) As Rownumber to do it.
I want to know if it is possible to use the KEYSET in my cursor to do it? How can I do it with KEYSET?
I don't completely understand what you're trying to achieve - but you could wrap your select statement including the ROW_NUMBER() function into a CTE (common table expression) and then go from there - no messy cursor needed, that really doesn't scale well:
WITH YourSelection AS
(
SELECT
ProductName,
ROW_NUMBER() OVER(ORDER BY ProductName) AS 'RowNum'
FROM dbo.Products
)
SELECT ProductName, RowNum
FROM YourSelection
That should give you the product names, sorted by ProductName, and corresponding row numbers, too.

Can I use #table variable in SQL Server Report Builder?

Using SQL Server 2008 Reporting services:
I'm trying to write a report that displays some correlated data so I thought to use a #table variable like so
DECLARE #Results TABLE (Number int
,Name nvarchar(250)
,Total1 money
,Total2 money
)
insert into #Results(Number, Name, Total1)
select number, name, sum(total)
from table1
group by number, name
update #Results
set total2 = total
from
(select number, sum(total) from table2) s
where s.number = number
select from #results
However, Report Builder keeps asking to enter a value for the variable #Results. It this at all possible?
EDIT: As suggested by KM I've used a stored procedure to solve my immediate problem, but the original question still stands: can I use #table variables in Report Builder?
No.
ReportBuilder will
2nd guess you
treats #Results as a parameter
Put all of that in a stored procedure and have report builder call that procedure. If you have many rows to process you might be better off (performance wise) with a #temp table where you create a clustered primary key on Number (or would it be Number+Name, not sure of your example code).
EDIT
you could try to do everything in one SELECT and send that to report builder, this should be the fastest (no temp tables):
select
dt.number, dt.name, dt.total1, s.total2
from (select
number, name, sum(total) AS total1
from table1
group by number, name
) dt
LEFT OUTER JOIN (select
number, sum(total) AS total2
from table2
GROUP BY number --<<OP code didn't have this, but is it needed??
) s ON dt.number=s.number
I've seen this problem as well. It seems SQLRS is a bit case-sensitive. If you ensure that your table variable is declared and referenced everywhere with the same letter case, you will clear up the prompt for parameter.
You can use Table Variables in SSRS dataset query like in my code where I am adding needed "empty" records for keep group footer in fixed postion (sample use pubs database):
DECLARE #NumberOfLines INT
DECLARE #RowsToProcess INT
DECLARE #CurrentRow INT
DECLARE #CurRow INT
DECLARE #cntMax INT
DECLARE #NumberOfRecords INT
DECLARE #SelectedType char(12)
DECLARE #varTable TABLE (# int, type char(12), ord int)
DECLARE #table1 TABLE (type char(12), title varchar(80), ord int )
DECLARE #table2 TABLE (type char(12), title varchar(80), ord int )
INSERT INTO #varTable
SELECT count(type) as '#', type, count(type) FROM titles GROUP BY type ORDER BY type
SELECT #cntMax = max(#) from #varTable
INSERT into #table1 (type, title, ord) SELECT type, N'', 1 FROM titles
INSERT into #table2 (type, title, ord) SELECT type, title, 1 FROM titles
SET #CurrentRow = 0
SET #SelectedType = N''
SET #NumberOfLines = #RowsPerPage
SELECT #RowsToProcess = COUNT(*) from #varTable
WHILE #CurrentRow < #RowsToProcess
BEGIN
SET #CurrentRow = #CurrentRow + 1
SELECT TOP 1 #NumberOfRecords = ord, #SelectedType = type
FROM #varTable WHERE type > #SelectedType
SET #CurRow = 0
WHILE #CurRow < (#NumberOfLines - #NumberOfRecords % #NumberOfLines) % #NumberOfLines
BEGIN
SET #CurRow = #CurRow + 1
INSERT into #table2 (type, title, ord)
SELECT type, '' , 2
FROM #varTable WHERE type = #SelectedType
END
END
SELECT type, title FROM #table2 ORDER BY type ASC, ord ASC, title ASC
Why can't you just UNION the two resultsets?
How about using a table valued function rather than a stored proc?
It's possible, only declare your table with '##'. Example:
DECLARE ##results TABLE (Number int
,Name nvarchar(250)
,Total1 money
,Total2 money
)
insert into ##results (Number, Name, Total1)
select number, name, sum(total)
from table1
group by number, name
update ##results
set total2 = total
from
(select number, sum(total) from table2) s
where s.number = number
select * from ##results

Resources