Im almost done with a procedure i started yesterday, but on the last step i have found a problem that happens only if a comparison is done with an if clause. The procedure shows all the information of a match if you introduce a football league round and the name of a team. Im storing the football match id on idp for later comparisons when the team is either the home or the away team and the date matches with one of the two rounds available (the if clause sets a different date to a variable called FECHA if it is either one or two). The thing is that, if i try to check the FECHA or date in spanish manually, it works, but if i try using the if path, an error appears claiming:
Could you help me solve that? Thank you so much!
create or replace PROCEDURE COMPROBARPARTIDO(JORNADA IN NUMBER, EQUIPO IN VARCHAR2) AS
FECHA DATE;
IDLOCAL NUMBER;
IDP NUMBER;
NUMAUX NUMBER;
NUMAUX2 NUMBER;
GOLAUX NUMBER;
GOLOC NUMBER;
GOLVI NUMBER;
BEGIN
NUMAUX:=0;
NUMAUX2:=0;
IF JORNADA = 1 THEN
FECHA := TO_DATE('2021-03-04','yyyy-mm-dd');
ELSIF JORNADA = 2 THEN
FECHA := TO_DATE('2021-03-13','yyyy-mm-dd');
ELSE
DBMS_OUTPUT.PUT_LINE('ERROR');
END IF;
SELECT DISTINCT P.ID INTO IDP
FROM PARTIDO P
INNER JOIN EQUIPO EL
ON P.ID_LOCAL =EL.ID
INNER JOIN EQUIPO EV
ON P.ID_VISITANTE = EV.ID
WHERE P.FECHA = FECHA AND (EV.NOMBRE =EQUIPO OR EL.NOMBRE=EQUIPO);
You are doing:
WHERE P.FECHA = FECHA
From the documentation:
If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence.
So that means that condition is equivalent to:
WHERE P.FECHA = P.FECHA
which is always true (unless P.FECHA is null). That means you are finding all rows where (EV.NOMBRE =EQUIPO OR EL.NOMBRE=EQUIPO), regardless of the P.FECHA value. That is giving you more rows than you expect; as you are using select ... into ... the query has to return exactly one row, and presumably does when you hard-code a date.
You should either explicitly prefix your variable name with the procedure name to give it context:
WHERE P.FECHA = COMPROBARPARTIDO.FECHA
or rename you local variable; it's fairly common to add a L_ prefix to local variable names and P_ for parameter names, for example, to distinguish them from column names:
create or replace PROCEDURE COMPROBARPARTIDO(P_JORNADA IN NUMBER, P_EQUIPO IN VARCHAR2) AS
L_FECHA DATE;
...
and change all the references to match:
WHERE P.FECHA = L_FECHA
Related
Good evening,
I have a SP and I want to compare 2 dates from different tables, but in the form 'dd/mm/yyyy hh:mi:ss'
I am using to_char(date01,'dd/mm/yyyy hh:mi:ss')> to_char(date02,'dd/mm/yyyy hh:mi:ss')
but it throws me errors.
For example: if the date is 02/12/2016 07:40:12>02/02/2022 06:40:46
it indicates that it is true, and it is not, it is considering the day and not the entire date.
when I only use date01>date02, I have the problem you consider for example.
'02/15/2022 07:48:50'='02/15/2022 07:50:22' (only considers the date)
How can I compare date, minutes and seconds regardless of the server configuration.
Thank you,
PROCEDURE SPU_CUENTA
(
p_nro in varchar2,
pr_Ret OUT number
) is
vfecha varchar(100);
vcount int;
begin
select COUNT(DFEC_SISTEMA) into vcount from TAB Where c=1;
IF vcount>0 THEN
select to_char(DFEC_SISTEMA,'dd/mm/yyyy hh:mi:ss') into vfecha from TAB Where c=1;
EXECUTE IMMEDIATE 'SELECT COUNT(DFEC_ANULA) FROM tablab WHERE to_char(DFEC_ANULA,'dd/mm/yyyy hh:mi:ss')>'''||vfecha||'''' into pr_Ret;
END IF;
end;
Code you suggest would make sense if columns involved were VARCHAR2 (which is a bad idea; store dates into DATE datatype columns).
If those columns really are DATEs, then part of your question (which suggests format) is meaningless - we compare dates as they are, simply by e.g. date1 > date2. Converting them to characters - in a format you specified - is plain wrong.
If those columns are strings, then you'll have to convert them TO_DATE, not TO_CHAR
Procedure you wrote should be a function; they are designed to return a value. Yes, you can use a procedure, but - why would you? You can't use it in SQL (only in PL/SQL).
Besides, code can be heavily shortened/optimized, as you need just one select statement. You don't have to first check whether there any rows in tab that satisfy the condition, and then select some other info - use a subquery instead.
Finally, why are you using dynamic SQL? There's nothing dynamic in your code.
I'd suggest something like this, see if it makes sense.
FUNCTION spu_cuenta (p_nro IN VARCHAR2)
RETURN NUMBER
IS
pr_ret NUMBER;
BEGIN
SELECT COUNT (dfec_anula)
INTO pr_ret
FROM tablab
WHERE dfec_anula > (SELECT dfec_sistema
FROM tab
WHERE c = 1);
RETURN pr_ret;
END;
create or replace PROCEDURE Show_R(A IN VARCHAR2, B OUT VARCHAR2)
IS
BEGIN
select func_w(day),TO_CHAR(hour, 'HH24:MI')INTO B
from task t
inner join mat m
on t.id_p = m.id_a
where m.cod_mod = A;
END;
I have a issue with this code, this select gets two types of columns data that are not the same type of data, i don't know how to add into B two types of data in only one "out parameter"
You can't put 2 values into 1 OUT parameter. So, use 2 OUT parameters.
Firstly don't store day and hour in separate columns. Just use a single DATE column as, in Oracle, the DATE data type has year, month, day, hour, minute and second components and so can store both the date and time.
Secondly, don't use A, B, show_R or func_w identifiers; use meaningful names as it will be far easier to debug your code in 6-months if you can tell what it is intended to do.
Third, your SELECT ... INTO statement will fail as you have two columns but only one variable to select into; you need 2 variables in INTO clause and this means (unless you are going to concatenate the two values) that you need 2 OUT parameters.
CREATE PROCEDURE Show_w_day_and_hour(
i_cod_mod IN mat.cod_mod%TYPE,
o_w_day OUT VARCHAR2,
o_hour OUT VARCHAR2
)
IS
BEGIN
SELECT func_w(day),
TO_CHAR(hour, 'HH24:MI')
INTO o_w_day,
o_hour
FROM task t
INNER JOIN mat m
ON ( t.id_p = m.id_a )
WHERE m.cod_mod = i_cod_mod;
END;
/
db<>fiddle
Thanks for looking...
I've spent hours researching this and I can't believe it's that difficult to do something in PL/SQL that is simple in TSQL.
I have a simple query that joins 2 tables:
Select DISTINCT
to_char(TO_DATE('1899123000', 'yymmddhh24')+ seg.NOM_DATE, 'mm/dd/yyyy') AS "Record Date"
, cd.CODE
, EMP.ID
, EMP.SHORT_NAME
FROM
EWFM.GEN_SEG seg join EWFM.SEG_CODE cd ON seg.SEG_CODE_SK = cd.SEG_CODE_SK
join EMP on seg.EMP_SK = EMP.EMP_SK
where NOM_DATE = vMyDate;
I use Toad Date Point and I'm querying against an Oracle Exadata source. The resulting query will be dropped into a visualization tool like QlikView or Tableau. I'd like to create a simple variable to use the the WHERE clause as you can see in the code.
In this example, NOM_DATE is an integer such as 42793 (2/27/2017) as you can see in the first row "Record Date". Nothing new here, not very exciting... Until... I tried to create a variable to make the query more dynamic.
I've tried a surprising variety of examples found here, all have failed. Such as:
declare
myDate number(8);
Begin
myDate := 42793;
--Fail ORA-06550 INTO Clause is expected
variable nomDate NUMBER
DEFINE nomDate = 42793
EXEC : nomDate := ' & nomDate'
...where NOM_DATE = ( & nomDate) ;
--ORA-00900: invalid SQL statement
and
variable nomDate NUMBER;
EXEC nomDate := 42793;
select count(DET_SEG_SK) from DET_SEG
where NOM_DATE = :nomDate;
--ORA-00900: invalid SQL statement
and several more.. hopefully you get the idea. I've spent a few hours researching stackoverflow for a correct answer but as you can see, I'm asking you. From simple declarations like "Var" to more complex " DECLARE, BEGIN, SELECT INTO...." to actually creating Functions, using cursors to iterate the output.... I still can't make a simple variable to use in a Where clause.
Please explain the error of my ways.
--Forlorn SQL Dev
Since you are using an implicit cursor, you have to select then INTO variables. Now I d not know the data types of you variables, so I have just guessed in this example below, but hopefully you get the point.
Two other things I should mention
Why are you TO_CHARing you DATE. Just use a DATE datatype. Also, I think your format mask is wrong too 1899123000 does not match yymmddhh24.
In explicit cursor expects exactly one row; no rows and you get NO_DATA_FOUND; more than one and you get TOO_MANY_ROWS
Declare
myDate number(8) := 42793;
/* These 4 variable data types are a guess */
v_record_date varchar2(8);
v_cd_code varchar2(10);
v_emp_id number(4);
v_emp_short_name varchar2(100);
BEGIN
Select DISTINCT to_char(TO_DATE('1899123000', 'yymmddhh24')
+ eg.NOM_DATE, 'mm/dd/yyyy') AS "Record Date"
, cd.CODE
, EMP.ID
, EMP.SHORT_NAME
INTO v_record_date, v_cd_code, v_emp_id, v_emp_short_name
FROM EWFM.GEN_SEG seg
join EWFM.SEG_CODE cd
ON seg.SEG_CODE_SK = cd.SEG_CODE_SK
join EMP
on seg.EMP_SK = EMP.EMP_SK
where NOM_DATE = myDate;
END;
/
VARIABLE vMyDate NUMBER;
BEGIN
:vMyDate := 42793;
END;
/
-- or
-- EXEC :vMyDate := 42793;
SELECT DISTINCT
TO_CHAR( DATE '1899-12-30' + seg.NOM_DATE, 'mm/dd/yyyy') AS "Record Date"
, cd.CODE
, EMP.ID
, EMP.SHORT_NAME
FROM EWFM.GEN_SEG seg
join EWFM.SEG_CODE cd
ON seg.SEG_CODE_SK = cd.SEG_CODE_SK
join EMP
on seg.EMP_SK = EMP.EMP_SK
WHERE NOM_DATE = :vMyDate;
You put the variables with getter and setter in a package.
Then use a view that uses the package getter
Personally I prefer to use a collection that way I can do a select * from table (packagage.func(myparam))
I am working on an assignment. What I want to do is store two parameter values into one variable value. My code at the moment is this:
create or replace function generateISIN(
countryCode Country.Code%type,
universityCode University.Code%type,
studentNumber varchar2)
return varchar2
as
v_numbers varchar2(9);
newStudentNumber varchar2(50) := '';
begin
select co.code, un.code
into newStudentNumber
from country co, university un
where v_numbers = studentNumber;
dbms_output.put_line('implementeer deze functie verder...');
return newStudentNumber;
end;
/
The meaning of this program/code is to get the country code (US, NL, AUS, etc..) and the university code (RUS, TUE, TIU, etc..) into one variable. So in the end I will get a ISIN
International Student Identification Number
For example: NL 1234 5678 944 TUE
Any idea how to combine the country code and university code with a number?
Assuming your variable is big enough to hold the result, you can simply concatenate the columns using the string concatenation operator ||:
select co.code || un.code
into newStudentNumber
from country co, university un
where v_numbers = studentNumber;
You could simply concatenate the column values using the concatenate operator || or the CONCAT function.
For example,
select co.code ||' '|| un.code
into newStudentNumber
from country co, university un
where v_numbers = studentNumber;
I have used a space in between just to make sure that the output is clear and each code is separated by a space.
Update You need to correct your query:
select co.code, un.code
into newStudentNumber
from country co, university un
where v_numbers = studentNumber;
You have a Cartesian join since the filter predicate has no real column(s)
On both sides of the operator you have used the local variable, makes no sense. Usually, you would do a comparison in a IF-ELSE construct based on the business logic.
dbms_output.put_line('implementeer deze functie verder...');
DBMS_OUTPUT is a good help while testing your code, however, make sure you don't carry it on production environment.
Also, remember, if you could do something in SQL, then you should not use PL/SQL unless there is a business need. Your entire code could be replaced with a single SQL statement.
I have a common problem with ORACLE in following example code:
create or replace procedure usp_test
(
p_customerId number,
p_eventTypeId number,
p_out OUT SYS_REFCURSOR
)
as
begin
open p_out for
select e.Id from eventstable e
where
(p_customerId is null or e.CustomerId = p_customerId)
and
(p_eventTypeId is null or e.EventTypeId = p_eventTypeId)
order by Id asc;
end usp_test;
The "OR" in "(p_customerId is null or e.CustomerId = p_customerId)" kills procedure performance, because optimizer will not use index (i hope for index seek) on "CustomerId" column optimally, resulting in scan instead of seek. Index on "CustomerId" has plenty of distinct values.
When working with MSSQL 2008 R2 (latest SP) or MSSQL 2012 i can hint the query with "option(recompile)" which will:
Recompile just this query
Resolve values for all variables (they are known after sproc is called)
Replace all resolved variables with constants and eliminate constant
predicate parts
For example: if i pass p_customerId = 1000, then "1000 is null" expression will always be false, so optimizer will ignore it.
This will add some CPU overhead, but it is used mostly for rarely called massive reports procedures, so no problems here.
Is there any way to do that in Oracle? Dynamic-SQL is not an option.
Adds
Same procedure just without "p_customerId is null" and "p_eventTypeId is null" runs for ~0.041 seconds, while the upper one runs for ~0.448 seconds (i have ~5.000.000 rows).
CREATE INDEX IX_YOURNAME1 ON eventstable (NVL(p_customerId, 'x'));
CREATE INDEX IX_YOURNAME2 ON eventstable (NVL(p_eventTypeId, 'x'));
create or replace procedure usp_test
(
p_customerId number,
p_eventTypeId number,
p_out OUT SYS_REFCURSOR
)
as
begin
open p_out for
select e.Id from eventstable e
where
(NVL(p_customerId, 'x') = e.CustomerId OR NVL(p_customerId, 'x') = 'x')
AND (NVL(p_eventTypeId, 'x') = e.EventTypeId OR NVL(p_eventTypeId, 'x') = 'x')
order by Id asc;
end usp_test;
One column index can't help as it's not stored in index definition.
Is creating index on (customer id, event id, id ) allowed? This way all needed columns are in index...