sql use while loop to fetch values and run query on them - sql

I have long query which is ran based on two value parameters (contcode, datime).
I am using this while loop to fetch these values in order from another table, run the query and insert the result into a final table:
DECLARE #r TABLE (id int IDENTITY (1,1),lnr char (9), pday datetime)
INSERT INTO #r (lnr, pday)
SELECT TOP 1 lnr, pday FROM memrepay
DECLARE #counter int SET #counter = 1
DECLARE #contcode char (9)
DECLARE #datime datetime
WHILE #counter <= (SELECT COUNT(*) FROM #r)
BEGIN
--long query
END
SET #counter = #counter + 1
END
SELECT * FROM #finaltable
However long query only works when I fetch 1 row:
SELECT TOP 1 contcode, datime FROM #tbl
And if I don't use TOP 1, I get the error message of:
"Subquery returned more than 1 value...."
How can I fetch those two or more values in order, run the long query based each , not getting this error?

I think you just need to move the SET #counter = #counter + 1 up between the BEGIN...END block under your WHILE loop. That's probably why it only works for TOP 1, since 1 is <= COUNT(*).
DECLARE #r TABLE (id int IDENTITY (1,1),lnr char (9), pday datetime)
INSERT INTO #r (lnr, pday)
SELECT TOP 1 lnr, pday FROM memrepay
DECLARE #counter int SET #counter = 1
DECLARE #contcode char (9)
DECLARE #datime datetime
WHILE #counter <= (SELECT COUNT(*) FROM #r)
BEGIN
--long query
SET #counter = #counter + 1
END
END
SELECT * FROM #finaltable

Related

sql accumulating decimals at the end

I need help with a simple query for dividing a value to equal numbers. But I also want to avoid decimals values to accumulate them all to the final value.This is achievable by query below, however if I choose different datatype such as MONEY or NUMERIC for #amt, it gets tricky. Can anyone do what I did below with MONEY or NUMERIC datatype?
DECLARE #tbl TABLE (id TINYINT, princ NUMERIC (14,2))
DECLARE #counter INT = 1
DECLARE #term TINYINT = 12
DECLARE #amt INT = 50000
DECLARE #decimals INT = #amt-((#amt/#term)*#term)
WHILE #counter <= #term
BEGIN
INSERT INTO #tbl (id,princ)
SELECT #counter, #amt/#term
SET #counter = #counter + 1
UPDATE #tbl SET princ = princ+#decimals WHERE id = #term
END
SELECT * FROM #tbl
<Do what you need to do you populate the table, excluding the last row>
INSERT INTO
#tbl (
id,
princ
)
SELECT
MAX(id) + 1,
#amt - SUM(princ)
FROM
#tbl
Or, perhaps, with a CTE...
WITH
recursive_distribution AS
(
SELECT 0 AS id, CAST(#amt / #term AS INT) AS princ
UNION ALL
SELECT id + 1, princ
FROM recursive_distribution
WHERE id < #term - 1
UNION ALL
SELECT 1, #amt - (princ * (#term-1))
FROM recursive_distribution
WHERE id = #term - 1
)
INSERT INTO
#tbl (id, princ)
SELECT
id, princ
FROM
recursive_distribution
WHERE
id > 0
OPTION
(MAXRECURSION 0) -- Only needed if #term >= 100
(Starting at 0, then excluding row 0 means this can cope with #term = 1)

Insert query in while loop in a function

I need to write a SQL function that should return temp table with 2 columns. But I want to use while loop. I want to insert multiple insert queries to temp table.
But when I am using insert query in while loop in SQL function, it is giving the empty result. Following is my case.
Code something like this:
create function dbo.fn_GetSubTree1(#type as Varchar(50))
returns #tree table
(sizename Varchar(9) not null,shiptotal int)
as
BEGIN
Declare #maxsize int;
Declare #counter int=0;
WHILE #counter < 24
BEGIN
insert into #tree(sizename,shiptotal) select s.size,s.shp from style st join scale s on st.scale=s.scale and s.nrfkey='' and s.prepak=''
SET #counter = #counter + 1;
END
return
End
You have WHILE #counter < 24, but never initialise the counter to any value.
The line DECLARE #counter INT; doesn't initialise the variable to 0, it initialises it to NULL. Which means your first loop is checking NULL < 24 which isn't TRUE.
Try this...
Declare #counter int = 0;
...
WHILE #counter < 24
BEGIN
...
SET #counter = #counter + 1;
END
You also seem to have other issues, such as select ' + #Size + ','+#Shp + ' from ... making no apparent sense.
EDIT:
I recommend starting with making a simple case work and building it up from there. If it ever stops working, the problem is what ever you last changed.
CREATE FUNCTION dbo.fn_GetSubTree1(
#type AS NVARCHAR(50)
)
RETURNS #tree TABLE (
sizename NVARCHAR(9) NOT NULL,
shiptotal INT
)
AS
BEGIN
DECLARE #counter INT = 0;
WHILE (#counter < 24)
BEGIN
INSERT INTO
#tree(
sizename,
shiptotal
)
VALUES (
'Test',
#counter
)
SET #counter = #counter + 1;
END
RETURN
END

How can I replace iterative execution of stored procedure with anything in SQL?

There is a scenario wherein a SP is executed inside a WHILE loop. I want a solution which can prevent iterative execution of SP but at the same time getting exact same result.
Below code snippet could be an example. Consider SP uspMyProc is updating tables based on the parameter it gets.
DECLARE #table TABLE
(
id INT IDENTITY
, Value INT
)
DECLARE #counter INT = 1
, #maxcount INT
, #value INT
INSERT INTO #table
VALUES ( 1 ),
( 11 ),
( 3 ),
( 23 ),
( 2 )
SELECT #maxcount = COUNT(*)
FROM #table
WHILE #counter <= #maxcount
BEGIN
SELECT #value = Value
FROM #table
WHERE id = #counter
EXECUTE uspMyProc #id = #value
SET #counter = #counter + 1
END

How does the while loop work?

declare #t INT = 1
declare #i INT = 1
while #t <= 5
BEGIN
while #i <= 40
BEGIN
set #i = #i + 1
END
set #t = #t + 1
select #t
END
The result I get is #t=2. If I replace it with #i I get #i=41. Why does the #variable in the first while loop show 2, does it brake? Should it not show#t=6?
If I put the select #t at the end it will show 6, but what if you need every result from the iteration, you would not get the others results if you put the #t after the end, you would only get the last result. I would like the results 1,2,3,4,5,6 all go through #t ,and the end result should show only 6.
This is simplified example of a procedure that i have written, for every #t iteration the #i makes a full iteration, and then #t should go for the second iteration. #i works as planned, but the #t breaks after the first itteration.
(if #t = 1 , #i gets 40 rows from column 1 from some table, #t = 2 it gets 40 rows from column 2 from some table..ect) and writes it in #sql string, when she gets to 40 while loop breaks and goes into the #t loop, the #t should execute the #sql string(example it creates a view, so at the end i should have 6 views, but after the first execute the #t loop breaks.)
You can store your result in a table variable.
DECLARE #t INT = 1
DECLARE #i INT = 1
DECLARE #Result table(Id int)
while #t <= 5
BEGIN
while #i <= 40
BEGIN
set #i = #i + 1
END
INSERT INTO #Result(Id) values(#t)
set #t = #t + 1
END
select Id FROM #Result
As I understand, you should have:
DECLARE #t INT = 1
DECLARE #i INT = 1
while #t <= 5
BEGIN
while #i <= 40
BEGIN
set #i = #i + 1
END
set #t = #t + 1
--select #t <-- this is breaking the loop before it ends
END
select #t

How to insert split-ed amount into a table without using loop?

I need to split an amount into multiple part and insert into an table called installment, how can i implement it without using loop?
declare #installment as table (installment_index int identity(1,1),
amount money,
due_date datetime)
declare #total_amount money
declare #number_of_installment int
declare #amount money
declare #i int
declare #date datetime
set #date = getdate()
set #number_of_installment = 20
set #total_amount = 5001.00
set #amount = #total_amount / #number_of_installment
set #i= 1
while #i <= #number_of_installment
begin
insert into #installment
(amount,due_date) values (#amount, dateadd(month,#i,#date))
set #i = #i + 1
end
This would replace while loop:
;with numbers as (
select 1 number
union all
select number + 1
from numbers
where number < #number_of_installment
)
insert into #installment (amount,due_date)
select #amount, dateadd(month,number,#date)
from numbers
option (maxrecursion 0)
CTE numbers returns table of numbers from 1 to #number_of_installment
insert uses this table to insert #number_of_installment records to #installment.
EDIT:
I must mention that, according to this article, nothing beats auxiliary table of numbers/dates for similar purposes.