The following procedure is not being created in Oracle SQL Developer
CREATE OR REPLACE PROCEDURE CheckUser(UserName IN VARCHAR2,Password IN VARCHAR2)
AS
DECLARE Counts int;
BEGIN
SELECT COUNT(UserNames) INTO Counts FROM tblUsers
WHERE UserNames = UserName and Passwords = Password;
IF Counts = 1 THEN
SELECT 1 AS Code;
ELSE
SELECT -1 AS Code;
END;
When I run above procedure the following error message is returned in SQL Developer:
PROCEDURE CHECKUSER compiled
Errors: check compiler log
Error(3,1): PLS-00103: Encountered the symbol "DECLARE" when expecting
one of the following: begin function pragma procedure subtype type
current
cursor delete exists prior external language
To actually go over your errors:
The procedure declaration CREATE OR REPLACE... is the DECLARE block; you can remove the DECLARE; see the documentation for more information.
You need to select from something, this is normally the table DUAL, which has been designed for this purpose, i.e.
select 1 as code from dual
If you're selecting data in a procedure you need to SELECT INTO a variable. You do this the first time but not the second, i.e.
select 1 into <some variable> from dual
INT is not a datatype; it's INTEGER, which is a synonym for NUMBER(38,0
As far as I can tell you're not actually using the return code at all... I assume you're authenticating users here, which means you need to tell the calling program whether it was successful or not.
If you want to return a value the you probably want a function, as opposed to a procedure.
To take this to it's logical conclusion, your IF statement is unnecessary; the COUNT(*) will return 1 or 0 depending on whether the username and password exist... use this as a Boolean True/False instead.
I hope this is a password hash and not the actual password...
It's often better to be explicit about naming conventions and separate out parameters from column names etc to make it easier to read and less likely to cause Oracle to choke on the scope.
Putting all this together you end up with something like this:
create or replace function check_user (
PUsername in varchar2, PPassword_Hash in varchar2
) return number is
l_exists number;
begin
select count(*) into l_exists
from tblUsers
where username = PUsername
and password = PPassword_Hash
;
return l_exists;
end;
/
It's worth noting that your method of authentication is only safe if you ensure that people can only have one username, i.e. if TBLUSERS has a unique constraint on the column USERNAME. If it doesn't you do need some other method of uniquely identifying each user in your database, otherwise you could end up logging in people as a different user than they actually are.
Related
Procedure with in , out parameters and having IS keyword with parameters list before BEGIN. In this case which result we will get after executing procedure.
PROCEDURE sample_proc (id in VARCHAR2, name in VARCHAR2, Course out varchar2)
IS error EXCEPTION, error_code VARCHAR2(100), courseID NUMBER, price NUMBER
BEGIN
select * from student where ID =id and name=name;
END executePROC;
Which fields we will get as result after executing via mybatis.
The stored procedure having parameters with data type before BEGIN keyword. In this case the parameters after IS and Before BEGIN keyword how it will work. Please explain
Which fields we will get as result after executing via mybatis.
None, the code has syntax errors and will not compile in Oracle.
You have:
A statement starting with PROCEDURE and not CREATE PROCEDURE (assuming that it is not part of a package as you do not include a preceding CREATE PACKAGE statement).
, following the variable declarations and not ;
A SELECT statement in the PL/SQL scope that does not have an INTO clause.
WHERE id = id AND name=name is (almost) the same as doing WHERE 1=1 AND 1=1 as the id and name values on both sides of the equality comparison will be from the local scope of the SQL statement and will not reference the procedure's arguments.
Variables that you are not using.
The procedure's identifier does not match the identifier after the final END statement.
To fix it you want something like:
CREATE PROCEDURE sample_proc (
i_id in STUDENT.ID%TYPE,
i_name in STUDENT.NAME%TYPE,
o_Course out STUDENT.COURSEID%TYPE
)
IS
BEGIN
SELECT courseid
INTO o_course
FROM student
WHERE id = i_id
AND name = i_name;
END sample_proc;
/
db<>fiddle here
IS keyword with parameters list before BEGIN.
They are not parameters, they are local variables.
As MTO has explained, the example you gave is not valid for a number of reasons.
But for the sake of argument, if you did actually have something like:
... PROCEDURE sample_proc (...)
IS
error EXCEPTION;
error_code VARCHAR2(100);
courseID NUMBER;
price NUMBER;
BEGIN
...
then the bit between IS and BEGIN is the optional declaration part, as described in the documention:
Declarative part (optional)
This part declares and defines local types, cursors, constants, variables, exceptions, and nested subprograms. These items cease to exist when the subprogram completes execution.
This part can also specify pragmas.
Note: The declarative part of a subprogram does not begin with the keyword DECLARE, as the declarative part of an anonymous block does.
The bit that is particularly relevant to your question (as I understand it anyway) is "These items cease to exist when the subprogram completes execution." They are local variables for use within the procedure, and not visible to or accessible by whatever calls the procedure.
The only data made visible to the caller is whatever it put into the OUT parameters.
The procedure that makes 1 SELECT(Output data types in the user package). Outside of the SELECT procedure, it works fine. Here is the code:
СREATE OR REPLACE PROCEDURE get_types
IS
BEGIN
select name, type
from user_identifiers
where object_name = 'MY_TYPES'
and usage = 'DECLARATION'
and type != 'PACKAGE'
order by name;
END get_types;
/
Error:
SP2-0734: unknown command beginning "СREATE OR ..." - rest of line ignored.
SQL> SP2-0042: unknown command "IS" - rest of line ignored.
There's something strange with create (as if contains some garbage). I deleted it and typed that word, and code looks OK. However, as it is a PL/SQL procedure, select requires into, such as:
CREATE OR REPLACE PROCEDURE get_types
IS
l_name user_identifiers.name%TYPE;
l_type user_identifiers.TYPE%TYPE;
BEGIN
SELECT name, TYPE
INTO l_name, l_type
FROM user_identifiers
WHERE object_name = 'MY_TYPES'
AND usage = 'DECLARATION'
AND TYPE != 'PACKAGE'
ORDER BY name;
END get_types;
This would work if select returned exactly one row. Otherwise, if it does not, that code will return no_data_found. If it returned more than a single row, you'd get too_many_rows. What to do? It depends on what you want to do. You could select into a collection. Or a refcursor. Or use a loop. There are various options, but actual one depends on you.
Is it possible to select the parameters for calling a procedure from the select statement?
EXECUTE PROCEDURE_NAME(para1,para2,para3,para4);
commit;
Is it possible to select para1,para2,para3,para4 from a select query?
EXECUTE PROCEDURE_NAME((SELECT PARA1,PARA2,PARA3,PARA4 FROM TABLEA))
COMMIT;
I do not have access to modify the procedure.
As a slight variation on what #vc74 suggested, you could just replace your EXECUTE command (which, assuming this is SQL*Plus or SQL Developer anyway, is just a wrapper for an anonymous block anyway) with an explicit anonymous block:
begin
for r in (SELECT PARA1,PARA2,PARA3,PARA4 FROM TABLEA) loop
PROCEDURE_NAME(r.PARA1,r.PARA2,r.PARA3,r.PARA4);
end loop;
end;
/
(I've left the bits from your original call uppercase and the new bits lower case mostly to distinguish them.)
Using a loop just means you don't need to declare local variables and select into those. It would also allow you to process multiple rows from the table, though I see form a comment you only expect one row. However, the flip side of that is it won't complain if there are no rows, or if there is more than one row, as the variable approach would do.
You could also use a record type to avoid declaring all the parameters separately:
declare
l_row tablea%rowtype;
begin
SELECT * into l_row FROM TABLEA;
PROCEDURE_NAME(l_row.PARA1,l_row.PARA2,l_row.PARA3,l_row.PARA4);
end;
/
This now does expect exactly one row to be found in the table.
You can call the functions in sql. So if you are able to create a function in your schema then you can do the following:
create a function function_name in your schema that calls the procedure procedure_name and returns some dummy result
use this function in sql query: select function_name(para1,para2,para3,para4) from tablea
example of function:
create or replace function function_name(
p1 varchar2,
p2 varchra2,
p3 varchar2,
p4 varchar2
) return number
is
begin
procedure_name(p1,p2,p3,p4); -- here you execute the procedure
return null;
end;
I am new to Oracle and Stored Procedures. I just would like to know if its possible, like in SQL Server, to return a recordset with Field Names to an extern program. I read some documentations but I'm not sure if I'm on the right track. When I use Sys_Refcursor I can only return one Field and not as many as I would like to.
I need to return multiple Field Names and I have one input parameter.
In the documentation of the program, i have an example for SQL Server and I would like to have the same for my Oracle Stored Procedure:
Use
Go
Set Ansi_Nulls ON
Go
Alter Procedure
#InputLocation Varchar(255)
As
Begin
Set Nocount On;
select FirstName as '#FirstName', Company as '#Company' from dbo.company where Location = #InputLocation
End
Are there any suggestions how I can do that? If you need some additional informations just let me know. Thanks.
/edit:
My sample Code (without using the Input Parameter in the first step, just for generating Output to see if it works):
create or replace
PROCEDURE TEST_PROZEDUR1 (
Input_Location IN Varchar2,
First_Name OUT SYS_Refcursor,
Company OUT Sys_Refcursor) IS
BEGIN
open First_Name For Select FirstName from dbo.company;
open Company For Select Company from dbo.company;
END TEST_PROZEDUR1;
The programming models used for PL/SQL and TSQL are different. Where you might return a recordset in TSQL, in PL/SQL you would return a cursor. A cursor is just a pointer to an SQL statement which is opened and can be read. It is not limited to returning a single column. Roughly, the PL/SQL equivalent of your TSQL procedure above would be something like:
CREATE OR REPLACE FUNCTION GET_INPUT_LOCATION(pinInput_location IN VARCHAR2(255))
RETURN SYS_REFCURSOR
IS
cCursor SYS_REFCURSOR;
BEGIN
OPEN cCursor FOR
SELECT FIRSTNAME,
COMPANY
FROM COMPANY
WHERE LOCATION = pinInput_location;
RETURN cCursor;
END GET_INPUT_LOCATION;
The caller would then invoke this function as:
DECLARE
cCursor SYS_REFCURSOR;
strFirstname COMPANY.FIRSTNAME%TYPE;
strCompany COMPANY.COMPANY%TYPE;
BEGIN
cCursor := GET_INPUT_LOCATION('SOMEWHERE OVER THE RAINBOW, INC.');
FETCH cCursor
INTO strFirstname,
strCompany;
CLOSE cCursor;
END;
However, I probably wouldn't code it this way. If COMPANY.LOCATION is unique then you're going to a lot of trouble to return a cursor which the caller will need to remember to close when they're done with it, which they may forget to do. Instead, I'd just return the FIRSTNAME and COMPANY fields using output parameters; e.g.
CREATE OR REPLACE PROCEDURE GET_INPUT_LOCATION
(pinInput_location IN VARCHAR2(255),
poutFirst_name OUT COMPANY.FIRSTNAME%TYPE,
poutCompany OUT COMPANY.COMPANY%TYPE)
IS
cCursor SYS_REFCURSOR;
BEGIN
SELECT FIRSTNAME,
COMPANY
INTO poutFirst_name,
poutCompany
FROM COMPANY
WHERE LOCATION = pinInput_location;
END GET_INPUT_LOCATION;
Share and enjoy.
I know it seems like a basic thing, but I've never done this before.
I'd like to return a single record from an existing table as the result of an Oracle PL/SQL function. I've found a few different ways of doing this already, but I'm interested in the best way to do it (read: I'm not all that happy with what I've found).
The jist of what I am doing is this... I have a table called 'users', and I want a function 'update_and_get_user' which given a UserName (as well as other trusted information about said user) will potentially perform various actions on the 'users' table, and then return either zero or one row/record from said table.
This is the basic outline of the code in my head at the moment (read: no idea if syntax is even close to correct):
CREATE FUNCTION update_and_get_user(UserName in VARCHAR2, OtherStuff in VARCHAR2)
RETURN users PIPELINED IS
TYPE ref0 IS REF CURSOR;
cur0 ref0;
output_rec users%ROWTYPE;
BEGIN
-- Do stuff
-- Return the row (or nothing)
OPEN cur0 FOR 'SELECT * FROM users WHERE username = :1'
USING UserName;
LOOP
FETCH cur0 INTO output_rec;
EXIT WHEN cur0%NOTFOUND;
PIPE ROW(output_rec);
END LOOP;
END update_and_get_user;
I've seen examples where a record or table is returned, the type of record or table having been created / declared beforehand, but it seems like if the table has already been defined, I should be able to utilize that, and thus not have to worry about syncing the type declaration code if table changes are ever made.
I'm open to all potential solutions and commentary, but I do really want to keep this in a single PL/SQL function (as opposed to code in some other language / framework that communicates with the database multiple times, finishing with some form of 'SELECT * FROM users WHERE username=blah') as the system calling the function and the database itself may be different cities. Outside of that limit, I'm open to changing my thinking.
This is how I would do it. Variables/table-names/column-names are case-insensitive in Oracle, so I would use user_name instead of UserName.
CREATE TABLE users( UserName varchar2(20), OtherStuff VARCHAR2(20) );
Function update_and_get_user. Note that I return a ROWTYPE instead of Pipelined Tables.
CREATE OR REPLACE FUNCTION update_and_get_user(
in_UserName IN users.UserName%TYPE,
in_OtherStuff IN users.OtherStuff%TYPE )
RETURN users%ROWTYPE
IS
output_rec users%ROWTYPE;
BEGIN
UPDATE users
SET OtherStuff = in_OtherStuff
WHERE UserName = in_UserName
RETURNING UserName, OtherStuff
INTO output_rec;
RETURN output_rec;
END update_and_get_user;
And this is how you would call it. You can not check a ROWTYPE to be NULL, but you can check username for example.
DECLARE
users_rec users%ROWTYPE;
BEGIN
users_rec := update_and_get_user('user', 'stuff');
IF( users_rec.username IS NOT NULL ) THEN
dbms_output.put_line('FOUND: ' || users_rec.otherstuff);
END IF;
END;
A solution using PIPED ROWS is below, but it doesn't work that way. You can not update tables inside a query.
SELECT * FROM TABLE(update_and_get_user('user', 'stuff'))
ORA-14551: cannot perform a DML operation inside a query
Solution would look like that:
CREATE OR REPLACE TYPE users_type
AS OBJECT
(
username VARCHAR2(20),
otherstuff VARCHAR2(20)
)
CREATE OR REPLACE TYPE users_tab
AS TABLE OF users_type;
CREATE OR REPLACE FUNCTION update_and_get_user(
in_UserName IN users.username%TYPE,
in_OtherStuff IN users.otherstuff%TYPE )
RETURN users_tab PIPELINED
IS
output_rec users%ROWTYPE;
BEGIN
UPDATE users
SET OtherStuff = in_OtherStuff
WHERE UserName = in_UserName
RETURNING username, otherstuff
INTO output_rec;
PIPE ROW(users_type(output_rec.username, output_rec.otherstuff));
END;