Function statement/programming on paper in Oracle SQL - oracle

i'm sorry for this probably a stupid question, but i have to make a couple of queries for a test, but i do not have access to a decent computer with the right software so basically i can only use pen and paper and have access to internet.
Assignment > Create a function that changes all the English consonant letters into uppercase and all the English
vowels into lowercase. (Hints: c CHAR; c > 'a', INSTR). Example:
funnycase('christmas time') => 'CHRiSTMaS TiMe'
I was thinking more like a programmer and thought that maybe there is a way to do it like:
CREATE FUNCTION f1 (in_word IN VARCHAR(20)) RETURN VARCHAR (20)
IS answer VARCHAR(20);
BEGIN;
DECLARE CHAR c;
So here is where i fail and i need help:
FOR char IN in_word .... if char NOT IN ("a","e", "i", "o", "u") THEN UPPERCASE char
Else pass?
Another thing is i do not know how to add it to answer
Answer add char?
Return answer;
End;
I am totally unclear how instr works and I can't understand how to use it without help of a computer.
I am programming with pen and paper.
Also I wrote another query:
Study how LISTAGG function works. Write a query that lists all the departments ordered by
department name. Show
· department name,
· location,
· comma-separated list of people in that department ordered by name,
· average salary compared to company average in %
Only knowing data from there https://courses.cs.ut.ee/MTAT.03.288/2013_fall/uploads/Main/p06_data.txt
SELECT dname, loc, LISTAGG(ename, "; ") WITHIN GROUP (ORDER BY ename),
TRUNC( ( (SUM(sal) OVER () ) / ( SUM(sal) OVER (PARTITION BY dname) ) , 2 ) AS AvgSal
FROM (SELECT DISTINCT t1.dname, t1.location, t2.ename, t2.sal FROM Dept t1, EMP t2
WHERE t1.deptno = t2.deptno ) ORDER BY dname;

Related

ORACLE SUBQUERY NOT WORKING IN (IN CONDITION)

I need help
i have records 123,456,789 in rows when i am execute like
this one is working
select * from table1 where num1 in('123','456')
but when i am execute
select * from table1 where num1 in(select value from table2)
no resultset found - why?
Check the DataType varchare2 or Number
try
select * from table1 where num1 in(select to_char(value) from table2)
Storing comma separated values could be the cause of problem.
You can try using regexp_substr to split comma.
First and foremost, an important thing to remember: Do not store numbers in character datatypes. Use NUMBER or INTEGER. Secondly, always prefer VARCHAR2 datatype over CHAR if you wish to store characters > 1.
You said in one of your comments that num1 column is of type char(4). The problem with CHAR datatype is that If your string is 3 characters wide, it stores the record by adding extra 1 space character to make it 4 characters. VARCHAR2 only stores as many characters as you pass while inserting/updating and are not blank padded.
To verify that you may run select length(any_char_col) from t;
Coming to your problem, the IN condition is never satisfied because what's actually being compared is
WHERE 'abc ' = 'abc' - Note the extra space in left side operator.
To fix this, one good option is to pad the right side expression with as many spaces as required to do the right comparison.The function RPAD( string1, padded_length [, pad_string] ) could be used for this purpose.So, your query should look something like this.
select * from table1 where num1 IN (select rpad(value,4) from table2);
This will likely utilise an index on the column num1 if it exists.
The other one is to use RTRIM on LHS, which is only useful if there's a function based index on RTRIM(num1)
select * from table1 where RTRIM(num1) in(select value from table2);
So, the takeaway from all these examples is always use NUMBER types to store numbers and prefer VARCHAR2 over CHAR for strings.
See Demo to fully understand what's happening.
EDIT : It seems You are storing comma separated numbers.You could do something like this.
SELECT *
FROM table1 t1
WHERE EXISTS (
SELECT 1
FROM table2 t2
WHERE ',' ||t2.value|| ',' LIKE '%,' || rtrim(t1.num1) || ',%'
);
See Demo2
Storing comma separated values are bound to cause problems, better change it.
Let me tell you first,
You have stored values in table2 which is comma seperated.
So, how could you match your data with table1 and table2.
Its not Possible.
That's why you did not get any values in result set.
I found the Solution using string array
SELECT T.* FROM TABLE1 T,
(SELECT TRIM(VALUE)AS VAL FROM TABLE2)TABLE2
WHERE
TRIM(NUM1) IN (SELECT COLUMN_VALUE FROM TABLE(FUNC_GETSTRING_ARRAY(TABLE2.VAL)))
thanks

oracle regexp_replace to remove commas between Quote

Please can anybody help me out please. Have been trying for days to get a regexp_replace to remove commas between quotes irrespective of the commas position.
Example
cold, gold, "Block 12C, Jones Avenue, Broad Street, London", car
Expected Answer
cold, gold, "Block 12C Jones Avenue Broad Street London", car
thanks in advance
You could extract the contents within double quotes(REGEXP_SUBSTR), replace the commas, and stuff it back to the old string using replace.
select REPLACE (whole_str,quoted_str,REPLACE (quoted_str,',')) FROM
(
select
whole_str,
REGEXP_SUBSTR( whole_str, '^[^"]*("[^"]+")',1,1,NULL,1) quoted_str
FROM yourtable
);
DEMO
Note that this could also be done using INSTR,SUBSTR which could be more efficient, but hard to read.
You can use regexp_replace to get the desired output :
with t( id , val ) as(
select 1,'cold, gold, "Block 12C, Jones Avenue, Broad Street, London", car' from dual union
select 2,'"Block 12C, Jones Avenue, Broad Street, London", car, cold, gold' from dual )
select id, val value
from t
model dimension by( id ) measures( val )
rules iterate(100)( val[any] = regexp_replace(val[cv()],',(([^"]*"){2})*([^"]*"[^"]*)$',' \1\3') );
d E m O
I doubt that there is a single regular expression function that will achieve the desired result. The "obvious" line of attack is to chop the input string into pieces and delete commas from each double-quoted substring as needed. (Unless each string has at most ONE double-quoted substring, in which case the problem has easier answers - but judging from the sample input string, it is possible that the same needs to be done for input strings with an arbitrary number of double-quoted substrings.)
Here is a solution using a recursive WITH clause - so it requires Oracle 11.2 or higher. (For earlier versions, a solution with a CONNECT BY hierarchical query can be used instead.) I wrote it with regular expressions, as requested; if speed becomes an issue, it can be re-written with standard INSTR, SUBSTR and REPLACE functions.
In the first factored subquery (subquery in the WITH clause) I created a few more inputs, to test whether the solution returns the correct result in different situations.
with
inputs ( str ) as (
select 'cold, gold, "Block 12C, Jones Ave., London", car' from dual union all
select '"One, two, three","Four, five six,",' from dual union all
select 'No, double-quotes, in this, string' from dual union all
select 'No commas in "double quotes" here' from dual
),
r ( str, init, quoted, fin ) as (
select str, null, null, str
from inputs
union all
select str,
init || replace(quoted, ',') || regexp_substr(fin, '[^"]*'),
regexp_substr(fin, '"[^"]*"'),
regexp_substr(fin, '([^"]*"){2}(.*)', 1, 1, null, 2)
from r
where quoted is not null or fin is not null
)
select str, init as new_str
from r
where quoted is null and fin is null
;
STR NEW_STR
--------------------------------------------- -------------------------------------------
No, double-quotes, in this, string No, double-quotes, in this, string
cold, gold, "Block 12C, Jay Ave, London", car cold, gold, "Block 12C Jay Ave London", car
No commas in "double quotes" here No commas in "double quotes" here
"One, two, three","Four, five six,", "One two three","Four five six",

Oracle change any string to a number

I'm having this problem we have this database witch IDS are stored in varchar2 type this ids contains Letters.
Is there any solution to convert a string to a number no matter what the value if this string.
for example there is : SELCT ASCII('t') FROM DUAL; result : 116.
but ASCII accept only one CHAR Hope you get the idea. sorry for my english
use oracle translate method to replace A-Z or a-z characters with numbers.
then use to_number to get number from it.
select translate('A1B2C3', 'ABC', '456') from dual; --result '415263'
select to_number(translate('A1B2C3', 'ABC', '456')) from dual; --result 415263
translate function documentation
The Oracle/PLSQL TRANSLATE function replaces a sequence of characters in a string with another set of characters. However, it replaces a single character at a time.
For example, it will replace the 1st character in the string_to_replace with the 1st character in the replacement_string. Then it will replace the 2nd character in the string_to_replace with the 2nd character in the replacement_string, and so on.
EDIT: After discussing further with the OP, it turns out he needed a function (in the mathematical sense) from short strings to integers. Such a function is ORA_HASH. The OP decided that ORA_HASH is likely what is needed for his project.
https://docs.oracle.com/cd/B28359_01/server.111/b28286/functions112.htm#SQLRF06313
The solution below is kept for historical perspective.
You could use the analytic function DENSE_RANK to assign numbers to strings.
For example:
with
employees ( id, first_name, last_name ) as (
select 'ABC', 'Jane', 'Smith' from dual union all
select 'ABD', 'Jane', 'Dryer' from dual union all
select 'XYZ', 'Mike', 'Lopez' from dual
)
-- End of simulated inputs (for testing purposes only).
-- Solution (SQL query) begins below this line.
select id, dense_rank() over (order by id) as num_id, first_name, last_name
from employees
;
ID NUM_ID FIRST_NAME LAST_NAME
--- ------ ---------- ---------
ABC 1 Jane Smith
ABD 2 Jane Dryer
XYZ 3 Mike Lopez

PL/SQL Stored proc that uses a comma separated parameter to drive a dynamic LIKE clause?

Is there a fairly simple way to take an input parameter containing a comma seperated list of prefixes and return a cursor based on a select statement that uses these?
i.e. (Pseudocode)
PROCEDURE get_by_prefix(p_list_of_prefixes IN varchar2, r_csr OUT SYS_REFCURSOR)
IS
BEGIN
OPEN r_csr FOR
SELECT * FROM my_table where some_column LIKE (the_individual_fields_from p_list_of_prefixes ||'%')
END
I've tried various combinations, and now have two problems - coercing the input into a suitable table (I think it needs to go into a table type rather than a VARCHAR2_TABLE), and secondly getting the like clause to be effectively a SELECT from an internal 'pseudotable'...
EDIT: It seems that people are suggesting ways to use 'IN' with a set of potential values - whereas Im looking at using LIKE. I could use a similar technique - building up dynamic SQL, but was wondering if there isnt a more elegant way...
PL/SQL has no concept of a comma-separated list and no built-in splitter as in Perl etc, so you'll have to use one of the hand-rolled methods such as this one:
https://stewashton.wordpress.com/2016/08/01/splitting-strings-surprise
(Other methods are available.) Then it's just a matter of either populating a collection in one step and using it in the next, or else combining the two as something like this:
declare
p_list_of_prefixes varchar2(100) := 'John,Jim,Jules,Janice,Jenny';
begin
open :refcur for
with params as
( select x.firstname
from xmltable(
'ora:tokenize($X, "\,")'
passing p_list_of_prefixes as x
columns firstname varchar2(4000) path '.'
) x
)
, people as
( select 'Dave Clark' as fullname from dual union all
select 'Jim Potter' from dual union all
select 'Jenny Jones' from dual
)
select x.firstname, p.fullname
from params x
left join people p on p.fullname like x.firstname || '%';
end;
Output:
FIRSTNAME FULLNAME
-------------- -----------
John
Jim Jim Potter
Jules
Janice
Jenny Jenny Jones
Using LIKE the way you want is easy, but it is the wrong solution. (See my Comment under the original post).
Anyway - if by order of your superiors, or some other semi-legitimate reason, you must use a LIKE condition, it should look something like this:
... where ',' || p_list_of_whatever || ',' like '%,' || some_column || ',%
Concatenating commas at both ends of both sides of the comparison is needed, because you don't want Jo in the column to match John in the input list. Start from there and you will see why you need the commas on the right-hand side, and then follow from there and you will see why you need them on the left also.

How to efficiently evaluate rows and sub records in single PL/SQL

I'm struggling writing PL/SQL (I'm new to PL/SQL) and I'm not sure how to structure SQL and loops for something like this.
I have 128 lines of SQL to create something like the following cursor:
ID Course Grade Attend? Date
123 MATH091 B Y 5/15
123 BIOL101 F N 3/10
123 ENGL201 W Y 1/2
456 MATH091 A Y 5/16
456 CHEM101 C Y 5/16
456 POLS301 NULL NULL NULL
With each ID, I need to several comparisons across the courses (e.g. which has the latest date, or were all courses attended). These comparisons need to be done in a certain order so that when they hit one that is true, they are flagged with a code and excluded from subsequent comparisons.
For example:
All courses attended? If true, output as attended and remove from next steps.
Find and store the latest date with a passing grade.
Find and store the latest date with a non-passing grade.
Is the later date after a course with a null grade? If true, output as coming back and remove from next steps.
Etc.
Each condition can be easily written in a SQL, but I don't know/understand the appropriate structure to loop through this process.
Is there syntax that can accomplish this easily?
We're on Oracle 11g and we do not have permissions to write to a temporary table.
I don't think you need PL/SQL for this. Except for the "unknown" requirement "etc." this can all be done in a single SQL statement:
Something like:
select id, course, grade, attended, attendance_date,
count(distinct case when attended = 'Y' then course end) over (partition by id) courses_attended,
count(distinct course) over () as total_courses,
case
when count(distinct case when attended = 'Y' then course end) over (partition by id) = count(distinct course) over () then 'yes'
else 'no'
end as all_courses_attended,
max(case when attended <> 'F' then attendance_date else null end) over (partition by id) as latest_passing_date,
max(case when attended = 'F' then attendance_date else null end) over (partition by id) as latest_non_passing_date
from attendees
order by id;
Btw: the attended column is not necessary if you have an attendance_date. If that date is not NULL than obviously the student attended the course. Otherwise she/he didn't.
Of course I have no idea what the "etc." steps should do though....
SQLFiddle example: http://sqlfiddle.com/#!4/e7c95/1

Resources