This nested while loop is only successfully executing the lowest level loop. It refuses to add 1 to #wKey even though I tell it to SET #wKey = #wKey + 1.... What am I doing wrong here? Does SQL not allow nested loops?
DECLARE #fMinKey INT;
DECLARE #fMaxKey INT;
DECLARE #wMaxKey INT;
DECLARE #wKey INT;
DECLARE #wDate date;
DECLARE #fcStart DATE;
SET #fMinKey = (select min([fcKey]) from dbo.fact_Fc);
SET #fMaxKey = (select max([fcKey]) from dw.fact_Fc);
SET #wMaxKey = (select max([WellKey]) from dw.fact_Fc);
SET #wKey = 1;
SET #wDate =
(select min([fapDate]) from dbo.dim_W where [Key] = #wKey);
SET #fcStart =
(select min([Date]) from dw.fact_Fc where [wKey] = #wKey);
WHILE (#fMinKey <= #fMaxKey)
BEGIN
WHILE (#wKey <= #wMaxKey)
BEGIN
WHILE (#wDate < #fcStart)
BEGIN
INSERT INTO dw.fact_FcTemp2 ([wKey], [Date], [pAmount], [fcKey], [AddedDate])
VALUES (#wKey, #wDate, 0, #fMinKey, CURRENT_TIMESTAMP)
SET #wDate = dateadd(DAY, 1, #wDate)
END
SET #wKey = #wKey + 1
END
SET #fMinKey = #fMinKey + 1
END
The resulting table is only showing [wKey] = 1, but it should have rows for multiple different wKeys
Once when #wDate reach #fcStart looks like you never reset #wDate to initial state
So next loop run again
You need some how to reset #wDate for next loop
Also having 3 loops perhaps is miss of design, sql performances does not like loops at all.
Can you show us data structure and needed result maybe tehe is way to constuct sql whitout 3 nested loops
Related
This is redshift. I have an update command that looks like this:
UPDATE users
SET birthday = temp_users.birth_date
FROM public.users as gpu
INNER JOIN temp_schema.users_birth_dates_with_row_numbers AS temp_users ON gpu.id = temp_users.id
WHERE mod(temp_users.row_id, 10) = 0 -- this is what I want to change from 0 -> 9
;
For context: I want to run this time times with a different modulo number. How can I do this? Row id is basically created with the row_number function and I want to do this in batches so that it doesn't hold any locks for too long.
SQL WHILE loop syntax and example
The syntax of the WHILE loop in SQL looks like as follows:
WHILE condition BEGIN {...statements...} END
Example:
DECLARE #Counter INT
SET #Counter=0
WHILE ( #Counter <= 9)
BEGIN
PRINT 'The counter value is = ' + CONVERT(VARCHAR,#Counter)
SET #Counter = #Counter + 1
END
I am trying to do an update on a specific record every 1000 rows using Postgres. I am looking for a better way to do that. My function is described below:
CREATE OR REPLACE FUNCTION update_row()
RETURNS void AS
$BODY$
declare
myUID integer;
nRow integer;
maxUid integer;
BEGIN
nRow:=1000;
select max(uid_atm_inp) from tab into maxUid where field1 = '1240200';
loop
if (nRow > 1000 and nRow < maxUid) then
select uid from tab into myUID where field1 = '1240200' and uid >= nRow limit 1;
update tab
set field = 'xxx'
where field1 = '1240200' and uid = myUID;
nRow:=nRow+1000;
end if;
end loop;
END; $BODY$
LANGUAGE plpgsql VOLATILE
How can I improve this procedure? I think there is something wrong. The loop does not end and takes too much time.
To perform this task in SQL, you could use the row_number window function and update only those rows where the number is divisible by 1000.
Your loop doesn't finish because there is no EXIT or RETURN in it.
I doubt you could ever rival the performance of a standard SQL update with a procedural loop. Instead of doing it a row at a time, just do it all as a single statement:
with t2 as (
select
uid, row_number() over (order by 1) as rn
from tab
where field1 = '1240200'
)
update tab t1
set field = 'xxx'
from t2
where
t1.uid = t2.uid and
mod (t2.rn, 1000) = 0
Per my comment, I am presupposing what you mean by "every 1000th row," as without some designation of how to determine what tuple is what row number. That is easily edited by changing the "order by" criteria.
Adding a second where clause on the update (t1.field1 = '1240200') can't hurt but might not be necessary if these are nested loop.
This might be notionally similar to what Laurenz has in mind.
I solved this way:
declare
myUID integer;
nRow integer;
rowNum integer;
checkrow integer;
myString varchar(272);
cur_check_row cursor for select uid , row_number() over (order by 1) as rn, substr(fieldxx,1,244)
from table where field1 = '1240200' and uid >= 1000 ORDER BY uid;
BEGIN
open cur_check_row;
loop
fetch cur_check_row into myUID, rowNum, myString;
EXIT WHEN NOT FOUND;
select mod(rowNum, 1000) into checkrow;
if checkrow = 0 then
update table
set fieldxx= myString||'O'
where uid in (myUID);
end if;
end loop;
close cur_check_row;
Anyone have a sample of how to do a WHILE Loop in 2005 SQL without using a cursor? I'd like to loop through based on a counter
You can do:
DECLARE #i INT = 10
WHILE #i > 0
BEGIN
SELECT #i -- Output: 10, 9, 8, etc...
SET #i = #i - 1
END
But you should first examine more carefully if what you are trying to do can be achieved using set based operations.
DECLARE #intFlag INT
SET #intFlag = 1
WHILE (#intFlag <=10000)
BEGIN
PRINT #intFlag
-- DO YOUR WORK HERE
SET #intFlag = #intFlag + 1
END
GO
i am using if else statement and ,i want to drop the temporary table which is created out side the inner if statement , but when i execute the statement , i get the answer as , There is already an object named '#table' in the database. ---------------------MY CODE IS LIKE enter code here
DECLARE #RowCount INT;
SET #RowCount = 1;
IF #RowCount = 1
BEGIN
IF OBJECT_ID('tempdb..#Guarantor_Details') IS NOT NULL
DROP TABLE #Guarantor_Details
SELECT DISTINCT TOP 1 GUARANTORS.GUARANTOR_CODE
INTO #Guarantor_Details
FROM GUARANTORS
WHERE ISNULL(GUARANTORS.deleted, 0) <> 1;
DECLARE #GuarantorCount INT;
SELECT #GuarantorCount = count(*)
FROM #Guarantor_Details;
IF (#GuarantorCount = 0)
DROP TABLE #Guarantor_Details
BEGIN
IF OBJECT_ID('tempdb..#Guarantor_Details') IS NOT NULL
SELECT TOP 1 CLIENT.Last_Name
INTO #Guarantor_Details
FROM CLIENT
LEFT JOIN GUARANTORS ON GUARANTORS.GUARANTOR_CODE = CLIENT.Financial_Guarantor
END
END
------------------------------------------------------------------------
You don't need to do this type of CREATE / DROP. You can (and probably should) create the table before the loop and simply either TRUNCATE TABLE or DELETE FROM the temp table when necessary to "reset" it. Just pick a datatype that fits both GUARANTOR_CODE and Last_Name.
You also don't need to do a SELECT COUNT(*) on the table when you have the ##ROWCOUNT from the INSERT operation that already has that value in it.
Essentially:
CREATE TABLE #Guarantor_Details (Value NVARCHAR(50));
WHILE (something)
BEGIN
IF (#RowCount = 1)
BEGIN
TRUNCATE TABLE #Guarantor_Details;
INSERT INTO #Guarantor_Details (Value)
SELECT DISTINCT TOP 1 GUARANTORS.GUARANTOR_CODE
FROM GUARANTORS
WHERE ISNULL(GUARANTORS.deleted, 0) <> 1;
IF (##ROWCOUNT = 0)
BEGIN
INSERT INTO #Guarantor_Details (Value)
SELECT TOP 1 CLIENT.Last_Name
FROM CLIENT
LEFT JOIN GUARANTORS
ON GUARANTORS.GUARANTOR_CODE = CLIENT.Financial_Guarantor;
END;
END;
FYI, there appear to be some logic problems in the original code:
There are BEGIN / END tags at the bottom of the IF #RowCount = 1 block that aren't associated to any IF or WHILE. This is technically ok, but usually implies something is missing.
If this code is all of the code, then the IF #RowCount = 1 doesn't work in the loop unless something sets #RowCount past the end of this outer IF block that is not shown here in the question.
I have insert statements (simplified) in a SPROC like the following
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
-- EDIT: Realized forgot to include this following vital line that is causing issue
SET #var = #var + ##ROWCOUNT
-- ##ROWCOUNT now takes on a value of 1, which will cause the following IF check to fail even when no lines are inserted
IF(##ROWCOUNT = 0)
BEGIN
BREAK
END
END
But the issue is, after any operation even when no more rows fit my some_condition, ##ROWCOUNT is equal to 1, not 0.
How can I break that loop when there are 0 rows returned matching my some_condition?
The "set" statement creates a row count of 1. What you should do is immediately save ##ROWCOUNT into a #rowCount variable and use that var later on.
declare #rowCount int
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
-- EDIT: Realized forgot to include this following vital line that is causing issue
SET #rowCount = ##ROWCOUNT
SET #var = #var + #rowCount
-- ##ROWCOUNT now takes on a value of 1, which will cause the following IF check to fail even when no lines are inserted
IF(#rowCount = 0)
BEGIN
BREAK
END
END
Also, you can simplify by setting #rowCount to -1 initially and changing the WHILE condition to #rowCount <> 0. The conditional BREAK will no longer be needed.
An alternative solution. This checks each iteration to see if the ID of the last inserted record has changed or not. If it hasn't changed, it indicates that no records were added that iteration.
SET ROWCOUNT 100
declare #id int;
WHILE(1=1)
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
IF(#id= ##identity)
BEGIN
BREAK
END
set #id = ##identity;
END
Try this solutions:
1st solution
Using ##ROWCOUNT in loop's condition.
SET ROWCOUNT 100
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
WHILE(##ROWCOUNT > 0)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
END
2nd solition
Using goto.
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
IF(##ROWCOUNT = 0)
BEGIN
goto label
END
END
label1:
print 'After lopp'
I think you should use select to get the ##rowcount into a variable. try this:
declare #number_of_rows int
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
SELECT #number_of_rows=##ROWCOUNT
IF (#number_of_rows = 0)
BEGIN
BREAK
END
END
Implemented solution similar to Moho, but used SELECT instead of SET to store ##ROWCOUNT.