vertical output - oracle

I have column name in the table:
select LASTNAME
FROM dbo.Employees
WHERE LASTNAME = 'Smith'
and output of the above query is
LASTNAME
Smith
I want output like
LASTNAME
S
m
i
t
h

With a little help of a numbers table.
SQL Server:
select substring(E.LASTNAME, N.N, 1) as LASTNAME
from Employees as E
inner join Numbers as N
on N.N between 1 and len(E.LASTNAME)
order by E.LASTNAME, N.N
Oracle:
select substr(E.LASTNAME, N.N, 1) as LASTNAME
from Employees E
inner join Numbers N
on N.N between 1 and length(E.LASTNAME)
order by E.LASTNAME, N.N;
SQL Fiddle

In SQL Server, if you don't have a table of numbers, then you can use CTE to generate the list:
;with cte (id, start, numb) as
(
select id, 1 start, len(lastname) numb
from employees
union all
select id, start + 1, numb
from cte
where start < numb
)
select c.id, substring(e.lastname, c.start, 1)
from employees e
inner join cte c
on c.start between 1 and len(e.lastname)
and c.id = e.id
order by e.id, e.lastname;
See SQL Fiddle With Demo

----- function for splitting
CREATE FUNCTION [dbo].[SPLIT_Test] (
#string VARCHAR(8000) )
RETURNS #table TABLE (strval VARCHAR(8000))
AS
BEGIN
IF LEN(#string)>=1
BEGIN
DECLARE #fulllen int=LEN(#string),#lastlen int=0
WHILE #fulllen>#lastlen
BEGIN
INSERT INTO #table
SELECT SUBSTRING(#string,1,1)
SET #string= RIGHT(#String, LEN(#String) - 1)
SET #lastlen=#lastlen+1
END
RETURN
END
RETURN
END
---- query
GO
DECLARE #name table(name varchar(500),row int IDENTITY(1,1))
INSERT INTO #name
select LASTNAME
FROM dbo.Employees
WHERE LASTNAME = 'Smith'
DECLARE #Finalname table(name varchar(50))
DECLARE #startrow int =(SELECT MAX(row) FROM #name)
,#endrow int =1
WHILE #startrow>=#endrow
BEGIN
INSERT INTO #Finalname
Select strval from [dbo].[SPLIT_test] ((SELECT name FROM #name where row=#endrow)) WHERE strval<>''-- removing empty spaces
SET #endrow=#endrow+1
END
SELECT * FROM #Finalname

Related

Need help in output for Oracle PLSQL Procedure

Am currently working on an oracle PL SQL procedure to list the project numbers, titles and names of employees who work on a project.
I am able to write a procedure that is able to get this info. However, I can only draw one output at a time as such:
1001 Computation
Alvin
Peter
How can I change my code to output all of the entries at the same time while printing them as such:
[Fragment Example][Showing only 1st 3 entries]
1001 Computation: Alvin, Peter
1002 Study methods: Bob, Robert
1003 Racing car: Robert
[Current Code]
create or replace procedure PROJECTGROUPS(projectid IN WorksOn.P#%TYPE)
is
PID Project.P#%TYPE;
PNAME Project.PTitle%TYPE;
ENAME Employee.Name%TYPE;
CURSOR query is
select Employee.Name from Employee
left outer join WorksOn On Employee.E# = WorksOn.E#
where WorksOn.P# = projectid
order by Employee.Name ASC
fetch first 20 rows only;
--
--
begin
select P#, PTitle into PID, PNAME from project where project.p# = projectid;
DBMS_OUTPUT.PUT_LINE(PID || ' ' || PNAME);
--
open query;
loop
fetch query into ENAME;
if query%NOTFOUND then exit;
end if;
DBMS_OUTPUT.PUT_LINE(ENAME);
end loop;
close query;
end PROJECTGROUPS;
You can directly use it in a single query and loop through it as follows:
CREATE OR REPLACE PROCEDURE PROJECTGROUPS (
PROJECTID IN WORKSON.P#%TYPE
) IS
BEGIN
FOR I IN (
SELECT PROJECT_NAME,
PTITLE,
LISTAGG(EMP_NAME,
',') WITHIN GROUP(
ORDER BY EMP_NAME
) AS EMP_NAMES
FROM (
SELECT P.P# PROJECT_NAME,
P.PTITLE,
E.EMPLOYEE.NAME EMP_NAME,
ROW_NUMBER() OVER(
PARTITION BY P.P#
ORDER BY E.NAME
) AS RN
FROM PROJECT P
JOIN WORKSON W
ON W.P# = P.P#
JOIN EMPLOYEE E
ON E.E# = W.E#
WHERE P.P# = PROJECTID
)
WHERE RN <= 20
GROUP BY PROJECT_NAME,
PTITLE
) LOOP
DBMS_OUTPUT.PUT_LINE(I.PROJECT_NAME
|| ' '
|| I.PTITLE
|| ' : '
|| I.EMP_NAMES);
END LOOP;
END PROJECTGROUPS;
Also, your query is not actually outer joined as you have used the condition in the WHERE clause.
left outer join WorksOn On Employee.E# = WorksOn.E# -- you want outer join
where WorksOn.P# = projected -- but the outer join is converted to inner join

Oracle SQL: How to remove duplicate employee Ids from results?

I need to write query to remove duplicate employee ids among 10,000 results
EmpID name
1 x
1 x
2 y
2 y
3 z
4 A
The result should be only:
EmpID name
3 z
4 A
Select * from EMPLOYEE where ?
How can I do this?
You need aggregation :
select e.empid, e.name
from employee e
group by e.empid, e.name
having count(*) = 1;
You can try below -
Select empid,name from EMPLOYEE
group by empid,name
having count(*)=1
I would really ask you to use other than the GROUP BY method as you will be able to fetch other fields of the EMPLOYEE table along with EMPID and NAME.
Using analytical function
SELECT * FROM
(SELECT T.*, COUNT(1) OVER (PARTITION BY EMPID) AS CNT
FROM EMPLOYEE)
WHERE CNT = 1;
Using NOT EXISTS
SELECT * FROM EMPLOYEE T
WHERE NOT EXISTS
(SELECT 1 FROM EMPLOYEE TIN
WHERE TIN.ID = T.ID AND TIN.ROWID <> T.ROWID);
Cheers!!

Function in Oracle that returns 2 different columns of a table

I'm trying to create a function that returns 2 different columns in a single table, can anyone help me with that?
I've already tried this way:
CREATE FUNCTION return_id_grade(subjectId IN NUMBER, semesterYear IN DATE , n IN INT, option IN INT)
RETURN NUMBER
IS studentId NUMBER(5),
IS studentGrade NUMBER(2,1);
BEGIN
SELECT DISTINCT student_id INTO studentId,
grade INTO studentGrade
FROM (SELECT studentId, grade, dense_rank() over (ORDER BY grade desc) rank FROM old_students)
WHERE subject_id = subjectId
AND semester_year = semesterYear
AND rank = n
AND rownum <= 1
CASE
WHEN option = 1 then RETURN(student_id)
WHEN option = 2 then RETURN(grade)
END;
END;
I expected to output the n'NTH grade of an university class and the student Id, but the actual can just output the option received on parameter field.
a.You cant use Select colum1 INTO variable1 , colum2 INTO variable2 . It has to be like :
Select column1 , column2 INTO variable1 , variable 2
b.Create an object type and use it as out parameter in a procedure
c.Have the option condition after the procedure is called.
Sample Code:
CREATE OR REPLACE TYPE ty_obj_idgrade AS OBJECT
(studentId NUMBER(5)
,studentGrade NUMBER(2,1)
);
CREATE OR REPLACE PROCEDURE return_id_grade(
subjectId IN NUMBER,
semesterYear IN DATE ,
n IN INT,
-- options IN INT
,p_idgrade OUT ty_obj_idgrade) IS
BEGIN
SELECT DISTINCT student_id --INTO studentId,
,grade --INTO studentGrade
INTO p_idgrade.studentId
,p_idgrade.grade
FROM (SELECT studentId
,grade
,dense_rank() over (ORDER BY grade desc) rank
,subject_id
,semester_year
FROM old_students )
WHERE subject_id = subjectId
AND semester_year = semesterYear
AND rank = n
AND rownum <= 1;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('we are inside when others -->'||sqlerrm);
END;
Call your procedure.
Since options was used as IN parameter , it should be availabe outside the prc/fnc
So this can be done after the prc/fnc call
If options = 1
THEN
value := p_idgrade.conatct
ELSE
value := p_idgrade.grade
END IF;
Hope it helps.
Did not try compiling it, but something like this should be close.
CREATE FUNCTION return_id_grade(subjectId IN NUMBER, semesterYear IN DATE , n IN INT, option IN INT)
RETURN NUMBER IS
studentId NUMBER(5),
studentGrade NUMBER(2,1);
BEGIN
SELECT DISTINCT student_id, grade
INTO studentId, studentGrade
FROM (SELECT studentId, grade, dense_rank() over (ORDER BY grade desc) rank FROM old_students)
WHERE subject_id = subjectId
AND semester_year = semesterYear
AND rank = n
AND rownum <= 1;
IF option = 1 then
RETURN studentId ;
ELSE
RETURN studentGrade ;
END IF;
END;
END;
However, this function is really not a good design. A function should perform a single task. If you want to return both items, create a PL/SQL record type, and use a stored procedure with an OUT parameter and return that in the procedure.
You can directly try to use OPTION in query as following:
CREATE FUNCTION RETURN_ID_GRADE (
SUBJECTID IN NUMBER,
SEMESTERYEAR IN DATE,
N IN INT,
OPTION IN INT
) RETURN NUMBER IS
LV_RETURN_NUMBER NUMBER(6, 1);
BEGIN
-- QUERY TO FETCH REQUIRED DATA ONLY
SELECT -- DISTINCT -- DISTINCT IS NOT NEEDED AS ROWNUM <= 1 IS USED
CASE
WHEN OPTION = 1 THEN STUDENT_ID
ELSE GRADE
END AS LV_RETURN_NUMBER
INTO LV_RETURN_NUMBER -- STORE VALUE BASED ON OPTION
FROM
(
SELECT
STUDENTID,
GRADE,
DENSE_RANK() OVER(
ORDER BY
GRADE DESC
) RANK
FROM
OLD_STUDENTS
)
WHERE
SUBJECT_ID = SUBJECTID
AND SEMESTER_YEAR = SEMESTERYEAR
AND RANK = N
AND ROWNUM <= 1;
RETURN LV_RETURN_NUMBER; -- RETURN THE VARIABLE
END;
Cheers!!

Oracle Function To Return List

I'm trying to develop a PLSQL function that outputs a list of employee names that I can then run through another script. I can't quite get it right though. I'm fairly new to PLSQL and am primarily used to building functions in Python, so I may be thinking about this the wrong way. At the end of all of this, I'd like to use the output of this within another script I'm writing.
Baseline Script:
SELECT EMPLOYEE FROM (
SELECT ID, EMPLOYEE, ROLE, STARTDATE,
ROW_NUMBER() OVER (PARTITION BY EMPLOYEE ORDER BY STARTDATE DESC, ID DESC) RN
FROM (
SELECT DISTINCT E.EMPLOYEE EMPLOYEE,
E.ID ID,
LR.DESCRIPTION ROLE,
ROLE_START_DATE STARTDATE
FROM EMPLOYEES E
JOIN ROLES R ON E.EMPLOYEE_ID = R.EMPLOYEE_ID
JOIN LU_ROLES LR ON R.ROLE_ID = LR.ROLE_ID
WHERE ROLE_START_DATE <= DATE '2017-12-03'))
WHERE RN = 1
My attempt at writing a PLSQL function:
CREATE FUNCTION get_employees(EMPLOYEE IN VARCHAR2)
RETURN VARCHAR2
IS EMPLOYEE_LIST;
BEGIN
SELECT EMPLOYEE FROM (
SELECT ID, EMPLOYEE, ROLE, STARTDATE,
ROW_NUMBER() OVER (PARTITION BY EMPLOYEE ORDER BY STARTDATE DESC, ID DESC) RN
FROM (
SELECT DISTINCT E.EMPLOYEE EMPLOYEE,
E.ID ID,
LR.DESCRIPTION ROLE,
ROLE_START_DATE STARTDATE
FROM EMPLOYEES E
JOIN ROLES R ON E.EMPLOYEE_ID = R.EMPLOYEE_ID
JOIN LU_ROLES LR ON R.ROLE_ID = LR.ROLE_ID
WHERE ROLE_START_DATE <= DATE '2017-12-03'))
WHERE RN = 1
RETURN EMPLOYEE_LIST
END;
I know I'm missing some syntax, I just don't know what and why...I'm reading through the docs at the moment to try to understand this. Any help you all could provide would be much appreciated!
Thanks!
Without your tables or sample data this has to be a bit of a guess, but a "fixed" version might be something like this:
create or replace type short_string_tt as table of varchar2(100)
/
create or replace function get_employees
( p_role_date_cutoff roles.role_start_date%type )
return short_string_tt
as
l_employee_list short_string_tt;
begin
select employee bulk collect into l_employee_list
from ( select employee
, row_number() over(partition by employee order by role_start_date desc, id desc) rn
from ( select distinct e.employee, e.id, lr.description, role_start_date
from employees e
join roles r
on r.employee_id = e.id
join lu_roles lr
on lr.role_id = r.role_id
where role_start_date <= p_role_date_cutoff )
)
where rn = 1;
return l_employee_list;
end;
/
If the number of rows returned is likely to be significant then you might look at making it a pipelined function, as these stream rows back as they are fetched rather than building the entire collection in memory before returning anything.
try this would it work ?
CREATE FUNCTION get_employees(EMPLOYEE IN VARCHAR2)
RETURN VARCHAR2
IS EMPLOYEE_LIST VARCHAR2(200);
BEGIN
SELECT EMPLOYEE into EMPLOYEE_LIST FROM (
SELECT ID, EMPLOYEE, ROLE, STARTDATE,
ROW_NUMBER() OVER (PARTITION BY EMPLOYEE ORDER BY STARTDATE DESC, ID DESC) RN
FROM (
SELECT DISTINCT EMPLOYEE EMPLOYEE,
E.ID ID,
LR.DESCRIPTION ROLE,
ROLE_START_DATE STARTDATE
FROM EMPLOYEES E
JOIN ROLES R ON E.EMPLOYEE_ID = R.EMPLOYEE_ID
JOIN LU_ROLES LR ON R.ROLE_ID = LR.ROLE_ID
WHERE ROLE_START_DATE <= DATE '2017-12-03'))
WHERE RN = 1;
RETURN EMPLOYEE_LIST;
END;

How to use 'EXIST' in a simple oracle query

I have a table called ‘MainTable’ with following data
Another table called ‘ChildTable’ with following data (foreighn key Number)
Now I want to fetch those records from ‘ChildTable’ if there exists at least one ‘S’ status.
But if any other record for this number id ‘R’ then I don’t want to fetch it
Something like this-
I tried following
Select m.Number, c.Status from MainTable m, ChildTable c
where EXISTS (SELECT NULL
FROM ChildTable c2
WHERE c2.status =’S’ and c2.status <> ‘R’
AND c2.number = m.number)
But here I am getting record having ‘R’ status also, what I am doing wrong?
You can try something like this
select num, status
from
(select id, num, status,
sum(decode(status, 'R', 1, 0)) over (partition by num) Rs,
sum(decode(status, 'S', 1, 0)) over (partition by num) Ss
from child_table) t
where t.Rs = 0 and t.Ss >= 1
-- and status = 'S'
Here is a sqlfiddle demo
The child records with 'R' might be associated with a maintable record that also has another child record with status 'S' -- that is what your query is asking for.
Select
m.Number,
c.Status
from MainTable m
join ChildTable c on c.number = m.number
where EXISTS (
SELECT NULL
FROM ChildTable c2
WHERE c2.status =’S’
AND c2.number = m.number) and
NOT EXISTS (
SELECT NULL
FROM ChildTable c2
WHERE c2.status =’R’
AND c2.number = m.number)
WITH ChildrenWithS AS (
SELECT Number
FROM ChildTable
WHERE Status = 'S'
)
,ChildrenWithR AS (
SELECT Number
FROM ChildTable
WHERE Status = 'R'
)
SELECT MaintTable.Number
,ChildTable.Status
FROM MainTable
INNER JOIN ChildTable
ON MainTable.Number = ChildTable.Number
WHERE MainTable.Number IN (SELECT Number FROM ChildrenWithS)
AND MainTable.Number NOT IN (SELECT Number FROM ChildrenWithR)

Resources