Not understanding cursor - plsql

The problem i'm dealing with is follow :
I have a table students with name and surname. I create a new table Students2, and i want to fill this table with names and surnames from the first table with cursor.
I make the declare of the cursor, the select part, but in the LOOP PART, i don't understand how you fetch the values. For example, if I do:
FETCH name_surname INTO Students2.name,Students2.surname
it doesn't work, and I don't know how to fix it.
In name_surname I mention, I Selected the name and surname from the first table.

"Declare cursor c1 is select * from student2;
Begin
For i in c1 loop
Dbms_output.put_line(i.fname||','||i.lanme);
End loop;
End;"

Related

Inserting from an object into another object

From table abc i am inserting values in the object abc_type now i'm trying
to insert form abc_type to abc_second object on some condition.While doing
his i'm getting error that this is not a table.Is it even possible to fetch
data from an object and insert into another one.
create table abc(id number,name varchar2(50));
create or replace type abc_obj as object(id number,name varchar2(50) ) ;
create or replace type abc_ref as table of abc_obj;
declare
abc_type abc_ref := abc_ref();
abc_second abc_ref := abc_ref();
begin
select abc_obj(id ,name)
bulk collect into abc_type
from abc;
insert into table(abc_second) select * from abc_type where id=1;
end;
Unfortunately, Oracle user the term "table" in 3, or more, totally different contexts. When you "create table ...' you build the definition of an object in which to persist data, this is the normal use for the term. However, when you use the form "... table of ...' you define an pl/sql collection (array) for holding data inside pl/sql. In this case you have created a "nested table" (3rd use of table). (Note: Some collection types can be declared as column attributes on tables.)
While not identical there are multiple issues with your object definitions as well.
You did not explain the intended use for "second_table" but it seems you merely a
copy of the data from "abc". This can be achieved in multiple ways. If it is basically a one time process then just
create table second_table as select * from abc;
If this is an ongoing action then
create table second_table as select * from abc where 1=0;
-- then when ever needed
insert into second_table select * from abc;
If neither of these satisfy your intended use please expand your question to explain the intended use.

PLSQL: How to update and Insert together when match found between source and target table

I have a source and a target table. I need to make update and insert together in final table with following conditions:
1) When SourceTable.ProductName = TargetTable.ProductName and SourceTable.Amount = TargetTable.Amount Then
Update TargetTable set TargetTable.ValidFrom = sysdate
Also i need to insert like this,
Insert Into TargetTable(ProductName,Amount,ValidFrom,Version) Values(TargetTable.ProductName,TargetTable.Amount, sysdate, TargetTable.Version + 1)
This is to maintain version.
2) When there is no match then i need to insert directly like this,
Insert Into TargetTable(ProductName,Amount,ValidFrom,Version) Values(SourceTable.ProductName,SourceTable.Amount, sysdate,1)
As of now i am thinking of capturing these matching records in a Cursor and perform Update and Insert together in cursor loop. But i am willing to avoid loops a i have approx 10 millions of matching records.
We also have Merge statement, but I believe Merge do not support Update and Insert together in single "WHEN MATCHED" Clause. Please correct me if i am wrong?
Kindly suggest the best way to achieve this behavior.

SQLite: How to UPDATE column using count(*) range?

I'm not sure I'm using right terminology here.
Basically I want to update entire "id" column using count(*) [485] as a delimiter, in an ascending order, so the resulting row value will correspond with rownumber (not the rowid).
If I understand you correctly, this should work for you:
UPDATE tbl_name SET id=rowid
EDIT
If that's is the case -> then it's a lit bit more tricky, since SQlite doesn't support variables declaration.
So what I suggest is,
To create temporary table from select of your original table which makes it's rowids to be as row numbers 1,2,3 etc...
Set it's rowNum (the needed row number column) as each rowid
Then replace the original table with it.
Like this: (assume original table called orig_name)
CREATE TABLE tmp_tbl AS SELECT rowNum FROM orig_name;
UPDATE tmp_tbl SET rowNum=rowid;
DROP TABLE orig_name;
CREATE TABLE orig_name AS SELECT rowNum FROM tmp_tbl;
DROP TABLE tmp_tbl;
Try this: http://www.sqlite.org/lang_createtable.html#rowid
You can use some inner database variables such as rowid, oid etc to get what you need.
[edit]
Think I just understood what you meant, you want for each insert action, add a value that is the total count of rows currently in the table?
If so, try something like this:
UPDATE tbl_name
SET id = (select count(*) from tbl_name)
WHERE yada_yada_yada

PL/SQL Triggers to update selected rows in column in another table

I'm trying to create a trigger for my 'rentals' table that will change the value in a certain column ('rent_avail') from another table when an update occurs in the former table. The idea is simple: when a dvd is returned to the dvd store (i.e. date_return has a date value, the dvd has now become available again for renting. Thus, my 'rent_avail' column for the record (dvd) in question should reflect this by being set to 'Y' (the alternative is 'null' when the dvd is currently being rented out). My trigger is being created without errors, but after an insert on the date_return column, all values in my DVD table are being changed. I want to know how can I simply modify the column values in 'rent_avail' column in my dvd table for only the row being updated! This is probably very trivial, but I have researched it and can't seem to find a solution easily..
CREATE OR REPLACE TRIGGER RENTAL_RETURNS
AFTER UPDATE OF DATE_RETURN ON RENTAL
FOR EACH ROW
BEGIN
IF :OLD.DATE.RETURN IS NULL AND :NEW.DATE_RETURN IS NOT NULL THEN
UPDATE DVD SET RENT_AVAIL = 'Y' WHERE DVD.DVD_ID = DVD_ID;
END IF;
END;
/
your update statement is not picking dvd_id from parent table rebtal, but evaluating like where dvd_id = dvd_id which will always be TRUE. Just ad :OLD qualifier and you should be good, considering this is same column name (dvd_id) in rental table.
CREATE OR REPLACE TRIGGER RENTAL_RETURNS
AFTER UPDATE OF DATE_RETURN ON RENTAL
FOR EACH ROW
BEGIN
IF :OLD.DATE.RETURN IS NULL AND :NEW.DATE_RETURN IS NOT NULL THEN
UPDATE DVD SET RENT_AVAIL = 'Y' WHERE DVD.DVD_ID = :OLD.DVD_ID;
END IF;
END;
It looks to me like there's a period where there should be an underscore in the IF statement - I think it should read
IF :OLD.DATE_RETURN IS NULL AND :NEW.DATE_RETURN IS NOT NULL THEN`
Share and enjoy.

How do I add a new column in between two columns?

I have a table with columns name, qty, rate. I need to add a new column COLNew in between the name and qty columns. How do I add a new column in between two columns?
You have two options.
First, you could simply add a new column with the following:
ALTER TABLE {tableName} ADD COLUMN COLNew {type};
Second, and more complicatedly, but would actually put the column where you want it, would be to create the new table with the missing column and a temporary new name:
CREATE TABLE {tempNewTableName} (name TEXT, COLNew {type} DEFAULT {defaultValue}, qty INTEGER, rate REAL);
And populate it with the old data:
INSERT INTO {tempNewTableName} (name, qty, rate) SELECT name, qty, rate FROM OldTable;
Then delete the old table:
DROP TABLE OldTable;
Then rename the new table to have the name of the OldTable:
ALTER TABLE {tempNewTableName} RENAME TO OldTable;
I'd much prefer the second option, as it will allow you to completely rename everything if need be.
You don't add columns between other columns in SQL, you just add them. Where they're put is totally up to the DBMS. The right place to ensure that columns come out in the correct order is when you select them.
In other words, if you want them in the order {name,colnew,qty,rate}, you use:
select name, colnew, qty, rate from ...
With SQLite, you need to use alter table, an example being:
alter table mytable add column colnew char(50)
You can add new column with the query
ALTER TABLE TableName ADD COLUMN COLNew CHAR(25)
But it will be added at the end, not in between the existing columns.
SQLite has limited ALTER TABLE support that you can use to add a column to the end of a table or to change the name of a table.
If you want to make more complex changes in the structure of a table, you will have to recreate the table. You can save existing data to a temporary table, drop the old table, create the new table, then copy the data back in from the temporary table.
For example, suppose you have a table named "t1" with columns names "a" and "c" and that you want to insert column "b" from this table. The following steps illustrate how this could be done:
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,c);
INSERT INTO t1_backup SELECT a,c FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b, c);
INSERT INTO t1 SELECT a,c FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;
Now you are ready to insert your new data like so:
UPDATE t1 SET b='blah' WHERE a='key'
ALTER TABLE {tableName} ADD COLUMN COLNew {type};
UPDATE {tableName} SET COLNew = {base on {type} pass value here};
This update is required to handle the null value, inputting a default value as you require. As in your case, you need to call the SELECT query and you will get the order of columns, as paxdiablo already said:
SELECT name, colnew, qty, rate FROM{tablename}
and in my opinion, your column name to get the value from the cursor:
private static final String ColNew="ColNew";
String val=cursor.getString(cursor.getColumnIndex(ColNew));
so if the index changes your application will not face any problems.
This is the safe way in the sense that otherwise, if you are using CREATE temptable or RENAME table or CREATE, there would be a high chance of data loss if not handled carefully, for example in the case where your transactions occur while the battery is running out.
I was facing the same problem and the second method proposed in the accepted answer, as noted in the comments, can be problematic when dealing with foreign keys.
My workaround is to export the database to a sql file making sure that the INSERT statements include column names. I do it using DB Browser for SQLite which has an handy feature for that. After that you just have to edit the create table statement and insert the new column where you want it and recreate the db.
In *nix like systems is just something along the lines of
cat db.sql | sqlite3 database.db
I don't know how feasible this is with very big databases, but it worked in my case.
I seldom add Answers to 11 year old questions. That said the answer with a lot of votes has a misleading line of code. I say misleading because I tried it and had no success. Here is the line of code I am referencing.
ALTER TABLE {tableName} RENAME TO TempOldTable
This is the line I tried in my first try at adding a Column into a DB Table that had already been created. It FAILED but WHY might be a better question. Any way here is the failing line of code.
Dim tb As String = "IncomeTable"
Dim sqlCmd As String = "$ALTER TABLE" '{tb}' "ADD COLUMN itNumVisit INTEGER"
So here is the final code that adds a new Column in my case an INTEGER type.
Private Sub btnCopyTable_Click(sender As Object, e As EventArgs) Handles btnCopyTable.Click
Dim sqlCmd As String = "ALTER TABLE IncomeTable ADD COLUMN itNumVisit INTEGER"
Try
Using conn As New SQLiteConnection($"Data Source = '{gv_dbName}';Version=3;")
conn.Open()
Using cmd As New SQLiteCommand(sqlCmd, conn)
cmd.ExecuteNonQuery()
End Using
End Using
Catch ex As Exception
MsgBox("It Failed")
End Try
End Sub
Notice the STRING sqlCmd is all one String. Just in case someone tried the accepted Answer!

Resources