SQL Server stored procedure with ROW_NUMBER - sql-server

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)

Related

WITH-AS statement with declare and set

I'm a newbie of sql-server and I've been learning sql queries about with-as and declare and set.
I have a question to make my query short.
DECLARE #MaxID int;
SET #MaxID = (SELECT MAX(rowNumber) FROM (
SELECT ROW_NUMBER() OVER(ORDER BY NoWkOrd ASC) AS rowNumber, a, b FROM
t
) t1) ;
SELECT rowNumber, NoWkOrd, CdEquip, DtWkOrd
FROM (
SELECT ROW_NUMBER() OVER(ORDER BY NoWkOrd ASC) AS rowNumber, a, b
FROM t
) t1
WHERE rowNumber > #MaxID
This is an orignal query and I want to make this short by using with-as cuz two query inside of from is used twice at the same time.
DECLARE #MaxID int;
WITH t1
AS (
SELECT ROW_NUMBER() OVER(ORDER BY NoWkOrd ASC) AS rowNumber, a, b
FROM t
)
SET #MaxID = (SELECT MAX(rowNumber) FROM (t1) ;
SELECT rowNumber, NoWkOrd, CdEquip, DtWkOrd
FROM t1
WHERE rowNumber > #MaxID
But here's an error around SET but i'm still struggling with what is wrong...

Row number with len in SQL Server

I want to take the length of a specific row and show this substring, but I'm not getting luck, can somebody help me I was trying something like this:
declare #teste int
declare #rows int
select #rows = (select COUNT(DS_Description) from IMPL_Activities)
while #rows > 0
begin
--select #test = (select LEN(DS_Description), ROW_NUMBER() over (order by ID_Deployment) rn
--from IMPL_Activities where rn in (#rows))
select
#test = (select *
from
(select
ROW_NUMBER() over(order by ID_Deployment) rn
from IMPL_Activities) as imp
where rn in (#rows)
select LEN(DS_Description) from IMPL_Activities where rn in (#rows))
set #rows = #rows - 1
select SUBSTRING(DS_Description, 1, #test)
from IMPL_Activities
end
But can't save the number of character that have this column in that row, I don't know if did you understood, but comments, or edit, to make sure that everyone will understand.
Thanks
No cursor necessary, is this what you want?
SELECT
SUBSTRING(DS_Description,LEN(DS_Description)/2,1)
FROM
IMPL_Activities;

How to make this sql query

I have 2 SQL Server tables with the following structure
Turns-time
cod_turn (PrimaryKey)
time (datetime)
Taken turns
cod_taken_turn (Primary Key)
cod_turn
...
and several other fields which are irrelevant to the problem. I cant alter the table structures because the app was made by someone else.
given a numeric variable parameter, which we will assume to be "3" for this example, and a given time, I need to create a query which looking from that time on, it looks the first 3 consecutive records by time which are not marked as "taken". For example:
For example, for these turns, starting by the time of "8:00" chosen by the user
8:00 (not taken)
9:00 (not taken)
10:00 (taken)
11:00 (not taken)
12:00 (not taken)
13:00 (not taken)
14:00 (taken)
The query it would have to list
11:00
12:00
13:00
I cant figure out how to make the query in pure sql, if possible.
with a cursor
declare #GivenTime datetime,
#GivenSequence int;
select #GivenTime = cast('08:00' as datetime),
#GivenSequence = 3;
declare #sequence int,
#code_turn int,
#time datetime,
#taked int,
#firstTimeInSequence datetime;
set #sequence = 0;
declare turnCursor cursor FAST_FORWARD for
select turn.cod_turn, turn.[time], taken.cod_taken_turn
from [Turns-time] as turn
left join [Taken turns] as taken on turn.cod_turn = taken.cod_turn
where turn.[time] >= #GivenTime
order by turn.[time] asc;
open turnCursor;
fetch next from turnCursor into #code_turn, #time, #taked;
while ##fetch_status = 0 AND #sequence < #GivenSequence
begin
if #taked IS NULL
select #firstTimeInSequence = coalesce(#firstTimeInSequence, #time)
,#sequence = #sequence + 1;
else
select #sequence = 0,
#firstTimeInSequence = null;
fetch next from turnCursor into #code_turn, #time, #taked;
end
close turnCursor;
deallocate turnCursor;
if #sequence = #GivenSequence
select top (#GivenSequence) * from [Turns-time] where [time] >= #firstTimeInSequence
order by [time] asc
WITH Base AS (
SELECT *,
CASE WHEN EXISTS(
SELECT *
FROM Taken_turns taken
WHERE taken.cod_turn = turns.cod_turn) THEN 1 ELSE 0 END AS taken
FROM [Turns-time] turns)
, RecursiveCTE As (
SELECT TOP 1 cod_turn, [time], taken AS run, 0 AS grp
FROM Base
WHERE [time] >= #start_time
ORDER BY [time]
UNION ALL
SELECT R.cod_turn, R.[time], R.run, R.grp
FROM (
SELECT T.*,
CASE WHEN T.taken = 0 THEN 0 ELSE run+1 END AS run,
CASE WHEN T.taken = 0 THEN grp + 1 ELSE grp END AS grp,
rn = ROW_NUMBER() OVER (ORDER BY T.[time])
FROM Base T
JOIN RecursiveCTE R
ON R.[time] < T.[time]
) R
WHERE R.rn = 1 AND run < #run_length
), T AS(
SELECT *,
MAX(grp) OVER () AS FinalGroup,
COUNT(*) OVER (PARTITION BY grp) AS group_size
FROM RecursiveCTE
)
SELECT cod_turn,time
FROM T
WHERE grp=FinalGroup AND group_size=#run_length
I think there is not a simple way to achieve this.
But probably there are many complex ways :). This is an approach that should work in Transact-SQL:
CREATE TABLE #CONSECUTIVE_TURNS (id int identity, time datetime, consecutive int)
INSERT INTO #CONSECUTIVE_TURNS (time, consecutive, 0)
SELECT cod_turn
, time
, 0
FROM Turns-time
ORDER BY time
DECLARE #i int
#n int
SET #i = 0
SET #n = 3 -- Number of consecutive not taken records
while (#i < #n) begin
UPDATE #CONSECUTIVE_TURNS
SET consecutive = consecutive + 1
WHERE not exists (SELECT 1
FROM Taken-turns
WHERE id = cod_turn + #i
)
SET #i = #i + 1
end
DECLARE #firstElement int
SELECT #firstElement = min(id)
FROM #CONSECUTIVE_TURNS
WHERE consecutive >= #n
SELECT *
FROM #CONSECUTIVE_TURNS
WHERE id between #firstElement
and #firstElement + #n - 1
This is untested but I think it will work.
Pure SQL
SELECT TOP 3 time FROM [turns-time] WHERE time >= (
-- get first result of the 3 consecutive results
SELECT TOP 1 time AS first_result
FROM [turns-time] tt
-- start from given time, which is 8:00 in this case
WHERE time >= '08:00'
-- turn is not taken
AND cod_turn NOT IN (SELECT cod_turn FROM taken_turns)
-- 3 consecutive turns from current turn are not taken
AND (
SELECT COUNT(*) FROM
(
SELECT TOP 3 cod_turn AS selected_turn FROM [turns-time] tt2 WHERE tt2.time >= tt.time
GROUP BY cod_turn ORDER BY tt2.time
) AS temp
WHERE selected_turn NOT IN (SELECT cod_turn FROM taken_turns)) = 3
) ORDER BY time
Note: I tested it on Postgresql (with some code modification), but not MS SQL Server. I'm not sure about performance compared to T-SQL.
Another set-based solution (tested):
DECLARE #Results TABLE
(
cod_turn INT NOT NULL
,[status] TINYINT NOT NULL
,RowNumber INT PRIMARY KEY
);
INSERT #Results (cod_turn, [status], RowNumber)
SELECT a.cod_turn
,CASE WHEN b.cod_turn IS NULL THEN 1 ELSE 0 END [status] --1=(not taken), 0=(taken)
,ROW_NUMBER() OVER(ORDER BY a.[time]) AS RowNumber
FROM [Turns-time] a
LEFT JOIN [Taken_turns] b ON a.cod_turn = b.cod_turn
WHERE a.[time] >= #Start;
--SELECT * FROM #Results r ORDER BY r.RowNumber;
SELECT *
FROM
(
SELECT TOP(1) ca.LastRowNumber
FROM #Results a
CROSS APPLY
(
SELECT SUM(c.status) CountNotTaken, MAX(c.RowNumber) LastRowNumber
FROM
(
SELECT TOP(#Len)
b.RowNumber, b.[status]
FROM #Results b
WHERE b.RowNumber <= a.RowNumber
ORDER BY b.RowNumber DESC
) c
) ca
WHERE ca.CountNotTaken = #Len
ORDER BY a.RowNumber ASC
) x INNER JOIN #Results y ON x.LastRowNumber - #Len + 1 <= y.RowNumber AND y.RowNumber <= x.LastRowNumber;

SQL Server Full Text Search Very Slow

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

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))

Resources