How to loop through 0-9 in a sql query? - amazon-redshift

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

Related

Postgres 9.6 How to loop over array and insert each array value into tables?

I can't seem to figure this out, the following is an attempt to get an array of items to run as foreach loop in postgres using pgadmin query tool, for each item x in the array, do 2 insert statements where x is each array value:
I tried this based on https://www.postgresql.org/docs/current/plpgsql-control-structures.html#PLPGSQL-FOREACH-ARRAY section 42.6.7. Looping through Arrays, here is some pseudocode:
DO
$do$
BEGIN
FOREACH x IN ['Apple','Banana','Cantaloupe']
LOOP
INSERT INTO foods.fruits(id, comment, fruit_name)
VALUES ((SELECT MAX(id) + 1 FROM foods.fruits), 'a comment', x);
INSERT INTO foods.fruits_ordered(id, price, fruit_id)
VALUES ((SELECT MAX(id) + 1 FROM foods.fruits_ordered), '5.00', (SELECT id FROM foods.fruits WHERE fruit_name = x));
END LOOP;
END
$do$;
This should run the loop 3 times and do a total of 6 insertions.
Any idea how to get this to work?
this ['Apple','Banana','Cantaloupe'] may not a correct array definition, this should be more like array['Apple','Banana','Cantaloupe']::varchar[]
Fixed loop could be something like following
...
FOREACH x IN array(select array['Apple','Banana','Cantaloupe']::varchar[])
...
Next, that x should be declared before block is started(begin), as following
DO
$do$
/*
type could be text
Better be based on target field should be inserted as
foods.fruits.fruit_name%type
*/
declare x varchar(31);
declare res text;
BEGIN
...
final script could be something like
DO
$do$
declare x _names.n%type;
BEGIN
FOREACH x IN array(select array['Apple','Banana','Cantaloupe']::varchar[])
LOOP
/*inserts here...*/
END LOOP;
END
$do$;
Another option is using unnest function, that pivot an array into rows.
e.g.
select array[9,1,1,9,9,2]::int[]
/*
{9,1,1,9,9,2}
*/
select unnest("array") from (select array[9,1,1,9,9,2]::int[] as "array") as "req"
/*
9
1
1
9
9
2
*/
Loop over the select using for x in select ...

Update table every 1000 rows

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;

What is wrong with this SQL WHILE LOOP?

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

SQL While Loop in 2005

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

TSQL set the column value of a table variable

I am trying to set individual columns of a table variable iteratively as follows:
declare #reg_data table
(
I int NOT NULL PRIMARY KEY IDENTITY,
Y float
)
declare #counter int, #numRows int
SET #counter = 0
SET #numRows = (select MAX(val) + 10 from tableY)
WHILE #counter < numRows
BEGIN
SET #reg_data.Y = dbo.func1(#counter) --HOW DO I DO THIS!!!
#counter = #counter + 1
END
The above does not work because you cannot access table variables like an array. How can I obtain the following functionality?
You can't set values in records that doesn't exists, so you need an insert:
WHILE #counter < numRows
BEGIN
INSERT INTO #reg_data (Y) values (dbo.func1(#counter))
#counter = #counter + 1
END
Just for the completeness a one-statement example with a CTE and no looping:
DECLARE #reg_data TABLE (
I INT NOT NULL PRIMARY KEY IDENTITY,
Y FLOAT
);
WITH cteNum AS (
SELECT MAX(val) + 10 AS val
FROM #tableY
HAVING MAX(val) >= 0
UNION ALL
SELECT val-1
FROM cteNum
WHERE val > 0
)
INSERT #reg_data(Y)
SELECT dbo.func1(val)
FROM cteNum
OPTION (MAXRECURSION 0);
Why use a cursor for this at all??
Why don't you just write some UPDATE statements:
WHILE #counter < numRows
BEGIN
UPDATE #reg_data
SET Y = dbo.func1(#counter)
WHERE I = #counter
SET #counter = #counter + 1
END
You need to somehow be able to read out the identifying values (the I) from the table variable, so you can use that in your UPDATE statement to apply the update to exactly one row (that's identified by that value of I)