CTAS query in Oracle for Index Organized tables - oracle

Is it possible to write a CTAS (create table as select ...) query in Oracle to create Index Organized tables ?
Have looked on the net and tried a few versions of the CTAS, but unable to find a working sample/quide query.

Short answer: yes.
Example, based on the JOB_HISTORY table:
table JOB_HISTORY
SQL> select * from hr.job_history ;
EMPLOYEE_ID START_DATE END_DATE JOB_ID DEPARTMENT_ID
102 13-JAN-93 24-JUL-98 IT_PROG 60
101 21-SEP-89 27-OCT-93 AC_ACCOUNT 110
101 28-OCT-93 15-MAR-97 AC_MGR 110
201 17-FEB-96 19-DEC-99 MK_REP 20
114 24-MAR-98 31-DEC-99 ST_CLERK 50
122 01-JAN-99 31-DEC-99 ST_CLERK 50
200 17-SEP-87 17-JUN-93 AD_ASST 90
176 24-MAR-98 31-DEC-98 SA_REP 80
176 01-JAN-99 31-DEC-99 SA_MAN 80
200 01-JUL-94 31-DEC-98 AC_ACCOUNT 90
Query (for generating sample data)
-- every employee must do every job (only for a day at a time ...)
select E.employee_id, J.job_id
, sysdate - ( row_number() over ( order by E.employee_id, J.job_id ) ) dt
from (
select unique employee_id from hr.job_history
) E cross join (
select unique job_id from hr.job_history
) J ;
--
EMPLOYEE_ID JOB_ID DT
101 AC_ACCOUNT 10-FEB-20
101 AC_MGR 09-FEB-20
101 AD_ASST 08-FEB-20
101 IT_PROG 07-FEB-20
101 MK_REP 06-FEB-20
101 SA_MAN 05-FEB-20
101 SA_REP 04-FEB-20
101 ST_CLERK 03-FEB-20
102 AC_ACCOUNT 02-FEB-20
102 AC_MGR 01-FEB-20
...
200 ST_CLERK 25-DEC-19
201 AC_ACCOUNT 24-DEC-19
201 AC_MGR 23-DEC-19
201 AD_ASST 22-DEC-19
201 IT_PROG 21-DEC-19
201 MK_REP 20-DEC-19
201 SA_MAN 19-DEC-19
201 SA_REP 18-DEC-19
201 ST_CLERK 17-DEC-19
--56 rows selected.
CTAS
-- heap organized table
create table heap_empjobs (
employee_id, job_id, dt
, constraint heap_empjobs_pk primary key( employee_id, job_id, dt )
)
as
select E.employee_id, J.job_id
, sysdate - ( row_number() over ( order by E.employee_id, J.job_id ) ) dt
from (
select unique employee_id from hr.job_history
) E cross join (
select unique job_id from hr.job_history
) J
;
-- Table created.
-- index organized table
create table iot_empjobs (
employee_id, job_id, dt
, constraint iot_pk primary key( employee_id, job_id, dt ) -- also works without naming the constraint
)
organization index
as
select E.employee_id, J.job_id
, sysdate - ( row_number() over ( order by E.employee_id, J.job_id ) ) dt
from (
select unique employee_id from hr.job_history
) E cross join (
select unique job_id from hr.job_history
) J
;
-- Table created.
Checks
select count(*) from heap_empjobs ;
COUNT(*)
56
select count(*) from iot_empjobs ;
COUNT(*)
56
select table_name, nvl( to_char( num_rows ), 'no rows!' ) rowcount
from user_tables
where table_name in ('HEAP_EMPJOBS', 'IOT_EMPJOBS') ;
TABLE_NAME ROWCOUNT
HEAP_EMPJOBS 56
IOT_EMPJOBS no rows!
Tested with Oracle 18c and 11g (see dbfiddle).

Related

Correct syntax for using inner join with pagination in Oracle

I have the following query:
select * from
(select rownum rnum, p.* from
(select DEPARTMENTS.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME,
DEPARTMENTS.MANAGER_ID,
EMPLOYEES.EMPLOYEE_ID,
EMPLOYEES.FIRST_NAME,
EMPLOYEES.LAST_NAME,
EMPLOYEES.MANAGER_ID,
EMPLOYEES.DEPARTMENT_ID,
EMPLOYEES.DETAILS
from HR.EMPLOYEES
INNER JOIN HR.DEPARTMENTS on
HR.DEPARTMENTS.DEPARTMENT_ID=EMPLOYEES.DEPARTMENT_ID
where EMPLOYEES.EMPLOYEE_ID >= 1) p
where rownum <= 4)
where rnum >= 2
that gives me the following error:
Error SQL: ORA-00918: column ambiguously defined
00918. 00000 - "column ambiguously defined"
If the fields have different names there's no errors. As an example, this query is correct:
select * from
(select rownum rnum, p.* from
(select DEPARTMENTS.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME,
DEPARTMENTS.MANAGER_ID,
EMPLOYEES.EMPLOYEE_ID,
EMPLOYEES.FIRST_NAME,
EMPLOYEES.LAST_NAME,
EMPLOYEES.DETAILS
from HR.EMPLOYEES
INNER JOIN HR.DEPARTMENTS on
HR.DEPARTMENTS.DEPARTMENT_ID=EMPLOYEES.DEPARTMENT_ID
where EMPLOYEES.EMPLOYEE_ID >= 1) p
where rownum <= 4)
where rnum >= 2
What is the correct syntax to make an inner join with pagination, and fields from different tables with the same names? How can I avoid the ORA-00918 error in my first query?
I'm using Oracle 11g.
Use column aliases in the inner query
eg
-- outer query omitted, table aliases and column aliases added
select rownum rnum, p.*
from (
select
D.DEPARTMENT_ID did
, D.DEPARTMENT_NAME dname
, D.MANAGER_ID dmgr
, E.EMPLOYEE_ID eid
, E.FIRST_NAME efname
, E.LAST_NAME elname
, E.MANAGER_ID emgr
, E.DEPARTMENT_ID edept
-- , E.DETAILS
from HR.EMPLOYEES E
INNER JOIN HR.DEPARTMENTS D on D.DEPARTMENT_ID = E.DEPARTMENT_ID
where E.EMPLOYEE_ID >= 1 ) p
;
-- result
RNUM DID DNAME DMGR EID EFNAME ELNAME EMGR EDEPT
1 10 Administration 200 200 Jennifer Whalen 101 10
2 20 Marketing 201 201 Michael Hartstein 100 20
3 20 Marketing 201 202 Pat Fay 201 20
4 30 Purchasing 114 114 Den Raphaely 100 30
5 30 Purchasing 114 115 Alexander Khoo 114 30
6 30 Purchasing 114 116 Shelli Baida 114 30
7 30 Purchasing 114 117 Sigal Tobias 114 30
8 30 Purchasing 114 118 Guy Himuro 114 30
9 30 Purchasing 114 119 Karen Colmenares 114 30
...
The MANAGER_ID and DEPARTMENT_ID columns (without aliases) are causing the error you have described (remove the comments to get the error message):
select rownum rnum, p.*
from (
select
D.DEPARTMENT_ID
, D.DEPARTMENT_NAME
--, D.MANAGER_ID
, E.EMPLOYEE_ID
, E.FIRST_NAME
, E.LAST_NAME
, E.MANAGER_ID
--, E.DEPARTMENT_ID
from HR.EMPLOYEES E
INNER JOIN HR.DEPARTMENTS D on D.DEPARTMENT_ID = E.DEPARTMENT_ID
where E.EMPLOYEE_ID >= 1 ) p
;
-- ORA-00918: column ambiguously defined

PIVOT table in Oracle

Can you help me to pivot the details in my Oracle table PAY_DETAILS
PAY_NO NOT NULL NUMBER
EMP_NO NOT NULL VARCHAR2(10)
EMP_ERN_DDCT_NO NOT NULL VARCHAR2(21)
ERN_DDCT_CATNO NOT NULL VARCHAR2(10)
ERN_DDCT_CATNAME NOT NULL VARCHAR2(1000)
PAY_MONTH NOT NULL DATE
AMOUNT NOT NULL NUMBER(10,2)
EARN_DEDUCT NOT NULL VARCHAR2(2)
select EMP_NO,EMP_ERN_DDCT_NO,AMOUNT,EARN_DEDUCT, ERN_DDCT_CATNO from pay_details
EMP_NO EMP_ERN_DDCT_NO AMOUNT EA ERN_DDCT_C
---------- --------------------- ---------- -- ----------
219 10 175 A 001
219 1 5000 A 002
794 7 50000 A 001
769 6 35000 A 001
465 4 5000 A 002
289 2 5000 A 002
435 3 5000 A 002
816 38 5 D 201
737 30 5 D 201
Is it possible to make this output into a cross tab?
So, lets assume you want to pivot your salary data for each month. You can use the following query.
SELECT * FROM
(
SELECT emp_no,
emp_ern_ddct_no,
pay_month,
amount
FROM pay_details
)
PIVOT
(
SUM(amount)
FOR pay_month IN ('01/01/2016', '02/01/2016', '03/01/2016','04/01/2016','05/01/2016','06/01/2016','07/01/2016','08/01/2016','09/01/2016','10/01/2016','11/01/2016','12/01/2016')
)
ORDER BY emp_no;
This is just an example, you can PIVOT your data based on different columns. For more details refer to the following link.
http://www.techonthenet.com/oracle/pivot.php
http://www.oracle-developer.net/display.php?id=506
Since you are on Oracle 10g PIVOT wont work. Try using something similar to the below query.
SELECT emp_no,
SUM(CASE WHEN pay_month ='01/01/2016' THEN AMOUNT ELSE 0 END) jan_pay,
SUM(CASE WHEN pay_month ='02/01/2016' THEN AMOUNT ELSE 0 END) feb_pay,
SUM(CASE WHEN pay_month ='03/01/2016' THEN AMOUNT ELSE 0 END) march_pay
.........
FROM pay_details
GROUP BY emp_no;

How to get only the one employee name from each department if the max salary is same from more than one employee

I am using below query:
SELECT rownum, job_id, employee_id, first_name, last_name, phone_number, salary
FROM employees OUTER
WHERE salary =
(
SELECT MAX(salary)
FROM employees
WHERE job_id = OUTER.job_id
GROUP BY job_id
)
AND ROWNUM < 6;
And getting below result:
1 AD_PRES 100 Steven King 515.123.4567 24000
2 AD_VP 101 Neena Kochhar 515.123.4568 17000
3 AD_VP 102 Lex De Haan 515.123.4569 17000
4 IT_PROG 103 Alexander Hunold 590.423.4567 9000
5 FI_MGR 108 Nancy Greenberg 515.124.4569 12008
But the problem is I want only one name for each JOB_ID. And that should be decided by alphabetical preference in FIRST_NAME.
One option would be to use a subquery which contains the job_id for the first name you want to retain. I wrapped your original query in a common table expression to make it more readable.
WITH t AS
(
SELECT rownum, job_id, employee_id, first_name, last_name,
phone_number, salary
FROM employees OUTER
WHERE salary =
(
SELECT MAX(salary)
FROM employees
WHERE job_id = OUTER.job_id
GROUP BY job_id
)
AND ROWNUM < 6;
)
SELECT t1.rownum, t1.job_id, t1.employee_id, t1.first_name, t1.last_name,
t1.phone_number, t1.salary
FROM t t1
INNER JOIN
(
SELECT job_id, MAX(first_name) AS max_name
FROM t
GROUP BY job_id
) t2
ON t1.job_id = t2.job_id AND t1.first_name = t2.max_name
Use analytic functions:
select * from (
SELECT job_id, employee_id, first_name, last_name, phone_number, salary,
RANK() over (
job_id
order by
salary desc,
first_name,
employee_id -- adding employe_id breaks ties in the ordering
) as rnk
FROM employees
) where rnk = 1;
This will probably also perform better then the subselect.
All this is written without a database at hand, so it might/will contain typos

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

Combining column values from different tables

I stuck. I have 2 tables - look at image no.1 Table columns And i would like to build query, that will give me the result - it is showed on image no 2. the result of query.
I have 2 queries and I would like to mix them up, to obtain the list from image no.2. Please help me, how to build a query.
Query no1: SELECT department_name, department_id FROM DEPARTMENTS WHERE department_id between 90 AND 110;
Query no 2: SELECT last_name, department_id from employees WHERE department_id between 90 AND 110;
Query:
with departments (department_id, department_name) as (
select 90, 'Executive' from dual union all
select 100, 'Finance' from dual union all
select 110, 'Accounting' from dual
),
employees (employee_id, last_name, department_id) as (
select 1003, 'King' , 90 from dual union all
select 1005, 'De Hann' , 90 from dual union all
select 1009, 'Gietz' , 110 from dual union all
select 1013, 'Popp' , 100 from dual union all
select 1014, 'Chen' , 100 from dual union all
select 1015, 'Higgins' , 110 from dual union all
select 1029, 'Greenberg', 100 from dual union all
select 1040, 'Kochar' , 90 from dual union all
select 1043, 'Faviet' , 100 from dual union all
select 1045, 'Urman' , 100 from dual union all
select 1049, 'Sciarra' , 100 from dual
)
-- end input data; begin actual query --
select c_name, department_id from
( select department_name as c_name, department_id, 0 as categ from departments
union all
select ' ' || last_name as c_name, department_id, 1 from employees
order by department_id, categ, c_name
);
Result:
C_NAME DEPARTMENT_ID
------------- -------------
Executive 90
De Hann 90
King 90
Kochar 90
Finance 100
Chen 100
Faviet 100
Greenberg 100
Popp 100
Sciarra 100
Urman 100
Accounting 110
Gietz 110
Higgins 110
You don't need the "with ..." part; just use the query that begins at the SELECT statement after the two factored subqueries (after the "input data"). I even ordered by last name within each department for you; if that is not needed, just delete "c_name" from the ORDER BY clause.
I called the first column c_name; you may call it whatever you want, but calling it department_name when it also holds employee last names didn't make much sense to me. To call it whatever you want, change the SELECT statement from SELECT c_name, department_id to SELECT c_name AS whatever, department_id...
SELECT c.last_name,
d.department_id,
d.department_name
FROM employee c
JOIN deptartment d ON d.department_id=c.department_id
WHERE d.department_id BETWEEN 90 AN 110
OUTPUT from my sample table
+-------+----+------------+
| KING | 10 | ACCOUNTING |
| BLAKE | 30 | SALES |
| CLARK | 10 | ACCOUNTING |
| JONES | 20 | RESEARCH |
| SCOTT | 20 | RESEARCH |
+-------+----+------------+

Resources