Use While loop to calculate values from Table? - sql

I have a following Table Where I need to loop through the records and add values to a variable
Declare #Variable1 INT
SET #Variable1=0
Declare #totalval INT
SET #totalval=0
While (#Variable1<=20)
BEGIN
SET #totalval=#totalval+(Select Salary from EmpTable Where EmpID=9)
PRINT #totalval
SET #Variable1= Variable1+1
END
GO
I cant print the value...I am using SQL server 2005
Thank you all

You could use this instead:
;WITH a AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) num
, *
FROM EmpTable
WHERE EmpID = 9
)
SELECT #totalval = SUM(Salary)
, #Variable1 = COUNT(*)
FROM a
WHERE num <= 20
This would be one of usual ways of doing such task and it's more efficient than looping. ROW_NUMBER() OVER (ORDER BY (SELECT 0)) gives ordinal numbers to records but doesn't change sort.

This is also working (for your requirement):
Fiddle demo here
declare #total int, #records int = 20
select top( #records) #total=isnull(#total,0) + salary
from EmpTable
where empid = 9
-- order by salary (you may need to order by something)

The problem is this line:
SET #Variable1= Variable1+1
that doesn't work, change it to this:
SET #Variable1= #Variable1+1
if you were to leave it you should be getting this error:
Msg 207, Level 16, State 1, Line 11
Invalid column name 'Variable1'.

Related

How to get Output parameter from a column in table for a stored procedure

I've stored procedure like this:
create procedure sp_testsp
(
#vc_order_by varchar(100),
#int_start_index INT,
#int_grid_size INT,
#count bigint output
)
as
begin
select * from
(select ROW_NUMBER() over
(order by
case #vc_order_by = '' then tab1.int_id end desc) AS row,
*,
COUNT(tab1.int_id) OVER() as totalRowCount
from
(select * from tbl_test) tab1) tab2
where row BETWEEN CONVERT(VARCHAR, #int_start_index) and CONVERT(VARCHAR,(#int_start_index-1) + #int_grid_size);
set #count = 0;
end
We can execute the above stored procedure by:
DECLARE #size bigint;
EXEC sp_testsp '', 1,5, #size output;
SELECT #size;
The written sp provides data based on pagination and we can retrieve 100 or any number of records by passing a number in #int_grid_size .
The table output looks like following:
row int_id vc_name totalRowCount
1 5 a 107
2 6 ab 107
3 7 abc 107
4 8 abcd 107
5 10 abcc 107
The last column gives the total records count of the table or total record if we use where condition.
I want to OUTPUT any one column value of the totalRowCount in '#count' in the stored procedure.
I cannot use ##ROWCOUNT as it only sends the count of records the sp is outputting i.e in this case 5 but actual records are 107.
Just wondering if there is any way. Any help is apperciated. Thanks.
Edit:
I tried something like this, and it works:
create procedure sp_testsp
#param1 nvarchar(800),
#count bigint output
as
begin
select * from tbl_test tt where tt.col1 = #param1;
set #count = select Count(*) from tbl_test tt where tt.col1 = #param1;
end
The issue with this is I've to call the query once and then call the query again for #count. This is working but taking lot of time for big queries.
You can do that by temp table
select * into #temp from
(select ROW_NUMBER() over
(order by
case #vc_order_by = '' then tab1.int_id end desc) AS row,
*,
COUNT(tab1.int_id) OVER() as totalRowCount
from
(select * from tbl_test) tab1) tab2
where row BETWEEN CONVERT(VARCHAR, #int_start_index) and CONVERT(VARCHAR,(#int_start_index-1) + #int_grid_size);
select top 1 #count=totalRowCount from #temp
select * from #temp --you can exclude totalRowCount

SQL creating function got error on the return part

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;

SQL: Update multiple rows (Top 100) with sequential data

I am working on SQL Server 2008 and trying to update a table. I just need the first one hundred rows to be updated with a 15 digit sequence. I have tried the following:
Declare #id varchar
Set #id = 2435435345962
UPDATE TOP (100) Table1
SET #id = Column_nm = #id + 1
GO
but I am getting the following error message:
Msg 426, Level 16, State 1, Line 3
The length 1 of the receiving variable is less than the length 15 of the column 'Column_nm'.
Several things. You want start in 0 so you can add 00-99.
So lets start with the base
WITH cte as (
SELECT TOP 100 *,
ROW_NUMBER() OVER (ORDER BY somefield) -1 as rn -- row_number start with 1.
FROM Table1
ORDER BY somefield
)
UPDATE cte
SET anotherfield = '1234567890ABC' + REPLACE(STR(rn, 2, 0), ' ', '0')
^^ 13 chars ^^ two digits number
The error message is about length of your variable. In SQL Server the default length of varchar is 1 (from msdn.microsoft.com i have only the french link sorry).
Declare #id varchar(15)
Set #id = 2435435345962
UPDATE top (100) Table1
SET #id = Column_nm = #id + 1
GO
You must declare id with length of 15 to put your string in this variable.

Gettin error in SQL Server while finding out max age in a table

We can try to get max age simply by using
SELECT TOP 1 age FROM Head1 ORDER BY Age DESC
But I've tried using while loop in SQL Server
code
declare #a int, #m int, #maxo int;
set #maxo = 0;
while(#a<10)
begin
select name, #m = age from head1 where ID = #a;
if #m>#maxo
#maxo = #m;
set #a=#a+1;
end
print #maxo
error
Msg 141, Level 15, State 1, Line 5
A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
Msg 102, Level 15, State 1, Line 7
Incorrect syntax near '#maxo'.
I am sort of stuck here. Please help guys.....
There are two issues:
Issue 1:
The error which you are getting is self explanatory ie, you cant select the column while you are assigning the values to the variable.
You can resolve it like this:
select #name = name, #m = age from head1 where ID = #a;
Issue 2:
In this I dont think you need that query at all to find the max age from your table. You can simply use max() function to find the max age from your table like this
SELECT Name, Age FROM Head1 WHERE Age = (SELECT MAX(Age) FROM Head1)
Using a loop is inefficient as well as it will create a performance bottleneck if your table is huge.
Exception text is self-explanatory.
Since you can't retrieve name in the same statement where you're assigning #m (and actually you're not using this name anywhere - so it looks you don't need it), you have to change this line
select name, #m = age from head1 where ID = #a;
to
select #m = age from head1 where ID = #a;
Or, if you really need some name, it should be assigned to some variable too, not only selected:
select #n = name, #m = age from head1 where ID = #a;
But in general this will not work, since there can be multiple records in head1 meets condition ID = #a. Assigning value to variable will only work if query returns only single row.
Note - using loop is very unefficient way of finding max value.
At first I should ask you about (#a<10) that it should be always 10?, And I think it is count of rows of your table that you take it like this:
DECLARE #rows bigint
SET #rows = (SELECT COUNT(1) FROM head1)
Then use it ; (#a < #rows).
Now the second problem is that you use ID = #a that is an invalid clause for deleted rows in your table or gaps. For solving this with using a kind of that clause you should find a max(ID) again and again.
I can just suggest you to use this query, But without WHILE:
DECLARE #max int
SET #max = 0
SELECT #max = CASE WHEN #max < age THEN age ELSE #max END
FROM head1
If you want to use WHILE in your way, you need a ROW_NUMBER() field like this in your code:
SET #m = (SELECT h1.age
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY head1.age) AS rn
FROM head1) h1
WHERE h1.rn = #a;
instead of
select name, #m = age from head1 where ID = #a;
declare #a int, #m int, #maxo int
declare #name NVARCHAR(100)
set #maxo = 0;
while(#a<10)
begin
select #name = name, #m = age from head1 where ID = #a;
if #m>#maxo
SET #maxo = #m;
set #a=#a+1;
END
PRINT #name + ',' + CAST(#maxo AS NVARCHAR(50))
ALTERNATE WAY IS
SELECT Name, Age FROM Header1 WHERE Age = (SELECT MAX(Age) FROM Header1)

Group data without changing query flow

For me it's hard to explait what do I want so article's name may be unclear, but I hope I can describe it with code.
I have some data with two most important value, so let it be time t and value f(t). It's stored in the table, for example
1 - 1000
2 - 1200
3 - 1100
4 - 1500
...
I want to plot a graph using it, and this graph should contain N points. If table has rows less than this N, then we just return this table. But if it hasn't, we should group this points, for example, N = Count/2, then for an example above:
1 - (1000+1200)/2 = 1100
2 - (1100+1500)/2 = 1300
...
I wrote an SQL script (it works fine for N >> Count) (MonitoringDateTime - is t, and ResultCount if f(t))
ALTER PROCEDURE [dbo].[usp_GetRequestStatisticsData]
#ResourceTypeID bigint,
#DateFrom datetime,
#DateTo datetime,
#EstimatedPointCount int
AS
BEGIN
SET NOCOUNT ON;
SET ARITHABORT ON;
declare #groupSize int;
declare #resourceCount int;
select #resourceCount = Count(*)
from ResourceType
where ID & #ResourceTypeID > 0
SELECT d.ResultCount
,MonitoringDateTime = d.GeneratedOnUtc
,ResourceType = a.ResourceTypeID,
ROW_NUMBER() OVER(ORDER BY d.GeneratedOnUtc asc) AS Row
into #t
FROM dbo.AgentData d
INNER JOIN dbo.Agent a ON a.CheckID = d.CheckID
WHERE d.EventType = 'Result' AND
a.ResourceTypeID & #ResourceTypeID > 0 AND
d.GeneratedOnUtc between #DateFrom AND #DateTo AND
d.Result = 1
select #groupSize = Count(*) / (#EstimatedPointCount * #resourceCount)
from #t
if #groupSize = 0 -- return all points
select ResourceType, MonitoringDateTime, ResultCount
from #t
else
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
where [Row] % #groupSize = 0
group by ResourceType, [Row]
order by MonitoringDateTime
END
, but it's doesn't work for N ~= Count, and spend a lot of time for inserts.
This is why I wanted to use CTE's, but it doesn't work with if else statement.
So i calculated a formula for a group number (for use it in GroupBy clause), because we have
GroupNumber = Count < N ? Row : Row*NumberOfGroups
where Count - numer of rows in the table, and NumberOfGroups = Count/EstimatedPointCount
using some trivial mathematics we get a formula
GroupNumber = Row + (Row*Count/EstimatedPointCount - Row)*MAX(Count - Count/EstimatedPointCount,0)/(Count - Count/EstimatedPointCount)
but it doesn't work because of Count aggregate function:
Column 'dbo.AgentData.ResultCount' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
My english is very bad and I know it (and i'm trying to improve it), but hope dies last, so please advice.
results of query
SELECT d.ResultCount
, MonitoringDateTime = d.GeneratedOnUtc
, ResourceType = a.ResourceTypeID
FROM dbo.AgentData d
INNER JOIN dbo.Agent a ON a.CheckID = d.CheckID
WHERE d.GeneratedOnUtc between '2015-01-28' AND '2015-01-30' AND
a.ResourceTypeID & 1376256 > 0 AND
d.EventType = 'Result' AND
d.Result = 1
https://onedrive.live.com/redir?resid=58A31FC352FC3D1A!6118&authkey=!AATDebemNJIgHoo&ithint=file%2ccsv
Here's an example using NTILE and your simple sample data at the top of your question:
declare #samples table (ID int, sample int)
insert into #samples (ID,sample) values
(1,1000),
(2,1200),
(3,1100),
(4,1500)
declare #results int
set #results = 2
;With grouped as (
select *,NTILE(#results) OVER (order by ID) as nt
from #samples
)
select nt,AVG(sample) from grouped
group by nt
Which produces:
nt
-------------------- -----------
1 1100
2 1300
If #results is changed to 4 (or any higher number) then you just get back your original result set.
Unfortunately, I don't have your full data nor can I fully understand what you're trying to do with the full stored procedure, so the above would probably need to be adapted somewhat.
I haven't tried it, but how about instead of
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
where [Row] % #groupSize = 0
group by ResourceType, [Row]
order by MonitoringDateTime
perhaps something like
select ResourceType, CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
from #t
group by ResourceType, convert(int,[Row]/#groupSize)
order by MonitoringDateTime
Maybe that points you in some new direction? by converting to int we are truncating everything after the decimal so Im hoping that will give you a better grouping? you might need to put your row-number over resource type for this to work?