Dynamically name variable from for loop - oracle

I am writing a procedure in oracle, when I am getting a variable as rowtype.
I am trying to achieve a test condition based on for loop
FOR idx in 1..10 LOOP
IF POST.SI_AMOUNT||idex <> 0 THEN
NULL;
END IF;
END LOOP;
I have columns in table like this
SI_AMOUNT1,
SI_AMOUNT2,
SI_AMOUNT3,
SI_AMOUNT4,
SI_AMOUNT5,
SI_AMOUNT6,
SI_AMOUNT7,
SI_AMOUNT8,
SI_AMOUNT9,
SI_AMOUNT10
I want to check all columns value by using for loop. is this possible in oracle?

First of all, it looks like your table design violates the First normal form. If you fix this error, I guess, you'll lose a need to iterate throw a columns in such manner.
Next, statement POST.SI_AMOUNT||idex <> 0 you had wrote, means Take the value of POST.SI_AMOUNT variable, concatenate it with the value of idex variable and compare concatenation result with zero using implicit datatype conversion.
At last, PL/SQL, as other non-script languages, have no ability to iterate throw variables list. There are no any ability to do this simplier than direct use of ten IF conditions, but the best way, as I said, is to redesign the table and to eliminate this need at all.

Related

Reserve Number in Oracle Sequence Generator

We have requirement to skip few defined number while generating the new number using Oracle Sequence Generator.
For Eg:- We have range defined from 0001-9999 but in this range, there are numbers 0011, 0020, 2056, 6547 never to be generated by Oracle sequencer.
Is it possible to do?
Is it possible to do?
Sort of ... just start the sequence at 2057 and stop it at 6546 then you will never generate any of those values.
If you want to start at 1 and go up to 9999 skipping those values then, no, there is nothing in an Oracle sequence that allows you to do that.
You can't do that purely within the built-in sequence mechanism. As a workaround you could have a wrapper function which skips the banned values, e.g.:
CREATE FUNCTION get_seq RETURN NUMBER IS
seq_val PLS_INTEGER := 11; -- any banned value
BEGIN
WHILE seq_val IN (11, 20, 2056, 6547) LOOP
seq_val := my_seq.nextval;
END LOOP;
RETURN seq_val;
END;
/
and then call get_seq in place of my_seq.nextval.
db<>fiddle demp with a smaller range, showing it skips values.
There are downsides of course; you may see a noticeable performance hit from the context switching, though with such a small range you presumably aren't going to be calling this very intensely. And if your banned numbers change you have to change the function, or have the function look them up from somewhere else - which you might need or want to anyway if you're avoiding existing PK values, say. Having to refer to different things to retrieve nextval (indirectly through the function) and currval (directly from the sequence) might be confusing. Etc. But it's an option...
If you are avoiding existing PK values then another option is to stick to a plain sequence and insert in a loop that exits when you don't get a constraint error - i.e. usually after one iteration. But it isn't clear what you are really trying to achieve.

Nested implicit cursor not clearin data

I have an oracle pl/sql anonymous block with an implicit cursor in a for loop that is nested on a for loop in this way:
FOR secuence IN 1..3 LOOP
FOR registro_notificacion IN (
SELECT 'data' FROM my_table WHERE my_table.column1=secuence)
LOOP
--work with data
END LOOP; END LOOP;
The problems occurs when I have values returned on secuence=1 and empty on the others because for some reason the implicit cursor doesn´t clean itself.
So, on the secuence=2 im suposed to don't do nothing because there is no data, but for some reason it still have the data returned on the first loop (secuence=1).
Should I declare and explicit cursor to close at the end of every iteration? Although I consider to use 3 for loops, one for every secuence value but that its not the idea I guess.
I use cursor for-loops all the time and have never seen the situation you describe. The only thing I can think is that you have a variable named secuence declared elsewhere in your program that happens to be set to 1. In this case, the PL/SQL compiler may be choosing to use the global variable secuence rather than the loop-control variable secuence when generating the SQL statement. A good rule of thumb is to use a unique name for each loop-control variable.
However, you can get rid of the FOR secuence... loop entirely by using:
FOR registro_notificacion IN (SELECT 'data'
FROM my_table
WHERE my_table.column1 BETWEEN 1 AND 3
ORDER BY my_table.column1)
LOOP
--work with data
END LOOP;
Opening one cursor is generally less costly than opening three cursors.
Best of luck.

Inserting/Updating numeric string in Sqlite with Ruby (Newbie query)

I have a simple Sqlite table with 2 columns for a telephone number and a counter. I want to update the table on the basis of .csv files that also contain telephone numbers and counters. If the number exists in the database it should be updated by the sum of the existing counter + the counter in the file. If it doesn't exist a new record should be inserted with the value from the file.
My one remaining problem is that the telephone numbers have a zero in the first position.
When I populate the db the zero is retained, (I can manually select and find an existing number like 09999) when I fetch the values from the file the zero is retained but when I try to insert/update something happens in my Ruby code that inserts a new record without the leading zero, so 0999 becomes 999 in the db. Numbers without leading zeros are handled correctly.
My code looks like this:
rowArray=thisFile[k].split(';')
number = rowArray[0]
couplings = rowArray[1]
updString="INSERT OR REPLACE INTO Caller (Telno,count) VALUES (#{number},COALESCE((SELECT count + #{couplings} FROM Caller WHERE Telno=#{number}),# {couplings}))"
db.execute(updString)
Any idea what I'm doing wrong here? The easiest solution would be to drop the leading zero but I would prefer to do it right. Many thanks in advance.
You need to use placeholders in your prepare call and pass the actual values in a call to execute. Like this
insert = db.prepare(<<__SQL__)
INSERT OR REPLACE INTO Caller (Telno, count)
VALUES (:number, COALESCE((SELECT count + :couplings FROM Caller WHERE Telno = :number), :couplings))
__SQL__
insert.execute(number: number, couplings: couplings)
(Note that :number and :couplings in the SQL statement are named placeholders. They can be anything, but I have chosen them to match the corresponding names of the variables that are to be bound.)
The problem is that, using simple interpolation, you end up with a string like
INSERT OR REPLACE INTO Caller (Telno, count) VALUES (0999, ...
and the 0999 appears to be a number rather than a string. If you pass strings to execute then the variables will be bound with the correct type.

Dynamic SQL, comparing two records one column at a time

Scenario: we have flashback set up on certain tables in a Oracle database. Every now and then, we want to see what fields changed from one row to another. We can inspect visually of course but that is error-prone.
So I had the "brilliant" idea to try to step through the rows, store the current record into one record variable, and the prior record into another one. Then, field-by-field, compare each field, and if different, print out the field name and the values. Something like this:
DECLARE CURSOR myflash IS SELECT * FROM myflashtable;
OLDRECORD myflashtable%ROWTYPE;
NEWRECORD myflashtable%ROWTYPE;
dynamic_statement varchar2(4000);
cursor colnames is select * from all_tab_columns where table_name = 'myflashtable';
begin
if not myflash%ISOPEN then
open myflash;
end if;
fetch myflash into NEWRECORD;
while myflash%FOUND loop;
for columnnames in colnames loop
/* cobble together dynamic SQL along the lines of
"if oldrecord.column_name != newrecord.column_name
then print some information``....end if;"
*/
execute immediate dynamic_statement;
end loop;
OLDRECORD := NEWRECORD;
fetch myflash into NEWRECORD;
end loop;
end;
Naturally this didn't work. Initially it gave me "invalid SQL statement" and I added begin/end onto the dynamic SQL. When I tried running that version, it gave me an error because it doesn't know about the old/new records. When I run without doing the execute, but just dumping the generated SQL, it is stepping through all the columns on each of the records, so that part of the logic is working.
I'm quite sure there's a better way to do this, or perhaps to make it work. One thought was to do something like declaring old/new value variables, then using dynamic SQL to move the old/new record fields to each of those:
EXECUTE IMMEDIATE 'oldvalue := OLDRECORD.'||columnnames.column_name;
EXECUTE IMMEDIATE 'newvalue := NEWRECORD.'||columnnames.column_name;
IF oldvalue != newvalue then
/* print some stuff */
END IF:
but of course the trick is that the target variable would have to handle columns of a bunch of different types - char, date, etc. So there'd need to be variants of old/newvalue variables, and logic to handle that, and it was turning into not-so-much-fun.
Any suggestions for a more elegant way to do this? I've checked around the site and haven't had much like finding anything that quite seemed like what I'm trying to do.
You are on the right track. But it is quite some more programming work to do. Read the old and new table in a join linking it with the correct primary key and loop through it. You can use DMBS_SQL package to build a dynamic cursor and loop through the tables.

how to fetch unknown column in oracle

Is there an operator in Oracle that fetch a column of a row in a result set without having to specify the column's name. I want something like:
for row in (select * from table1)
loop
for col in row
loop
// do stuff with col
end loop;
end loop;
Thank you.
There is no built in function for this. You will have to use DBMS_SQL package in order to use dynamic SQL : http://docstore.mik.ua/orelly/oracle/bipack/ch02_02.htm
In static SQL, no.
If you were using dynamic SQL (via DBMS_SQL), you could describe the statement and iterate over the columns. But if the query itself is not dynamic, it would almost certainly increase the complexity of the code and decrease the maintainability if you were to move toward using DBMS_SQL rather than simply coding the column names that you're interested in.

Resources