Retrieve login name into column - oracle

I have created a trigger.
In this trigger I would like to have the login user name returned, among other things.
create or replace trigger trg_form_submitted
after insert or delete or update on tbl1
for each ROW
declare
v_user varchar2(30);
v_date varchar2(30);
begin
select v('&APP_USER'), to_char(sysdate, 'DD/MON/YYYY HH24:MI:SS' ) into v_user,v_date from dual;
if INSERTING THEN
....
However this unfortunately does not work I do not get the login name.
Can anyone help?

That's probably because you misused it.
Correct syntax is
v ('APP_USER')
(remove the ampersand & sign).

While #Littlefoot s' answer is 100% correct, there is a more performant option available now. The V is a function that has to be executed every time. A faster way to get the same data outside of your apex application is to use SYS_CONTEXT('APEX$SESSION','APP_USER') which is instant.
Also Note that you don't have to do SELECT INTO to assign variables. Your trigger will be a bit more efficient (one less call to the sql engine) if you just assign the variables:
create or replace trigger trg_form_submitted
after insert or delete or update on tbl1
for each ROW
declare
v_user varchar2(30);
v_date varchar2(30);
begin
v_user := SYS_CONTEXT('APEX$SESSION','APP_USER');
v_date := to_char(sysdate, 'DD/MON/YYYY HH24:MI:SS' );
if INSERTING THEN
....
A couple more tips
A good naming convention is to start the trigger name with the table name and add a suffix, like tbl1_aiud (aiud stands for "after insert update delete"). That way the object name indicates it is related to "tbl1". Naming it "trg" is like naming your work document "Word document xyz.doc"...
Storing a date in a variable or column of datatype VARCHAR2 is a bad idea in 99% of the cases. The datatype DATE is extremely powerful - use that if the data is a date.

Related

How do I declare a variable and use it in a subsequent query

I'm trying to do something that's really simple in TSQL but I'm utterly stuck doing the same in PlSQL.
I want to do the equivalent of this:-
declare #Today Date = GetDate();
declare #FirstDate Date;
declare #LastDate Date;
with cteStartDates as
(
Select START_DATE
From TABLE1
Union All
Select START_DATE
From TABLE2
)
Select #FirstDate = MIN(START_DATE),
#LastDate = DateAdd(Day, 1, #Today)
From cteStartDates;
Basically, I'm trying to get the a start date and end date where start date is the date of a first record in one of two tables and end date is tomorrow. I then need to use #FirstDate and #LastDate as parameters in a whole bunch of subsequent queries.
I'm falling at the first hurdle in PLSQL. I can't even work out how to do the equivalent of this:-
declare #Today Date = GetDate();
I've tried reading around Oracle Variables but I'm not really understanding it. I don't understand the difference between DEFINE, DECLARE and VARIABLE and no matter which I try I can't seem to get it to work and keep getting problems I don't really understand.
For example (based on Declare but I've tried all the following with Define and Variable also), I've tried this as an experiment (assign a value to variable and then issue an otherwise valid query which doesn't even use the variable):-
Declare
v_Today Date;
Begin
Select sysdate into v_Today from dual;
Select ID From atable;
End;
That tells me I need an Into clause on the second select. I don't really understand why, I'm not trying to assign ID to anything, I just want to select it. I've seen some examples that sort of imply that an into will define the column names (I'm not sure I've understood that correctly though). OK, I tried this:-
Declare
v_Today Date;
Begin
Select sysdate into v_Today from dual;
Select ID into IDColumn From atable;
End;
That gives me a error saying identifier IDColumn must be declared so clearly the into can't simply name columns.
From examples I get the impression that perhaps the begin and end surround the bock in which variables are assigned values that can then be used later in the script. So I tried this:-
Declare
v_Today Date;
Begin
Select sysdate into v_Today from dual;
End;
Select v_Today from Dual;
That tells me that it encountered the keyword Select, so it seem I can't just simply follow up the declare begin and end block with a query.
Some example seem to show that you can assign a variable, execute then use the variable. So I tried executing the Declare/Begin/End Block on it's own - that gave me message saying it ran successfully. Then I tried executing the subsequent Select v_Today from Dual, v_Today's not recognised, so clearly I've lost the value of the variable by splitting up the executions.
I feel like this should be trivially easy but I'm clearly not getting it. Can someone point me in the right direction?
Edit> Ah, finally figured it out. I can use the variables within the Begin and end but I can't just issue a select in there.
PL/SQL block is enclosed with BEGIN/END keywords. Once you leave the block you end your PL/SQL context. And enter plain SQL context where PL/SQL variables are not known.
In simple words :-)
This is correct what you have learned:
you need to select values into variables
variables must be declared in DECLARE part of PL/SQL block
if you want to return a variable to the client - e.g. to have it displayed - you need to use Oracle's package dbms_output.
Like this:
Declare
v_Today Date;
Begin
Select sysdate into v_Today from dual;
dbms_output.put_line(v_Today);
End;
You will not see a thing until you issue before PL/SQL block:
SET SERVEROUTPUT ON
This blog post can help: https://blogs.oracle.com/connect/post/building-with-blocks
Steve published whole series of posts for PL/SQL beginners. This is just the first one.
The variables in the Declare section can be used in the Begin and End block

How to find the column used in the dynamic query without executing whole query

Problem Statement
I have a dynamic SQL which i need to store in a table ,but before
storing the sql i need to validate the sql with the list of columns
stored in another table.
Without executing the query , is it possible to find name of columns in the select ?
Approach1
Only option i can think of is ,try to use explain plan of the query and read the meta data in the data dictionaries table .But unfortunately i am not able to find any table with such data.Please let me know if you know such views?
Approach2
Use DBMS_SQL.DESCRIBE_COLUMNS package to find the column name ,but i believe this will execute the whole query.
You don't need to execute the query to get the column names, you just need to parse it; e.g. as a simple example:
set serveroutput on
declare
l_statement varchar2(4000) := 'select * from employees';
l_c pls_integer;
l_col_cnt pls_integer;
l_desc_t dbms_sql.desc_tab;
begin
l_c := dbms_sql.open_cursor;
dbms_sql.parse(c=>l_c, statement=>l_statement, language_flag=>dbms_sql.native);
dbms_sql.describe_columns(c=>l_c, col_cnt=>l_col_cnt, desc_t=>l_desc_t);
for i in 1..l_col_cnt loop
dbms_output.put_line(l_desc_t(i).col_name);
end loop;
dbms_sql.close_cursor(l_c);
exception
when others then
if (dbms_sql.is_open(l_c)) then
dbms_sql.close_cursor(l_c);
end if;
raise;
end;
/
which outputs:
EMPLOYEE_ID
FIRST_NAME
LAST_NAME
EMAIL
PHONE_NUMBER
HIRE_DATE
JOB_ID
SALARY
COMMISSION_PCT
MANAGER_ID
DEPARTMENT_ID
PL/SQL procedure successfully completed.
You can do whatever validation you need on the column names inside the loop.
Bear in mind that you'll only see (and validate) the column names or aliases for column expressions, which won't necessarily reflect the data that is actually being retrieved. Someone could craft a query that pulls any data from anywhere it has permission to access, but then gives the columns/expression aliases that are considered valid.
If you're trying to restrict access to specific data then look into other mechanisms like views, virtual private database, etc.
DBMS_SQL.PARSE will not execute a SELECT statement but it will execute a DDL statement. If the string 'select * from employees' is replaced by 'drop table employees' the code will fail but the table will still get dropped.
If you're only worried about the performance of retrieving the metadata then Alex Poole's answer will work fine.
If you're worried about running the wrong statement types then you'll want to make some adjustments to Alex Poole's answer.
It is surprisingly difficult to tell if a statement is a SELECT instead of something else. A simple condition checking that the string begins with select will work 99% of the time but getting from 99% to 100% is a huge amount of work. Simple regular expressions cannot keep up with all the different keywords, comments, alternative quoting format, spaces, etc.
/*comment in front -- */ select * from dual
select * from dual
with asdf as (select * from dual) select * from asdf;
((((((select * from dual))))));
If you need 100% accuracy I recommend you use my open source PLSQL_LEXER. Once installed you can reliably test the command types like this:
select
statement_classifier.get_command_name(' /*comment*/ ((select * from dual))') test1,
statement_classifier.get_command_name('alter table asdf move compress') test2
from dual;
TEST1 TEST2
----- -----
SELECT ALTER TABLE

Log In procedure not working in oracle 11g

My table contain only two row. When i am giving empid and password of another row stil login is done. This is my procedure
CREATE OR REPLACE PROCEDURE prco_LoginCheck(Emp_Id IN number,
Cpassword In varchar2,
cur_out out Types.cursor_type) AS
BEGIN
open cur_out for
select count(*) from TBL_REGISTRATION a
where a.confirm_password= Cpassword and a.emp_id=Emp_Id;
END prco_LoginCheck;
Aside from the fact that storing a password in a table is a horribly insecure design (you should only be storing a hash of the password plus some random salt), and the fact that it doesn't make a lot of sense to define your own weak ref cursor type rather than using the built-in sys_refcursor (unless you really need to work with ancient versions of Oracle), and that returning a cursor from a procedure seems like a really odd way to do a login rather than writing a function that returns a boolean or some indicator, the problem is one of naming conventions.
When you write the SQL statement
select count(*)
from TBL_REGISTRATION a
where a.confirm_password= Cpassword
and a.emp_id=Emp_Id;
I assume that your intention is to compare the emp_id column in tbl_registration to the emp_id parameter. That is not actually what is happening, however. Your unqualified emp_id is resolved as a column in the table not the parameter. Normally, people use a naming convention to ensure that parameter names don't match column names. I, for example, generally use a p_ prefix for parameters and l_ for local variables. That would look something like
CREATE OR REPLACE PROCEDURE prco_LoginCheck(p_Emp_Id IN number,
p_password In varchar2,
p_cur_out out Types.cursor_type) AS
BEGIN
open p_cur_out for
select count(*)
from TBL_REGISTRATION a
where a.confirm_password= p_password
and a.emp_id=p_Emp_Id;
END prco_LoginCheck;
It is also possible to use your existing parameter names and just fully qualify the emp_id to force it to resolve to the parameter name
CREATE OR REPLACE PROCEDURE prco_LoginCheck(Emp_Id IN number,
Cpassword In varchar2,
cur_out out Types.cursor_type) AS
BEGIN
open cur_out for
select count(*)
from TBL_REGISTRATION a
where a.confirm_password= Cpassword
and a.emp_id=prco_LoginCheck.Emp_Id;
END prco_LoginCheck;

PL/SQL Procedure - Ticket system

I'm trying to make a Procedure in PL/SQL for a ticket system for my website.
I want a simple procedure that saves some data into the database.
It will have name of the user (random VARCHAR2) and a number and the date of today as input.
When this procedure is called it will automatically higher a number in the database (Maybe primary key) and add the input into the database. But I have no idea how to make something like this with the option to have more people access it at once.
Can anyone help please?
For the people that want to know what I have already.
CREATE OR REPLACE PROCEDURE VoegBoeteToe
(v_speler IN number, v_date IN date, v_boete IN number)
IS VoegBoeteToe
DECLARE
v_nextNum NUMBER;
BEGIN
SELECT BETALINGSNR
INTO v_nextNum
FROM BOETES
ORDER BY BETALINGSNR DESC;
v_nextNum := v_nextNum + 1;
INSERT INTO BOETES VALUES(v_nextNum,v_speler,v_date,v_boete);
end;
/
The fourth line of your procedure should be IS instead of IS VoegBoeteToe. In addition, remove the fifth line (DECLARE), which is not needed in a procedure. And use a sequence to derive the sequential number safely.
So, when all's said and done, your procedure should look something like:
CREATE SEQUENCE BOETES_SEQ;
CREATE OR REPLACE PROCEDURE VoegBoeteToe
(v_speler IN number, v_date IN date, v_boete IN number)
IS
BEGIN
INSERT INTO BOETES VALUES(BOETES_SEQ.NEXTVAL,v_speler,v_date,v_boete);
end;
It would also be a very good idea to include the list of field names in the BOETES table into which you're inserting values. This will make life better for anyone who has to maintain your code, and may save you from making a few errors.
Share and enjoy.
You need to create SEQUENCE
CREATE SEQUENCE DUMMY_SEQ
/
Then in Your code You can use something like that:
select DUMMY_SEQ.NEXTVAL into myVar from dual;
First, create a sequence. A sequence generates an ever incrementing number whenever it is invoked. See this for some nice examples. The documentation could be useful too.
CREATE SEQUENCE user_sequence
START WITH 1000 -- your sequence will start with 1000
INCREMENT BY 1; -- each subsequent number will increment by 1
Then,
SELECT user_sequence.NEXTVAL INTO v_nextNum FROM DUAL;

create trigger for copying values between tables in oracle

I am new to the sql. I want to create a trigger for copying values between tables.
basically, the task I want to finish is forwarding students' message table values to specific staff_maibox
here is the code.
drop trigger forward_msg_to_staff;
create or replace trigger forward_msg_to_staff
update on message
for each row
declare
message_id VARCHAR2(10);
client_id NUMBER(10);
staff_id NUMBER(5);
message_date DATE;
message_title VARCHAR2(20);
staff_mailbox VARCHAR2(255);
begin
insert into staff_mailbox(message_id, client_id, staff_id, message_date, message_title, staff_mailbox)
values(:new.message_id, :new.client_id, :new.staff_id, :sysdate, :new.message_title, :old.staff_mailbox)
end;
/
is this code correct?
Please advise. thanks in advance.
You're getting an error because you're missing either the BEFORE or AFTER keyword from the CREATE TRIGGER statement.
These are required as indicated in the documentation:
Additionally:
There's no need to declare all the variables, you're not using them
:sysdate is incorrect, you're not binding it. You can just use sysdate instead as you would in standard SQL or PL/SQL.
You're missing a semi-colon after the VALUES clause of the INSERT statement.
Putting this together your trigger may look like this
create or replace trigger forward_msg_to_staff
after update on message
for each row
begin
insert into staff_mailbox( message_id, client_id, staff_id, message_date
, message_title, staff_mailbox )
values ( :new.message_id, :new.client_id, :new.staff_id, sysdate
, :new.message_title, :old.staff_mailbox );
end forward_msg_to_staff;
/
Note that I've used the trigger name in the END as well. This is for convenience only, it makes it obvious where the trigger ends...
If you want to see what errors your're getting when you're creating a trigger use show errors as a_horse_with_no_name suggests. This shows any compilation errors, which is invaluable for tracking them down.

Resources