I'm trying to implement a stored function in PL/SQL that finds the total number of cities visited by a given driver, return the driving license number.
I tried something like this, but i keep getting "Function created with compilation errors.". The thing is, oracle 11g doesn't show where is the error at?
Here is what i got.
create or replace function driverL return number
as licence L#%TYPE;
begin
select L#, count(T#)
from driver d
join trip t ON d.L# = t.L#
join leg l ON l.t# = l.t#;
return licence;
end;
/
How do i consider multiple visits to the same city as one visit?
If you are using SQL*Plus you can use the statement show errors after the create statement to show the compilation errors for the most recently created or altered stored procedure/function/package.
In your code I see at least two problems:
You are missing an INTO clause in your select
The column L# in your SELECT clause is ambiguously defined since two tables have a column with that name
Related
There are many threads on
column reference "something" is ambiguous
This is typical in most RDBMS when more than one tables involved have the same column name. However, all my tables have an unique prefix for their own columns therefore no any columns in the schema would share the same name. Reason being is in a big query, it's easy to know where a column is from! For example productid in order table is odrproductid, and in product table is pdtproductid, so this query ran perfectly in pgAdmin query tool:
select pdtproductid
from orders
join product on odrproductid = pdtproductid and ...
Paste into a function and call this function.
ERROR: column reference "odrproductid" is ambiguous
LINE 3: join product on odrproductid = ...
^
DETAIL: It could refer to either a PL/pgSQL variable or a table
column.
Obviously it can be resolved by adding alias and prefix columns in JOIN. But there is no variables defined in the function, and if it ran perfect interactively why not in the function? And added an alias for orders, it will pick pdtproductid and so on. If it's really an ambiguous column, logically it should have picked up from the up-front SELECT list.
Solution, paste the exact good sql into a variable and run it as dynamic SQL, it goes well. For example
AS $BODY$
declare pgsql text ;
begin
pgsql := 'select ... from orders join product on odrproductid = pdtproductid and ...';
return query execute pgsql;
end;
There is a PL/pgSQL variable, a function parameter or a return parameter with the same name. Avoid ambiguity by qualifying column references with the table name:
select pdtproductid
from orders
join product on orders.odrproductid = product.pdtproductid and ...
I have lots of experience with T-SQL (MS SQL Server).
There it is quite common to first select some set of records into a
table variable or say temp table t, and then work with this t
throughout the whole SP body using it just like a regular table
(for JOINS, sub-queries, etc.).
Now I am trying the same thing in Oracle but it's a pain.
I get errors all the way and it keeps saying
that it does not recognize my table (i.e. my table variable).
Error(28,7): PL/SQL: SQL Statement ignored
Error(30,28): PL/SQL: ORA-00942: table or view does not exist
I start thinking what at all is possible to do with this
table variable and what not (in the SP body) ?
I have this declaration:
TYPE V_CAMPAIGN_TYPE IS TABLE OF V_CAMPAIGN%ROWTYPE;
tc V_CAMPAIGN_TYPE;
What on Earth can I do with this tc now in my SP?!
This is what I am trying to do in the body of the SP.
UPDATE ( SELECT t1.STATUS_ID, t2.CAMPAIGN_ID
FROM V_CAMPAIGN t1
INNER JOIN tc t2 ON t1.CAMPAIGN_ID = t2.CAMPAIGN_ID
) z
SET z.STATUS_ID = 4;
V_CAMPAIGN is a DB view, tc is my table variable
Presumably you are trying to update a subset of the V_CAMPAIGN records.
While in SQLServer it may be useful to define a 'temporary' table containing the subset and then operate on that it isn't necessary in Oracle.
Simply update the table with the where clause you would have used to define the temp table.
E.g.
UPDATE v_campaign z
SET z.status_id = 4
WHERE z.column_name = 'a value'
AND z.status <> 4
I assume that the technique you are familiar with is to minimise the effect of read locks that are taken while selecting the data.
Oracle uses a different locking strategy so the technique is mostly unnecessary.
Echoing a comment above - tell us what you want to achieve in Oracle and you will get suggestions for the best way forward.
Im trying to create a function which takes in 3 parameters and returns one. The value of the returned parameter is obtained by querying two tables, however, one of the tables is on a different schema. The SQL im using is below:
create or replace FUNCTION tester
(
originaltext IN VARCHAR2, lang IN VARCHAR2, category IN VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
translatedttextval VARCHAR2(255) := '';
BEGIN
--if category is null, disregard category
if category is null then
SELECT distinct nvl(trans.translatedtext, originaltext)
INTO translatedttext
FROM tbl_translations trans, SECDEVSCHEMA.tbl_instanceids ids
WHERE trans.translatedlang = ids.id_a
AND ids.name = lang
AND trans.originaltext = originaltext;
end if;
RETURN translatedttextval;
END;
I've removed a bit of the query here which searches with category because it does something similar and has the same issue.
So, when I run this and pass in the params, I get an error which reads:
Error(16,46): PL/SQL: ORA-00942: table or view does not exist
If I do the following query it works fine when not on the SECDEVSCHEMA and returns the reults from tbl_instanceids which is on the SECDEVSCHEMA schema:
SELECT * FROM SECDEVSCHEMA.tbl_instanceids ids WHERE ids.id_a = 1234555;
I don't have DBA access to the DB but if I need some select priviledge granted or something I can get it done. Not sure if this the case though as the above query works.
Small additional question also:
where it says in the query
nvl(trans.translatedtext, originaltext)
If i wanted to surround the original text value with brackets when no translatedtext value exists, how would I go about this?
I'm using SQL Developer by the way in case that's important.
Thanks
for your smaller question
nvl(trans.translatedtext, '<'|| originaltext ||'>' )
This was down to a priviledges issue.
In SQL Developer, I expanded the tables package and selected the table I needed access to in the Function, right clicked and selected Priviledges > Grant.
As this is just a Development DB I just assigned all the priviledges to the appropriate schema or User and its working now.
Still unsure about this part though:
Small additional question also: where it says in the query nvl(trans.translatedtext, originaltext)
If i wanted to surround the original text value with brackets when no translatedtext value exists, how would >I go about this?
Any takers for this?
Thanks
I have some code that runs the same query in 2 different databases.
SELECT
P.MYID,
CASE WHEN
SUBSTR(P.MYID, 1, 1) NOT IN ('W') THEN
'YOUR_ID_IS_NOT_START_WITH_W'
ELSE
(SELECT OTHER_ID FROM PERSON WHERE NUMBER = '2554' )
END AS "ALTERNATE_ID"
FROM
PERS_INFO P
WHERE
P.NUMBER = '2554'
OTHER_ID in this example is a column that only exists in the 2nd database. Thats completely fine because the query will only execute in the 1st database when the id DOES NOT start with 'W'. In otherwords, this query will only ever run in the 1st database, when the MYID does not start with 'W' and will only ever run in the 2nd database when MYID does start with 'W'.
So the query would work in both databases, however, the query fails with an ORA-00904 in the first database because it says OTHER_ID is not legal in the first database (which is true, but i dont care). how do i force oracle to run the query anyways or work around this?
You could create a function in both databases to get the OTHER_ID value. It would just return null in your first database.
For example, in the first database:
create or replace function f_get_other_id(for_no in varchar2) return varchar2 is
begin
return null;
end;
In the second database:
create or replace function f_get_other_id(for_no in varchar2) return varchar2 is
v_other_id varchar2(100);
begin
select other_id into v_other_id from person where number = for_no;
return other_id;
end;
Then, your query can be:
select p.myid,
case
when substr(p.myid, 1, 1) not in ('W') then 'YOUR_ID_IS_NOT_START_WITH_W'
else f_get_other_id('2554')
end as "ALTERNATE_ID"
from pers_info p
where p.number = '2554'
Have you heard about the EXECUTE_IMMEDIATE (http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/executeimmediate_statement.htm) command or about DBMS_SQL (see http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm)?
If you are working with scripts on various databases that have different tables definition, this might be the solution for you, though this requires PL/SQL.
I'm not sure what the problem would be with adding this column to the database that it is currently absent from, but adding it seems like a pretty low effort and low risk exercise, and would permanently solve this kind of problem.
Probably you need to add another CASE after else checking the database name. Case db_name = db_name1 then your other_id query else some other query lk select 1 from dual... You can get db_name from v$database view.
Hey guys i cant get this trigger to work, ive worked on it for an hour or so and cant see to figure out where im going wrong, any help would be appreciated
CREATE OR REPLACE TRIGGER allergy
BEFORE INSERT ON
DECLARE
med VARCHAR2(20);
BEGIN
SELECT v.medication RCD.specify
INTO med
FROM visit v, relcondetails RCD
WHERE :new.medication = v.medication AND RCD.specifiy = 'allergies';
IF med = allergies THEN
RAISE_APPLICATION_ERROR(-20000, 'Patient Is alergic to this medication');
END IF;
END allergy;
When put into oracle
ERROR at line 6: ORA-04079: invalid
trigger specification
CREATE OR REPLACE TRIGGER allergy BEFORE INSERT ON
name of table here
FOR EACH ROW -- forgot this too
DECLARE
med VARCHAR2(20);
You should really be declaring this as %type.
med visit.medication%type;
BEGIN
SELECT v.medication RCD.specify
Requires a comma between columns
INTO med
Two columns need two variables
FROM visit v, relcondetails RCD
WHERE :new.medication = v.medication AND RCD.specifiy = 'allergies';
You have no join condition between your two tables, that's very bad. This query will perform a Cartesian between the two tables and then return all of them that have 'allergies' and :new.medication in their respective columns.
you also probably need a filter condition to limit the query to a particular patient or a particular visit. This query will do it for all patients and all their visits squared.
IF med = allergies THEN
I don't know what /allergies/ is in this IF. There's no variable that's defined as that and without quotes it's not a string.
RAISE_APPLICATION_ERROR(-20000, 'Patient Is alergic to this medication');
This error message reinforces what I said about your query. You think you're querying for a single patient but you're not.
END IF;
END allergy;
Seriously, if you're writing software to save a person from getting potentially life threatening medication then please consider some other line of work. I swear I'm not saying this to be rude, but your code sample shows almost no understanding of the pl/sql language or sql or any scrap of programming background. I think you started with some sample code and tried to modify it into something something. But you're really left with gibberish. I'm starting to think this is homework.
In addition to Mark's point that you are missing the table name, and Martin's point that you want this to be a row-level trigger, your actual body won't compile, for a couple of reasons.
You look like you're trying to select two columns, but you don't have a comma between them, and you only have one local variable in the INTO clause
You use an identifier allergies which is not declared anywhere.
I also doubt that your query is logically correct, but of course I don't know the database design so I can't say for sure.
BEFORE INSERT ON <TABLE NAME>
and why select both v.medication and RCD.specify when you're only selecting into one variable?
You've probably seen this but:
http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx
CREATE TRIGGER TriggerName
ON MyTableName
FOR MyEvent
AS
-- My Trigger Logic