I am new to sql programming; trying to develop this function to get rows of clients who have a specific number of visits from a view:
ALTER FUNCTION [dbo].[fn_NumberOfVisit]
(
#nv int
)
RETURNS varchar(500)
AS
BEGIN
DECLARE #ret varchar(500)
select *
from (
select
*,
rn = row_number() over (partition by ClientId order by VisitId)
from
Visit
) activityWithRn
inner join vw_MasterView on vw_MasterView.VisitId = activityWithRn.VisitId
where activityWithRn.rn =#nv
RETURN #ret
END
I get the following error:
Select statements included within a function cannot return data to a client
I would appreciate your support. Thanks in advance.
Your problem is here:
set #Count = ( select *
from (
select
*,
#Count is expecting a number - you're giving it a bunch of rows, try:
set #Count = ( select Count(*)
from (
select
The error is telling you that your subquery is returning too many rows. You need to return only one row is you are assigning the result to a variable.
Change
set #Count = ( select *
from (
select
*,
rn = row_number() over (partition by ClientId order by VisitId)
from
Visit
To
set #Count = ( select count(*)
from (
select
*,
rn = row_number() over (partition by ClientId order by VisitId)
from
Visit
Related
Can anyone please let me know where of the SQL query is wrong?
The error is
Incorrect syntax near "return"
Code:
CREATE FUNCTION getNthHighestSalary(#N INT)
RETURNS INT
AS
BEGIN
WITH ranksalary AS
(
SELECT
salary,
ROW_NUMBER() OVER (ORDER BY Salary DESC) AS Rank
FROM
Employee
)
RETURN (SELECT Salary AS getNthHighestSalary
FROM ranksalary
WHERE Rank = #N);
END
Looks like T-SQL to me. As #marc_s already pointed out you need to store the value in a variable first and then return that.
CREATE FUNCTION getNthHighestSalary(#N INT)
RETURNS INT
AS
BEGIN
DECLARE #result int
;WITH ranksalary AS
(
SELECT
salary,
ROW_NUMBER() OVER (ORDER BY Salary DESC) AS [Rank]
FROM
Employee
)
SELECT #result = Salary
FROM ranksalary
WHERE [Rank] = #N
RETURN #result
END
GO
Why not just use this logic?
declare #rank;
select #rank = count(*) + 1
from employee
where salary > #salary;
return #rank;
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)
How can I udpate a column with row_number in SQL Server 2008 R2?
BEGIN TRANSACTION
DECLARE #count int
DECLARE #maxcount int
SET #count = 1
SET #maxcount = (SELECT count(*)
FROM Applicant_Detail ad
WHERE ad.identification_code = 1)
PRINT #maxcount
WHILE (#count<#maxcount)
BEGIN
UPDATE ad
SET ad.NRIC_nbr = s.myRowNumber
FROM Applicant_Detail ad
INNER JOIN (
SELECT ROW_NUMBER() OVER (ORDER BY NRIC_nbr ASC) AS myRowNumber
FROM Applicant_Detail ad
)S
ON s.myRowNumber = #count
SET #count = #count+1
END
This query takes a lot of time. I do not have any column in the applicant_detail table which has sequential data? I use the count logic but takes lot of time?
What i want?
Update the column of the table with sequential data like 1,2,3,4,5,6,7,8,9...... max row of the table?
Try this:
declare #count int = (select count(1) from Applicant_Detail)
;with cte as
(select *, row_number() over (order by #count) rn
from Applicant_Detail)
update cte
set NRIC_nbr = rn
select * from Applicant_Detail
Demo
Solution for this problem
BEGIN TRANSACTION
;WITH numbering AS
( SELECT AD.NRIC_nbr,
AD.application_number,
ROW_NUMBER() OVER(ORDER BY AD.application_number) AS ROWNUMBER
FROM Applicant_Detail ad WHERE AD.identification_code=1
)
UPDATE numbering
SET NRIC_nbr=ROWNUMBER
when you want to update the column with row_numebr. it is the perfect solution
WITH TEMP AS
(
SELECT id as rid,
ROW_NUMBER() OVER (ORDER BY [ID] ASC) AS RN
FROM ORG
)
update ORG Set columnname1 ='200'+(select RN FROM TEMP where TEMP.rid=ORG.id)
Below is a piece of my stored proc.
I am getting error as invalid object MyCount, Please let me know where am going wrong
;With MyCount AS
(
Select DispatchToRegionId ,FolderNo, row_number() OVER(ORDER BY FolderNo DESC) as Row
from tblTransite where FolderNo = #VAL group by DispatchToRegionId,FolderNo
)
select #cnt = COUNT(*) from MyCount
if #cnt = 0
begin
set #InvalidFolderNo = #VAL
print 'cnt -' + cast(#cnt as varchar(max) ) + 'invalid folder - ' + cast(#InvalidFolderNo as varchar(max) )
return
end
select #Region =( Select top 1 DispatchToRegionId from MyCount
order by Row desc )
MSDN clearly states the following about the scope of a Common Table Expression (CTE):
A common table expression (CTE) can be thought of as a temporary result set that is defined within the execution scope of a single SELECT, INSERT, UPDATE, DELETE, or CREATE VIEW statement
Once you have run the first select query, you can no longer use the CTE for your next one. You may want to consider storing the data in a temporary table or table variable if you want to access it in multiple queries.
MyCount is available only for the first query after it. Try to select all you need in this one query:
;With MyCount AS
(
Select DispatchToRegionId ,FolderNo, row_number() OVER(ORDER BY FolderNo DESC) as Row
from tblTransite where FolderNo = #VAL group by DispatchToRegionId,FolderNo
)
SELECT
#cnt = (select COUNT(*) from MyCount),
#Region =( Select top 1 DispatchToRegionId from MyCount
order by Row desc )
if #cnt = 0
begin
set #InvalidFolderNo = #VAL
print 'cnt -' + cast(#cnt as varchar(max) ) + 'invalid folder - ' + cast(#InvalidFolderNo as varchar(max) )
return
end
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