Selecting Spicific data placed in the middle of the database table - sql

how to determine which row number in my database will the selection query start to select and the Limit of the selection ,as I want to select piece of data which is located in the middle of my database table
I may use between #indexOfSelection , #limitOfSelection or something like that ,but I don't know how !
CREATE PROCEDURE ordered_articles
#LowerBound int,
#UpperBound int
AS
select * from orderedData where articleid between LowerBound and UpperBound ;
with orderedData
(
select * , rn = ROW_NUMBER() over ORDER BY (articleid)
from articles
)
WHERE rn >= #LowerBound AND rn <= #UpperBound
RETURN

I guess you can make use of ROW_NUMBER Function something like this ....
;WITH OrderedData
AS
(
SELECT * , rn = ROW_NUMBER() OVER (ORDER BY SomeColumn)
FROM Table_Name
)
SELECT * FROM OrderedData
WHERE rn >= #LowerLimit AND rn <= #UpperLimit
Your Query
select * from articles
where articleid between #indexOfSelection AND #LimitOfselection
You just need to add the key word AND between your upper lower limit variable and upper limit variable.
Your Stored Procedure
CREATE PROCEDURE ordered_articles
#LowerBound int,
#UpperBound int
AS
BEGIN
SET NOCOUNT ON;
select * from articles
where articleid between #LowerBound and #UpperBound
END
To Select A range Of Rows
CREATE PROCEDURE ordered_articles
#LowerBound int,
#UpperBound int
AS
BEGIN
SET NOCOUNT ON;
WITH OrderedData
AS
(
SELECT * , rn = ROW_NUMBER() OVER (ORDER BY articleid)
FROM articles
)
SELECT * FROM OrderedData
WHERE rn >= #LowerBound AND rn <= #UpperBound
END
EXECUTE ordered_articles 10, 15 --<-- this will return 10 to 15 number row ordered by ArticleID

Related

Selecting data from table where sum of values in a column equal to the value in another column

Sample data:
create table #temp (id int, qty int, checkvalue int)
insert into #temp values (1,1,3)
insert into #temp values (2,2,3)
insert into #temp values (3,1,3)
insert into #temp values (4,1,3)
According to data above, I would like to show exact number of lines from top to bottom where sum(qty) = checkvalue. Note that checkvalue is same for all the records all the time. Regarding the sample data above, the desired output is:
Id Qty checkValue
1 1 3
2 2 3
Because 1+2=3 and no more data is needed to show. If checkvalue was 4, we would show the third record: Id:3 Qty:1 checkValue:4 as well.
This is the code I am handling this problem. The code is working very well.
declare #checkValue int = (select top 1 checkvalue from #temp);
declare #counter int = 0, #sumValue int = 0;
while #sumValue < #checkValue
begin
set #counter = #counter + 1;
set #sumValue = #sumValue + (
select t.qty from
(
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY id ASC) AS rownumber,
id,qty,checkvalue
FROM #temp
) AS foo
WHERE rownumber = #counter
) t
)
end
declare #sql nvarchar(255) = 'select top '+cast(#counter as varchar(5))+' * from #temp'
EXECUTE sp_executesql #sql, N'#counter int', #counter = #counter;
However, I am not sure if this is the best way to deal with it and wonder if there is a better approach. There are many professionals here and I'd like to hear from them about what they think about my approach and how we can improve it. Any advice would be appreciated!
Try this:
select id, qty, checkvalue from (
select t1.*,
sum(t1.qty) over (partition by t2.id) [sum]
from #temp [t1] join #temp [t2] on t1.id <= t2.id
) a where checkvalue = [sum]
Smart self-join is all you need :)
For SQL Server 2012, and onwards, you can easily achieve this using ROWS BETWEEN in your OVER clause and the use of a CTE:
WITH Running AS(
SELECT *,
SUM(qty) OVER (ORDER BY id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningQty
FROM #temp t)
SELECT id, qty, checkvalue
FROM Running
WHERE RunningQty <= checkvalue;
One basic improvement is to try & reduce the no. of iterations. You're incrementing by 1, but if you repurpose the logic behind binary searching, you'd get something close to this:
DECLARE #RoughAverage int = 1 -- Some arbitrary value. The closer it is to the real average, the faster things should be.
DECLARE #CheckValue int = (SELECT TOP 1 checkvalue FROM #temp)
DECLARE #Sum int = 0
WHILE 1 = 1 -- Refer to BREAK below.
BEGIN
SELECT TOP (#RoughAverage) #Sum = SUM(qty) OVER(ORDER BY id)
FROM #temp
ORDER BY id
IF #Sum = #CheckValue
BREAK -- Indicating you reached your objective.
ELSE
SET #RoughAverage = #CheckValue - #Sum -- Most likely incomplete like this.
END
For SQL 2008 you can use recursive cte. Top 1 with ties limits result with first combination. Remove it to see all combinations
with cte as (
select
*, rn = row_number() over (order by id)
from
#temp
)
, rcte as (
select
i = id, id, qty, sumV = qty, checkvalue, rn
from
cte
union all
select
a.id, b.id, b.qty, a.sumV + b.qty, a.checkvalue, b.rn
from
rcte a
join cte b on a.rn + 1 = b.rn
where
a.sumV < b.checkvalue
)
select
top 1 with ties id, qty, checkvalue
from (
select
*, needed = max(case when sumV = checkvalue then 1 else 0 end) over (partition by i)
from
rcte
) t
where
needed = 1
order by dense_rank() over (order by i)

sql select into select in function

When I try to alter the function below I get the following error message:
Only one expression can be specified in the select list when the
subquery is not introduced with EXISTS.
I guess it is probably because of select into select. But why does this select into select work separately ( not in function ) but not in function.
ALTER FUNCTION [dbo].[Getcurrentexchangerate] (#CurrencyFromId INT,
#CurrencyToId INT)
returns DECIMAL(13, 10)
AS
BEGIN
DECLARE #rate DECIMAL (13, 10)
DECLARE #dw INT
SET #dw = (SELECT Datepart(dw, Getdate()))
IF( #dw != 2 ) -- Monday
BEGIN
SET #rate = (SELECT TOP (1) [rate]
FROM currencyconversionrate
WHERE [currencyfromid] = #CurrencyFromId
AND [currencytoid] = #CurrencyToId
ORDER BY id DESC)
END
ELSE
BEGIN
SET #rate = (SELECT *
FROM (SELECT TOP(2) Row_number()
OVER (
ORDER BY id DESC) AS
rownumber,
rate
FROM currencyconversionrate
WHERE ( [currencyfromid] = 2
AND [currencytoid] = 5 )
ORDER BY id DESC) AS Rate
WHERE rownumber = 2)
END
IF( #rate IS NULL )
BEGIN
SET #rate = 1
END
RETURN #rate
END
See your "else" part
SET #rate = (SELECT *
FROM (SELECT TOP(2) Row_number()
OVER (
ORDER BY id DESC) AS
rownumber,
rate
FROM currencyconversionrate
WHERE ( [currencyfromid] = 2
AND [currencytoid] = 5 )
ORDER BY id DESC) AS Rate
WHERE rownumber = 2)
You're trying to select all fields from currencyconversionrate table, you can't do that, or do you want to select "RATE" column only?
Try changing to below:
SET #rate = (SELECT rate
FROM (SELECT TOP(1) Row_number()
OVER (
ORDER BY id DESC) AS
rownumber,
rate
FROM currencyconversionrate
WHERE ( [currencyfromid] = 2
AND [currencytoid] = 5 )
ORDER BY id DESC) AS Rate
WHERE rownumber = 2)

How to Select A number of data of table record, but before apply a condition on this record?

I want that in my wen site admin can confirm dis active users. for this, I want show dis active users in repeater to admin.
I want the Disactive user repeater has paging.
for this purpose I write this Procedure in SQL:
ALTER proc [dbo].[SPFetchAllDisActiveUser]
(
#StartRowIndex int,
#MaxRows int
)
as
Begin
Select * From
(select *
,ROW_NUMBER() OVER(ORDER BY [User-ID] asc) AS RowNum
from [User-Tbl]
) As DisActiveUser
Where RowNum between #StartRowIndex+1 and #MaxRows
and [Gu-Id] is null;
END
I know that (for example if #StartRowIndex=0 and #MaxRows=10) this code first select 10 record from table and so check that if an record has condition [Gu-Id] is null, return it.
I want that first apply condition next select a number rows from it. but I don't know how to do it.
I Thanks anybody that guide me :)
To expand on my comment about moving the [Gu-Id] IS NULL, try this:
ALTER PROC [dbo].[SPFetchAllDisActiveUser]
(
#StartRowIndex INT,
#MaxRows INT
)
AS
BEGIN
SELECT * FROM
(
SELECT *, ROW_NUMBER() OVER(ORDER BY [User-ID] ASC) AS RowNum
FROM [User-Tbl]
WHERE [Gu-Id] IS NULL
) AS DisActiveUser
WHERE RowNum BETWEEN #StartRowIndex + 1 AND #MaxRows;
END

How Can I Set Value To output parameter in sql stored procedure within select statement?

I have Stored Procedure in sql server 2008 used to paginating data returned from product table in database. what i want is return total pages count in the same select statement
ALTER PROCEDURE [dbo].[SP_GetProducts]
#PageSize int = 10,
#PageNumber int = 1,
#ProductCode nvarchar(50) = null,
#ProductName nvarchar(100) = null,
#TotalPages int = null output
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT TOP (#PageSize) *, #TotalPages = COUNT(*) FROM (
SELECT ROW_NUMBER() OVER (ORDER BY ProductId ASC) offset, * FROM (
SELECT *, count(*) over() AS TotalRows
FROM Products
) myquery
) paginator
WHERE offset >= (((#PageSize * #PageNumber) - #PageSize)) +1 and offset <= (#PageSize * #PageNumber)
you cannot retrieve values from a Select statement and assign value to a variable at the same time. In your particular case since you are only assigning the number or rows returned from your select statement you can use a slightly different approach to get what you want.
ALTER PROCEDURE [dbo].[SP_GetProducts]
#PageSize int = 10,
#PageNumber int = 1,
#ProductCode nvarchar(50) = null,
#ProductName nvarchar(100) = null,
#TotalPages int = null output
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP (#PageSize) *
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY ProductId ASC) offset, *
FROM (
SELECT *, count(*) over() AS TotalRows
FROM Products
) myquery
) paginator
WHERE offset >= (((#PageSize * #PageNumber) - #PageSize)) +1
AND offset <= (#PageSize * #PageNumber)
-- here assign value to your output parameter using the following
SELECT #TotalPages = ##ROWCOUNT;
END
Note
Avoid using SELECT * instead use Column names, only the columns you actually want to retrieve.

returning total records in cte

With help from article here and recent answers from SO experts I have arrived with the following which will help me efficiently page through a set of records.
I think my last couple of questions are
See how I include the Total Number of Records in the payload at the end
of SQL CTE called 'Total'. Is that how you would do this?
Any other suggestions? Potential areas for being more concise or improvements? Return Total Number of Pages
DECLARE #page_size INT = 5;
DECLARE #page_nbr INT = 4;
DECLARE #search NVARCHAR(MAX) = '';
DECLARE #sort_order INT = 2;
WITH AllProducts
AS
(
SELECT *,
CASE #sort_order
WHEN 1 THEN ROW_NUMBER() OVER ( ORDER BY ProductID )
WHEN 2 THEN ROW_NUMBER() OVER ( ORDER BY ProductName )
END AS 'Seq'
FROM Products
),
Filtered
AS
(
SELECT * FROM AllProducts
WHERE ProductName like '%'+#search+'%'
OR
#search is null
)
SELECT (select COUNT(*) from Filtered) as 'Total', * FROM Filtered
WHERE seq > (#page_nbr - 1) * #page_size
AND seq <= #page_nbr * #page_size
I think there's something wrong in your query: it numbers records (for paging) and after that applies the filter.
It is, for example, possible that you request page 2 of records, but all records with the corresponding seq values could be filtered out in the meantime. So, in this case the query would yield no results, although there may be plenty of records in the table.
In order to fix that, you could do the filtering and record numbering in the same CTE, like this:
DECLARE #page_size INT = 5;
DECLARE #page_nbr INT = 4;
DECLARE #search NVARCHAR(MAX) = '';
DECLARE #sort_order INT = 2;
WITH Filtered AS (
SELECT *,
CASE #sort_order
WHEN 1 THEN ROW_NUMBER() OVER ( ORDER BY ProductID )
WHEN 2 THEN ROW_NUMBER() OVER ( ORDER BY ProductName )
END AS 'Seq'
FROM AllProducts
WHERE ProductName like '%'+#search+'%' OR #search is null
)
SELECT (select COUNT(*) from Filtered) as 'Total', * FROM Filtered
WHERE seq > (#page_nbr - 1) * #page_size
AND seq <= #page_nbr * #page_size