Cannot find data after commiting in PL/SQL procedure? - sql

I have code snippet in my PL/SQL procedure that does the following:
INSERT INTO payment_operations (id, subscriber, amount, description) VALUES (payment_id, 1234, 5, 'Test');
COMMIT;
SELECT subscriber INTO test_subscriber FROM payment_operations_view WHERE id = payment_id;
After this I get an exception "no_data_found"! However, if I do the same SELECT statement myself after running the procedure, I see the record.
Note that I am selecting from a view, and not directly from the table. Why I cannot see this data right after insertion?

This is a hunch:
Does the payment_options table have a column payment_id?
I ask because in the following statement, within PL/SQL, if payment_id exists as a column, then the column is going to be used not the local PL/SQL variable:
SELECT subscriber
INTO test_subscriber
FROM payment_operations_view
WHERE id = payment_id;
Since it is using the payment_id column, if it exists, and since it was not set in the insert you might be doing where id = null which never evaluates to true.
I use v_ to signify variables. So your snippet would become (with the rest of the procedure changed accordinly):
INSERT INTO payment_operations (id, subscriber, amount, description)
VALUES (v_payment_id, 1234, 5, 'Test');
COMMIT;
SELECT subscriber
INTO v_test_subscriber
FROM payment_operations_view WHERE id = v_payment_id;

Looks like the view you are referring to is a 'materialized' view. If yes, try this code snippet to refresh the view manually before you fetch the data:
...
INSERT INTO payment_operations (id, subscriber, amount, description) VALUES (payment_id, 1234, 5, 'Test');
COMMIT;
DBMS_SNAPSHOT.REFRESH( 'payment_operations_view','c');
SELECT subscriber INTO test_subscriber FROM payment_operations_view WHERE id = payment_id;
DBMS_OUTPUT.PUT_LINE('--> ' || test_subscriber);
...
Hope this helps.

You're inserting into the base table, then selecting from a view. Does the view have any filter conditions? If so, it's possible that the data you've inserted simply doesn't match the view's conditions.
As others have pointed out, it's also possible that the view is a materialized view, in which case it may need to be refreshed.
I have to ask ... why does the logic require selecting out data you've just inserted anyway? Unless you expect something else to have modified the data between the INSERT and SELECT, why not just use the subscriber value that you just inserted instead of querying to get it again?

Explicitly put a commit after the insert statement and check.
To me it appears that select statement is running before the commit actually happens after insert.

Related

Insert values two dependent tables Oracle Apex

I've created a query out of 3 dependent tables:
car parts(id, name, value);
car_model(id, name,price);
part_and_model(car_model_id, car_part_id)
I get my car models from a list of values, and for car_id I use sequence for increment.
I have all the needed fields from my query, but now I want to insert data into car_parts and part_and_model at the same time. I've created two processes on submit to insert (the first for car_parts(fk) and the second for part_and_model). When I run this, I get this error and no data gets inserted into the tables:
ORA-02291: integrity constraint (PRACTICE.FK_CAR_PART_car_part) violated - parent key not found
When I delete the second process, the data is correctly inserted into the car_parts table. Why is this happening? Does the second process execute before first? What else am I missing?
item P8_CAR_MODEL = LOV;
item P8_ID=>sequence: SELECT CP_SEK.NEXTVAL FROM DUAL;
process1:insert into CAR_PARTS(CP_ID,CP_NAME,CP_PRICE) VALUES (:P8_ID, :P8_NAME, :P8_PRICE);
process2: insert into part_and_model(PART_ID, MODEL_ID) values (:P8_ID,:P8_CAR_MODEL);
EDIT:
I managed to insert into car_parts table, but the error still says that the parent cannot be found (even though) it's in the table, so it must be the sequence...
here is my code:
process1:
begin
select PART_SEK.NEXTVAL INTO :P8_ID from dual;
insert into car_parts(id,name,value) VALUES (:P8_ID, :P8_NAME, :P8_PRICE);
end;
process2:
insert into part_and_model(car_model_id, car_part_id) values (:P8_MODEL,:P8_ID);
EDIT2:
when i use PART_SEK.currval instead of p8_id in second process, it works
Once again, thank you all for your time.
Insert rows into both tables in the same process; master table first, details next.
Look at the sequence of the processes. The first process must have a lower sequence e.g 10 and the second must be greater.
Be sure that you in the first process you have to insert into the master table.
Change the processes execution point to processing.
DECLARE
a_id NUMBER;
BEGIN
a_id := your_sequence.nextval;
INSERT INTO car_parts (cp_id, cp_name, cp_price)
VALUES (a_id, :P8_NAME, :P8_PRICE);
INSERT INTO part_and_model (part_id, model_id)
VALUES (a_id, :P8_CAR_MODEL);
END;
By all rights this should work. I do believe your problem really is with the sequence, nothing else I can think of that would cause your error. So please try this and let us know.

How to insert into the table a user name record

I've a table. In this table I have two columns - 'insert_name' and 'modified_name'. I need to insert into this columns data about who has inserted data into the table('insert_name') and who has changed these data in the table (modified_name). How it can be done?
You are looking for basic DML statements.
If your record is already in the table, then you need to UPDATE it. Otherwise, when you are about to add your record to it and it doesn't already exist in the destination table then you are looking for INSERT INTO statement.
Example of updating information for record with first id:
UPDATE yourtable SET insert_name = 'value1', modified_name = 'value2' WHERE id = 1
Example of inserting new record:
INSERT INTO yourtable(id, company_name, product_name, insert_name)
VALUES (1, 'Google', 'PC', 'value1')
If you are looking for automatic changes to those columns then you need to look into triggers.
Remember that more often than not you may find that the application connecting to the database is using single database user in which case you probably know the context within the application itself (who inserts, who updates). This does eliminate triggers and put the task straight on simple insert/update commands from within your application layer.
You might be able to use the CURRENT_USER function to find the name of the user making the change.
The value from this function could then be used to update the appropriate column. This update could be done as part of the INSERT or UPDATE statement. Alternatively use an INSERT or UPDATE trigger.
Personally I avoid triggers if I can.
For those 2 columns add Current_User as Default constraint.
As the first time Insert Statement will save them with current login user names. For update write an Update trigger with the same Current_User statement for the column Modified_Name.
If and only if your application business logic can't update the column modified_nme then only go for Trigger.
See the use of Current_Use
https://msdn.microsoft.com/en-us/library/ms176050.aspx

Is is safe to use ##IDENTITY in a transaction?

I was reading this answer about different methods of getting the last identity value entered into a database.
From what I understand, ##IDENTITY is usually a very bad idea because it might return an identity that is not the one you expected--for example an identity value that was recently created by a trigger.
But what if your code is in a transaction?
For example this is a simplified version of a transaction I'm doing (using ColdFusion):
<cftransaction>
<cfquery name="queryInsertA" datasource="source">
INSERT INTO tableA (columnName) VALUES (value)
</cfquery>
<cfquery name="queryInsertB" datasource="source">
INSERT INTO tableB (fkey_tableA, columnName) VALUES (##IDENTITY, value)
</cfquery>
</cftransaction>
Since, "If a transaction is successful, all of the data modifications made during the transaction are committed and become a permanent part of the database," does this mean that it would also prevent the isses that can arise when using ##IDENTITY? Or am I misunderstanding the behavior of transactions?
The answer you linked already explains what the main issue is with ##IDENTITY: scope. If your insert triggers another insert, you get an unexpected identity back. Transactions do not change anything.
If you wanted to get the last identity value inserted to a table, use the Ident_current() function.
Select ident_current ('your table name')
Also you can use scope_identity(), It will bring the identity value of a tablein that particular scope only.
Select scope_identity()
You don't need ##Identity, nor do you need 2 separate queries. Use the Scope_identity() function for integrity and make it a part of the same connection & query - like so.
<cfquery name="putUser" datasource="#dsn#">
SET NOCOUNT ON
INSERT INTO users(username, email)
VALUES
('#usersname#','#email#' )
SELECT SCOPE_IDENTITY() AS newId FROM users
SET NOCOUNT OFF
</cfquery>
<cfoutput>#putUser.newID#</cfoutput>
This will be totally safe, but like all db transactions it will still be subject to deadlocks so tuning is still important.
CFTRANSACTION is good for multiple DB operations where some CF logic might also be involved, but let the DB locking and transactional system work for you by keeping it together.
You can also use the result attribute of cfquery. If the query performs an INSERT of an identity or auto-increment value for ID, there will be a key named GENERATEDKEY returned in the structure.
<cftransaction>
<cfquery name="queryInsertA" datasource="source" result="resultA">
INSERT INTO tableA (columnName) VALUES (value)
</cfquery>
<cfquery name="queryInsertB" datasource="source">
INSERT INTO tableB (fkey_tableA, columnName) VALUES (#resultA.generatedKey#, value)
</cfquery>
</cftransaction>
Keep in mind this is only CF9 and higher.
You can use Sequence and use that during insert as below:
CREATE SEQUENCE Testseq
START WITH 1
INCREMENT BY 1 ;
Access the sequence by using below query:
SELECT NEXT VALUE FOR Testseq;
To make it simple :
IF
You know you'r ALL ALONE in the db system, this means, no other user, or process running at the same time, no other transaction running, there is absolutly ZERO activity at the time you use it, and i mean it, ZERO ACTIVITY, then, ok...
ELSE
NO ! If anything like i listed above does occur exactly while your transact is running, you will end up with the wrong identity.
It depends on what else is running at the same time as your transaction is instantiated. If there is a trigger on a table unrelated to the transaction that can insert a new identity value, the transaction scope you are currently in will not protect you.
For example say I create a SPROC that updates Table_A and inserts a record into it. This table has an identity field on it that will increment the ID value in that table each time a new record is inserted. Inside my SPROC I create a transaction and place my insert inside the transaction. After the insert I store the value of ##IDENTITY in a variable inside the same transaction.
Now I also have another table Table_B with it's own identity value but this table is trigger maintained. If I am executing my SPROC to insert a row in Table_A and during this update Table_B is also updated via a trigger, it is possible that when I retrieve the value of ##IDENTITY, it will actually give me the value of the ID created for Table_B rather than Table_A.
You should definitely use Transactions in your stored procedures but you are better off selecting the MAX(ID) of the table you inserted into to retrieve the ID you created rather than ##IDENTITY.

SQL Insert Query With Condition

I am trying to insert values into 1 column of a table when a condition is satisfied.
Note: The table already contains data for all the columns but for 1 which is empty. I would like to insert value into this 1 column depending on the WHERE clause.
I have this query:
INSERT INTO <TABLE_NAME>
(COLUMN_NAME)
(VALUE)
WHERE <CONDITION>
I am getting an exception:
Incorrect Syntax Near WHERE Keyword
I am able to do this using UPDATE:
UPDATE <TABLE_NAME>
SET <COL_NAME>
WHERE <CONDITION>
But was wondering why the INSERT query was failing. Any advise appreciated.
As I understand your problem, you already have data in one row, and one column in that row does not have value, so you want to add value in to that column.
This the scenario for Update existing row, not the insert new row. You have to use UPDATE clause when data already present and you want to modify record(s). Choose insert when You want to insert new row in table.
So in your current scenario, Update Clause is your friend with Where Clause as you want to modify subset of records not all.
UPDATE <TABLE_NAME>
SET <COL_NAME>
WHERE <CONDITION>
INSERT Clause does not have any Where Clause as per any RDBMS syntax(I think). Insert is condition less sql query, While SELECT, UPDATE, DELETE all are conditional commands, you can add Where Clause in all later ones.
In order to add a value into the one column when the rows are already populated, you will need to use the update statement.
If you need to insert a new row that has a where clause, you will need to use an insert into select statement:
INSERT INTO <table> (<columns>)
SELECT <columns>
FROM <table>
WHERE <condition>;
The SQL Insert dont accept where parameters, you could check this: SQL Insert Definition...
I do not know the whole question of what you want to do, but just using the INSERT statement is not possible, however it is possible to condition the insertion of data into a table, if this data is dependent on another table or comes from another table ... check here... SQL Insert explain in wikipedia
like this:
Copying rows from other tables
INSERT INTO phone_book2
SELECT *
FROM phone_book
WHERE name IN ('John Doe', 'Peter Doe')
or
INSERT INTO phone_book2 ( [name], [phoneNumber] )
SELECT [name], [phoneNumber]
FROM phone_book
WHERE name IN ('John Doe', 'Peter Doe')
Based on your question I have the feeling that you are trying to UPDATE a column in a table rather than insert.
Something like:
UPDATE column SET value WHERE different_column_value = some_value
I know this is kinda late, for those who still want to use the where clause in an insert query, it's kinda possible with a hack.
My understanding is that, you want to insert only if a condition is true. Let's assume you have a column in your database "surname" and you want to insert only if a surname doesn't exist from the table.
You kinda want something like INSERT INTO table_name blha blha blah WHERE surname!="this_surname".
The solution is to make that cell unique from your admin panel.
Insert statement will insert a new record. You cannot apply a where clause to the record that you are inserting.
The where clause can be used to update the row that you want.
update SET = where .
But insert will not have a where clause.
Hope this answers your question
INSERT syntax cannot have WHERE clause. The only time you will find INSERT has WHERE clause is when you are using INSERT INTO...SELECT statement.
I take it the code you included is simply a template to show how you structured your query. See the SO questions here, here and the MSDN question here.
In SQL Server (which uses Transact-SQL aka T-SQL) you need an UPDATE query for INSERT where columns already have values - by using the answer #HaveNoDisplayName gave :)
If you are executing INSERT / UPDATE from code (or if you need it regularly) I would strongly recommend using a stored procedure with parameters.
You could extend the procedure further by adding an INSERT block to the procedure using an IF-ELSE to determine whether to execute INSERT new record or UPDATE an existing, as seen in this SO answer.
Finally, take a look at SQLFiddle for a sandbox playground to test your SQL without risk to your RDMS :-)
Private case I found useful: Conditional insert which avoids duplications:
-- create a temporary table with desired values
SELECT 'Peter' FirstName, 'Pan' LastName
INTO #tmp
-- insert only if row doesn't exist
INSERT INTO Persons (FirstName, LastName)
SELECT *
FROM #tmp t
WHERE NOT EXISTS (SELECT * FROM Persons where FirstName=t.FirstName and LastName=t.LastName)
If the data need to be added for a column for an existing row then it’s UPDATE.
INSERT is creating a new row in the table.
For conditional INSERT, you can use the MERGE command.

How to fire trigger after every insert - Oracle (Bulk insert)

I want the trigger to run after each and every record that gets inserted.
This validation works fine if I have a simple insert like this:
insert into g_dossier values
(112334, 'BBT', 'LPO','TTS','Y') ;
However, when it’s like a bulk insert like this:
INSERT INTO g_piece(
refpiece,
typpiece,
class_piece
group_piece
flag_piere)
SELECT :new.element_num,
PROC_TYPE,
DECODE( piece_it, 'F', 'FTTR', 'N', 'FTTR', NULL ),
DECODE( piece_it, 'T', 'TTSN', 'N', 'TTSN', NULL ),
'N'
FROM t_elements
WHERE :new.db_piece_flag = 'Y';
The trigger mutates. I want that the validation also work when done as a bulk insert.
The query causing this problem is
SELECT COUNT(*)
INTO existing_cmcl_cnt
FROM g_piece cmcl
WHERE cmcl.class_piece= :new.class_piece
The problem is that this query is called in a trigger applied on the same table "g_piece". When I proceed with a simple insert (insert into g_piece values(...)), I do not have this problem.
How can I avoid this problem?
Thanks.
Here you should change the query and insert the result of a SELECT ... FROM some join, not using a trigger at all. You want that when inserting into table X, a trigger also inserts into the same table, which is not possible (it would recurse).
If you cannot change the query, you should rename your table, create a view to the table with the old name, and create a TRIGGER INSTEAD OF INSERT ON that view FOR EACH ROW
that would INSERT INTO into the real table the result of a SELECT ... FROM some join.
If you're using Oracle 11G, you can take a look to Compound triggers to avoid the mutating-table error. Take a look:
http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD