Getting multiple column values in single row - oracle

This is my actual result and i have used for store details of client
SID ATTRIBUTE_VALUES ATTRIBUTE_ID
---------- ---------------- -----------
20 101010 MEMBERSHIP_NO
20 ALLEN MEMBERSHIP_NAME
20 WARD MEMBERSHIP_LNAME
30 101011 MEMBERSHIP_NO
30 MARTIN MEMBERSHIP_NAME
30 BLAKE MEMBERSHIP_LNAME
In this I need to retrieve the details based on membership no and also using sid. sid is unique number .
I need to show the result as
SID MEMBERSHIP_NO MEMBERSHIP_NAME MEMBERSHIP_LNAME
----- ------------- --------------- ----------------
20 101010 ALLEN WARD
30 101011 MARTIN BLAKE
Above 3 attributes are constant using that values only client number, names are inserts into table. Im stuck with this...I need to solution for it.
This is the query i have used for retrieve the details. But it returns empty
SELECT sid, LISTAGG(attribute_value, ',') WITHIN GROUP
(ORDER BY attribute_value) AS att FROM customer_attributes WHERE
sid ='20' AND attribute_value='101010'
AND attribute_id ='MEMBERSHIP_NO'
AND attribute_id ='MEMBERSHIP_NAME'
AND attribute_id ='MEMBERSHIP_LNAME'
GROUP BY SID;

You can do this by join. LISTAGG wont create 3 columns:
select
a.sid,
a.ATTRIBUTE_VALUES MEMBERSHIP_NO,
b.ATTRIBUTE_VALUES MEMBERSHIP_NAME,
c.ATTRIBUTE_VALUES MEMBERSHIP_LNAME
from tbl a
join tbl b on a.sid = b.sid
join tbl c on c.sid = b.sid
where a.ATTRIBUTE_ID = 'MEMBERSHIP_NO'
and b.ATTRIBUTE_ID = 'MEMBERSHIP_NAME'
and c.ATTRIBUTE_ID = 'MEMBERSHIP_LNAME'
Demo sqlfiddle
This also gives the same result;
select
sid,
Max(DECODE(ATTRIBUTE_ID, 'MEMBERSHIP_NO', ATTRIBUTE_VALUES, '')) MEMBERSHIP_NO,
Max(DECODE(ATTRIBUTE_ID, 'MEMBERSHIP_NAME', ATTRIBUTE_VALUES, '')) MEMBERSHIP_NAME,
Max(DECODE(ATTRIBUTE_ID, 'MEMBERSHIP_LNAME', ATTRIBUTE_VALUES, '')) MEMBERSHIP_LNAME
from tbl
group by sid
order by sid
Same can be done by Pivot also
select *
from tbl
pivot(
max(ATTRIBUTE_VALUES) for ATTRIBUTE_ID in (
'MEMBERSHIP_NO' MEMBERSHIP_NO,
'MEMBERSHIP_NAME' MEMBERSHIP_NAME,
'MEMBERSHIP_LNAME' MEMBERSHIP_LNAME
)
)
order by sid

Related

How to use a rowcount in select statement to modify the query to fetch data for 10 days , if rowcount is 0 for 5 days?

I need to modify my script using rowcount to check if the data in table or not?. Here, i write the query to select a data for last 5 days from current system date. But sometimes there is no data in table for 5 days. So i need to fetch for 10 day or more.
Query:
Select ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
from JOB_SUMMARY_EXT e, ob_summary s, enterprise ep
where e.id = s.id and e.name_res_key = 'Model'
and s.job_id in (select id from job_summary where
trunc(start_date) > trunc(sysdate) -10 and service_name ='Model2' )
I don't know how to modify my Query using rowcount. If rowcount is 0 then i want select data for 10 days.Otherwise it should to fetch for 5 days automatically. I want this to be done as single query.
It looks that you want to select the last 5 "days" from that table. So, why would you anchor to SYSDATE if there aren't rows for each of those days? I'd suggest another approach: literally, select last 5 days. Here's how.
As I don't have your tables, I'm using Scott's EMP table which contains information about employees. It is an ancient one so HIREDATE column is set to 1980s, but never mind that. Sorting employees by HIREDATE in descending order shows:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select ename, hiredate from emp order by hiredate desc;
ENAME HIREDATE
---------- ----------
ADAMS 12.01.1983 1.
SCOTT 09.12.1982 2.
MILLER 23.01.1982 3.
FORD 03.12.1981 4.
JAMES 03.12.1981 4.
KING 17.11.1981 5. --> I want to fetch rows up to KING
MARTIN 28.09.1981
TURNER 08.09.1981
CLARK 09.06.1981
BLAKE 01.05.1981
JONES 02.04.1981
WARD 22.02.1981
ALLEN 20.02.1981
SMITH 17.12.1980
14 rows selected.
SQL>
As you can see, the 4th date is shared by two employees so I want to include them both. DENSE_RANK analytic function helps:
SQL> with last5 as
2 (select ename,
3 job,
4 sal,
5 hiredate,
6 dense_rank() over (order by hiredate desc) rnk
7 from emp
8 )
9 select ename, job, sal, hiredate
10 from last5
11 where rnk <= 5;
ENAME JOB SAL HIREDATE
---------- --------- ---------- ----------
ADAMS CLERK 1100 12.01.1983
SCOTT ANALYST 3000 09.12.1982
MILLER CLERK 1300 23.01.1982
JAMES CLERK 950 03.12.1981
FORD ANALYST 3000 03.12.1981
KING PRESIDENT 5000 17.11.1981
6 rows selected.
SQL>
What does it do? The LAST5 CTE sorts employees (as above), DENSE_RANK ranks them; finally, the last SELECT (which begins at line #9) fetches desired rows.
In your case, that might look like this:
with last5 as
(select id,
dense_rank() over (order by start_date desc) rnk
from job_summary
where service_name = 'Model2'
)
select ep.enterprise_name,
s.id,
s.submission_date,
e.value
from job_summary_ext e
join ob_summary s on e.id = s.id
join last5 t on t.id = s.id
join enterprise ep on <you're missing join condition for this table>
where e.name_res_key = 'Model';
Note that you're missing join condition for the ENTERPRISE table; if that's really so, no problem - you'd use cross join for that table, but I somehow doubt that you want that.
Finally, as you use SQL*Plus, perhaps you don't need to concatenate all columns and separate them by the pipe | sign - set it as a column separator, e.g.
SQL> set colsep '|'
SQL>
SQL> select deptno, dname, loc from dept;
DEPTNO|DNAME |LOC
----------|--------------|-------------
10|ACCOUNTING |NEW YORK
20|RESEARCH |DALLAS
30|SALES |CHICAGO
40|OPERATIONS |BOSTON
SQL>
If you want to
return 10 last days if select count(*) returns 0, or
return 5 last days if select count(*) returns a positive number
then something like this might help (again based on Scott's EMP table):
with
tcnt as
-- count number of rows; use your own requirement, I'm checking
-- whether someone got hired today. In Scott's EMP table, nobody was
-- so CNT = 0
(select count(*) cnt
from emp
where hiredate >= trunc(sysdate)
)
select e.ename, e.job, e.sal, e.hiredate
from emp e cross join tcnt c
where e.hiredate >= case when c.cnt = 0 then trunc(sysdate) - 10
else trunc(sysdate) - 5
end;
Apply it to your tables; I don't know which of those 3 tables' count you want to check.
Tried to add in comments but it was too long for comments and Not clear on count based on but here is case in where clause substitute your count statement with nvl function
SELECT ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
FROM JOB_SUMMARY_EXT e,
ob_summary s,
enterprise ep
WHERE e.id = s.id
AND e.name_res_key = 'Model'
AND s.job_id IN
(SELECT id
FROM job_summary
WHERE service='Model'
AND trunc(start_date) >
CASE WHEN
(WRITE your SELECT COUNT criteria WITH NVL FUNCTION)<=0 THEN
trunc(sysdate) -10
ELSE trunc(sysdate)-5
END )

Reduction columns are not working for group by expression in oracle

I tried to concat two columns and using group by expression, but it is not work. how can I grouped multiple columns in oracle. first_name and last_name are reduction data.
SELECT employee_id,
employee_name,
employee_unique
FROM
(SELECT a.id AS employee_id,
(a.first_name
|| a.last_name) AS employee_name,
b.employee_unique
FROM A a
INNER JOIN b
ON a.id=b.employee_id
GROUP BY a.id,
b.employee_unique,
(a.first_name
|| a.last_name)
);
What does "not work" mean? It works for me (though, as I don't have your tables, I used Scott's EMP and DEPT, but everything else is more or less the same). If it isn't correct, you should explain what is wrong with it. If you want us to work with your data, please, provide CREATE TABLE and INSERT INTO sample data.
SQL> select employee_id,
2 employee_name,
3 employee_unique
4 from
5 (select a.empno as employee_id,
6 a.ename || a.job as employee_name,
7 b.dname as employee_unique
8 from emp a
9 inner join dept b
10 on a.deptno=b.deptno
11 group by a.empno,
12 b.dname,
13 a.ename || a.job
14 );
EMPLOYEE_ID EMPLOYEE_NAME EMPLOYEE_UNIQU
----------- ------------------- --------------
7654 MARTINSALESMAN SALES
7876 ADAMSCLERK RESEARCH
7566 JONESMANAGER RESEARCH
7698 BLAKEMANAGER SALES
7844 TURNERSALESMAN SALES
7369 SMITHCLERK RESEARCH
7788 SCOTTANALYST RESEARCH
7900 JAMESCLERK SALES
7902 FORDANALYST RESEARCH
7782 CLARKMANAGER ACCOUNTING
7934 MILLERCLERK ACCOUNTING
7499 ALLENSALESMAN SALES
7521 WARDSALESMAN SALES
7839 KINGPRESIDENT ACCOUNTING
14 rows selected.
SQL>
Though, as there's nothing really to be grouped (no aggregation here), you could have used distinct (without group by clause) and get the same result:
select employee_id,
employee_name,
employee_unique
from
(select distinct
a.empno as employee_id,
a.ename || a.job as employee_name,
b.dname as employee_unique
from emp a
inner join dept b
on a.deptno=b.deptno
);

Oracle aggregate functions on strings

I have an employee with multiple managers. The manager name field has (firstname,lastname) and the email field has(last.first#email.com).There is no Mgr id.
So, when I try to group this by employee id to get the max of Mgr name and email, some times I end up getting the wrong name/email id combination.
ex:
person Mgr_name Mgr_email
------- --------- ----------
111 brad,pitt pitt.brad#test.com
111 mike,clark clark.mike#test.com
when I group it by person and get the max(mgr_name),mgr_email, I get
person max(Mgr_name) max(Mgr_email)
------- --------- ----------
111 mike,clark pitt.brad#test.com
How do I get the correct email/name combination?
Use row_number analytical function instead:
with t(person ,Mgr_name , Mgr_email) as (
select 111 ,'brad,pitt' , 'pitt.brad#test.com' from dual union all
select 111 ,'mike,clark' , 'clark.mike#test.com' from dual )
select person ,Mgr_name , Mgr_email from (
select t1.*, row_number() over (order by mgr_name) num from t t1)
where num = 1
This get max mgr_name with correct email.
Output:
PERSON MGR_NAME MGR_EMAIL
---------- ---------- -------------------
111 brad,pitt pitt.brad#test.com
You could use a subselect to obtain the max mgr_name for each person in the table then join it back to the base results to limit to only display each persons "Max" manager...
SELECT t1.Person, t1.Mgr_name, t1.mgr_email
FROM tableName t1
INNER JOIN (Select max(mgr_name) mname, Person from TableName group by person) t2
on t1.mgr_name = t2.mname
and t2.Person = T1.Person

display manager name and count of employees reporting him in employees table

I want to display manager_name and count of employees reporting him in employees table.I want to sort the data based on count IE maximum employees reporting to a manager should come first.
I tried to write self join but i could not get the out put .
EMPLOYEE_ID FIRST_NAME MANAGER_ID SALARY HIRE_DATE
198 Donald 124 2600 21-JUN-99
199 Douglas 124 2600 13-JAN-00
200 Jennifer 101 4400 17-SEP-87
201 Michael 100 13000 17-FEB-96
202 Pat 201 6000 17-AUG-97
203 Susan 101 6500 07-JUN-94
204 Hermann 101 10000 07-JUN-94
205 Shelley 101 12000 07-JUN-94
206 William 205 8300 07-JUN-94
100 Steven 24000 17-JUN-87
101 Neena 100 17000 21-SEP-89
the table name is employees and i want to see names also
You can use the aggregate function COUNT and ORDER BY clause
You didn't mention the table name assuming the table name as EMPLOYEES, below query would help you.
SELECT MANAGER_ID, COUNT(EMPLOYEE_ID) as EMP_COUNT
FROM EMPLOYEES
GROUP BY MANAGER_ID
ORDER BY EMP_COUNT DESC;
Here EMP_COUNT is the column alias name.If you don't want any column alias you can simply use the query below.
SELECT MANAGER_ID, COUNT(EMPLOYEE_ID)
FROM EMPLOYEES
GROUP BY MANAGER_ID
ORDER BY COUNT(EMPLOYEE_ID) DESC;
If you want to sort by ascending order instead of DESC you can use ASC.
We can get this output using an analytical function:
SELECT E.EMPID,E.EMPNAME as "Manager Name",M.EMPNAME AS "Employee Name",count(*) over(partition by e.empid) reportee_count
from empmgid m,empmgid e where M.MAGID=e.EMPID order by reportee_count desc;
Please employ the following SQL-Query:
SELECT
e.empno,
e.ename,
e1.empcnt
FROM
emp e,
(
SELECT
mgr,
COUNT(*) empcnt
FROM
emp
GROUP BY
mgr
) e1
WHERE
e.empno = e1.mgr;
-- Restricting which manager is having two employees working under them
-----------------------------------------------------------------------
SELECT E1.* FROM
(
SELECT E1.EMPNO,E1.ENAME AS EMPLOYE,
M1.ENAME AS MANAGERS,
COUNT(*)
OVER
(
PARTITION BY E1.EMPNO
) EMPCNT
FROM EMP E1,EMP M1
WHERE M1.MGR=E1.EMPNO
) E1
WHERE EMPCNT = 2;
select count(distinct manager_id) from employees;
select count(distinct manager_id) from employees;
Ans:
COUNT(DISTINCTMANAGER_ID)
18

Oracle UPDATE Question

Can the update described below be completed in one UPDATE statement?
I want to update the Operators.name_id values with the Users.name_id values by joining on Users.name = Operators.op_name. Both Users.name and Operators.op_name are have a unique.
I know the situation described below doesn't follow "best practices", but it's a much simpler example of what I'm trying to do: namely updating a field with the value from another joined table.
Table: Users
user_id name
----------------
34 Billy
43 Jimmy
50 Joe
Table: Operators (before UPDATE)
op_id op_name user_id
-------------------------
12 Billy 35
35 Jimmy 46
33 Joe 99
Table: Operators (after UPDATE)
op_id op_name name_id
-------------------------
12 Billy 34
35 Jimmy 43
33 Joe 50
UPDATE operators o
SET user_id =
(
SELECT u.user_id
FROM users u
WHERE o.op_name = u.name
)
WHERE o.op_name IN
(
SELECT name
FROM users
)
In Oracle 10g, more efficient:
MERGE
INTO operators o
USING users u
ON (u.name = o.op_name)
WHEN MATCHED THEN
UPDATE
SET user_id = u.user_id
update (
select oo.user_id, uu.user_id uu_id
from operators oo
join
users uu on uu.name = oo.op_name
)
set user_id = uu_id
Here's the full test script (I'm using my_users and my_operators so as not to mess with your data).
drop table my_users;
create table my_users (
user_id number(2),
name varchar2(30) unique
)
;
insert into my_users (user_id, name)
select 34, 'Billy' from dual union all
select 43, 'Jimmy' from dual union all
select 50, 'Joe' from dual
;
drop table my_operators;
create table my_operators (
op_id number(2),
op_name varchar2(30) unique,
user_id number(2)
)
;
insert into my_operators (op_id, op_name, user_id)
select 12, 'Billy', 35 from dual union all
select 35, 'Jimmy', 46 from dual union all
select 33, 'Joe', 99 from dual
;
update (
select oo.user_id, uu.user_id uu_id
from my_operators oo
join
my_users uu on uu.name = oo.op_name
)
set user_id = uu_id
;
select * from my_operators;

Resources