How to save data after using With Clause? - sql

I want to save the data I have after With Clause. Maybe saving into TEMP table or some other data set.
WITH TASKLIST AS
(SELECT * FROM IC_V_NEWSKUSTASKLIST WHERE MSTID IS NULL),
RESULTS AS
(SELECT *
FROM ICTASK
LEFT JOIN TASKLIST
ON ICTASK.SOURCETABLEID = TASKLIST.SOURCETABLEID)
SELECT * FROM RESULTS;
I want to save RESULTS data into some other table in stand of SELECT them, but the select statement is mandatory after WITH clause.

CTAS (Create Table As Select) is one option:
SQL> create table test as
2 with
3 tasklist as
4 (select deptno, empno, ename, job, sal
5 from emp
6 ),
7 results as
8 (select d.dname, t.deptno, t.ename
9 from tasklist t join dept d on d.deptno = t.deptno
10 where t.deptno = 10
11 )
12 select * from results;
Table created.
SQL> select * from test;
DNAME DEPTNO ENAME
-------------- ---------- ----------
ACCOUNTING 10 CLARK
ACCOUNTING 10 KING
ACCOUNTING 10 MILLER
SQL>

Related

why can't I use grouping functions without group by?

In the following code, why can't I use count () for each row in the table? The count() must return the number of colours of each row, which all become one, but it does not. If I use group by, it will.
select colour,count(*)
from bricks;
updated:
another example:
select b.*,
(select count(*) from bricks where colour=b.colour)
from bricks b;
The above example used grouping function without group by and it works without any errors
That's just the way SQL demands it. You can't select a discrete column and and aggregate function without listing the discrete columns in a group by statement.
Your second example runs a subquery for each record returned from the main bricks query. This won't perform as well as the first query using a group by.
And you best alias your subquery table - I've seen circumstances where the parser decides you're essentially saying b.colour = b.colour, which will give you the same count(*) for each record in bricks.
I don't have those bricks, but - I do have Scott's sample EMP table with jobs instead.
This is what you tried (and it doesn't work):
SQL> select job, count(*)
2 from emp
3 order by 1, 2;
select job, count(*)
*
ERROR at line 1:
ORA-00937: not a single-group group function
This is what works, but - that's not what you want:
SQL> select job, count(*)
2 from emp
3 group by job
4 order by 1, 2;
JOB COUNT(*)
--------- ----------
ANALYST 2
CLERK 4
MANAGER 3
PRESIDENT 1
SALESMAN 4
This is what you want (and it works), but - doesn't look pretty:
SQL> select e.job,
2 (select count(*) from emp a where a.job = e.job) cnt
3 from emp e
4 order by 1, 2;
JOB CNT
--------- ----------
ANALYST 2
ANALYST 2
CLERK 4
CLERK 4
CLERK 4
CLERK 4
MANAGER 3
MANAGER 3
MANAGER 3
PRESIDENT 1
SALESMAN 4
SALESMAN 4
SALESMAN 4
SALESMAN 4
14 rows selected.
Good news for you - use count function in its analytic form:
SQL> select job, count(*) over (partition by job order by null) cnt
2 from emp
3 order by 1, 2;
JOB CNT
--------- ----------
ANALYST 2
ANALYST 2
CLERK 4
CLERK 4
CLERK 4
CLERK 4
MANAGER 3
MANAGER 3
MANAGER 3
PRESIDENT 1
SALESMAN 4
SALESMAN 4
SALESMAN 4
SALESMAN 4
14 rows selected.
SQL>

How to fix group function nested too deeply error

I've run into an error I've never seen before while trying to learn SQL where it is saying that my group by function is nested too deeply? Any ideas on how to fix this code would be greatly appreciated. I am trying to find the division with the most employees.
Tables:
division(DID, dname, managerID)
employee(empid, name, salary, DID)
Code:
select dname
from division
where did in (select did from employee group by did having max(count(empid)))
I don't have your tables, but Scott's sample schema contains similar tables - dept (departments) and emp (employees who work in these departments).
How many employees work in each department?
SQL> select deptno, count(*)
2 from emp
3 group by deptno
4 order by 2 desc;
DEPTNO COUNT(*)
---------- ----------
30 6 --> this is what you want, it has the most employees
20 5
10 3
Which department is it?
SQL> select * from dept order by deptno;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO --> it's SALES
40 OPERATIONS BOSTON
SQL>
This is what you tried: not that it won't work (Oracle doesn't let you), syntax is wrong. having clause should be something like having count(*) = 25, not just your wishes comes true (having max(count(*)) which reads as "give me department that has maximum count of employees").
SQL> select dname
2 from dept
3 where deptno in (select deptno from emp
4 group by deptno
5 having max(count(empno))
6 );
having max(count(empno))
*
ERROR at line 5:
ORA-00935: group function is nested too deeply
SQL>
So, what can we do about it? A simple option is to rank departments by number of employees:
SQL> select deptno,
2 count(*) cnt,
3 rank() over (order by count(*) desc) rnk
4 from emp
5 group by deptno;
DEPTNO CNT RNK
---------- ---------- ----------
30 6 1 --> department 30 ranks as the highest
20 5 2
10 3 3
SQL>
The rest is easy: use that query as a subquery (or a CTE, as I did), and select the one that ranks the highest:
SQL> with temp as
2 (select deptno,
3 count(*) cnt,
4 rank() over (order by count(*) desc) rnk
5 from emp
6 group by deptno
7 )
8 select d.dname
9 from dept d join temp t on t.deptno = d.deptno
10 where t.rnk = 1; --> the highest rank
DNAME
--------------
SALES
SQL>
SALES it is.

Writing a PLSQL select in array

I'm using a DB 'A' to output a list of numbers :
123455
123456
123457
And I'm looking to build a dynamic statement to look into a DB 'B' with those results as a filter
a. Build an array with the values from DB 'A'
SELECT * FROM my_table
WHERE number in &array;
How can I achieve this ?
The DB 'B' is an Oracle DB.
Thanks
Hm. Looks like your "background" is not Oracle, because there are no "DB"s there. I mean, there are, but not in a context you're using them. If "DB" stands for a "Database", to me it looks as if you're actually talking about tables here.
Also, I don't understand what
The DB 'B' is in PLSQL means.
If "database" is a table, how is it in PL/SQL?
Anyway, to get you started: I'm fetching some data from Scott's EMP and DEPT tables. For collections, I'm using Oracle's built-in types.
These are employees in departments 10 and 20:
SQL> select deptno, ename
2 from emp
3 where deptno in (10, 20)
4 order by deptno, ename;
DEPTNO ENAME
---------- ----------
10 CLARK
10 KING
10 MILLER
20 ADAMS
20 FORD
20 JONES
20 SCOTT
20 SMITH
8 rows selected.
PL/SQL procedure which does something with them (the way I understood the question):
SQL> declare
2 l_a sys.odcinumberlist;
3 l_b sys.odcivarchar2list;
4 begin
5 select deptno
6 bulk collect into l_a
7 from dept
8 where deptno in (10, 20);
9
10 select ename
11 bulk collect into l_b
12 from emp
13 where deptno in (select * from table(l_a))
14 order by ename;
15
16 for i in l_b.first .. l_b.last loop
17 dbms_output.put_line(l_b(i));
18 end loop;
19 end;
20 /
ADAMS
CLARK
FORD
JONES
KING
MILLER
SCOTT
SMITH
PL/SQL procedure successfully completed.
SQL>
Lines #1 - 3 - declaration section
lines #5 - 8 - inserting values (departments) into l_a collection
Lines #10 - 14 - inserting values (employees) into l_b collection, based on values stored in l_a
Lines #16 - 18 - displaying contents of l_b
See if it helps.
[EDIT] After seeing your comment: as far as I can tell, you can't do what you wanted, not as simple as you'd want it to. This is how it works - you enter a comma-separated values as a parameter (that's your "array"), split it into rows and use the result as a subquery:
SQL> SELECT *
2 FROM dept
3 WHERE deptno IN ( SELECT REGEXP_SUBSTR ( '&&par_depts',
4 '[^,]+',
5 1,
6 LEVEL)
7 FROM DUAL
8 CONNECT BY LEVEL <= REGEXP_COUNT ( '&&par_depts', ',') + 1);
Enter value for par_depts: 10,20
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
SQL>
This is a SQL*Plus example; you'll probably have to substitute '&&par_depts' with :par_depts (depending on a tool you use).
Use the MEMBER operator.
First create a collection type in SQL:
CREATE TYPE int_list IS TABLE OF INT;
Then just use it in an SQL statement:
SELECT *
FROM my_table
WHERE value MEMBER OF int_list(123455, 123456, 123457);
Which, for the sample data:
CREATE TABLE my_table ( id, value ) AS
SELECT LEVEL, 123453 + LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
Outputs:
ID
VALUE
2
123455
3
123456
4
123457
If you want it in PL/SQL then:
DECLARE
items int_list := int_list(123455, 123456, 123457);
BEGIN
FOR row IN (
SELECT *
FROM my_table
WHERE value MEMBER OF items
)
LOOP
DBMS_OUTPUT.PUT_LINE( row.id || ', ' || row.value );
END LOOP;
END;
/
Which, for the same data, outputs:
2, 123455
3, 123456
4, 123457
db<>fiddle here
However, if you just want to connect two databases then setup a database link.

Unable to MAX(COUNT) and have multiple columns

I have 4 tables, EMPLOYEE, DRIVER, TRIP and TRIPLEG
EMPLOYEE table has Name which I want to extract and show with MAX count, has E# which is shared with DRIVER
DRIVER table has L#(licence number) which is common with TRIP
TRIP table has T#(trip number) which is common with TRIPLEG
I'm trying to find the max number of tripleg a driver has done(In this case driver with licence number 10002:
SELECT MAX(COUNT(TRIPLEG.LEG#))
FROM TRIP, TRIPLEG
ON TRIP.T# = TRIPLEG.T#
WHERE TRIP.L# = 10002
GROUP BY TRIP.T#
COUNT(TRIPLEG.LEG#) gives me https://i.imgur.com/AYAovov.png,
so I did the above MAX(COUNT(TRIPLEG.LEG#)) which gives me this: https://i.imgur.com/alCFlO3.png
I'm unable to proceed as I tried SELECTING more columns(TRIP.T#) like
SELECT TRIP.T#, MAX(COUNT(TRIPLEG.LEG#))
FROM TRIP, TRIPLEG
ON TRIP.T# = TRIPLEG.T#
WHERE TRIP.L# = 10002
GROUP BY TRIP.T#
Gives me an error: ORA-00937: not a single-group group function
Any advice? Need to be able to start small and selecting before I can join more tables to get the Employee name displayed beside the MAX tripleg count
Thanks in advance
Essentially I want something like: (only 1 row, which is the MAX triplegs done (5))
NAME MAX(COUNT(TRIPLEG.LEG#))
-----------------------------------
BOB 5
I don't have your table so I'll use Scott's EMP and DEPT (as you use Oracle, so I presume you're familiar with them).
This works:
SQL> select d.dname, count(e.empno) cnt
2 from emp e join dept d on e.deptno = d.deptno
3 where d.deptno in (10, 20)
4 group by d.dname;
DNAME CNT
-------------- ----------
ACCOUNTING 3
RESEARCH 5 --> MAX count is this
SQL>
Nested count works if there are no other columns in SELECT column list (you already know that), and it returns desired value:
SQL> select max(count(e.empno)) cnt
2 from emp e join dept d on e.deptno = d.deptno
3 where d.deptno in (10, 20)
4 group by d.dname;
CNT
----------
5
SQL>
But, this won't work (you know that too):
select d.dname, max(count(e.empno)) cnt
from emp e join dept d on e.deptno = d.deptno
where d.deptno in (10, 20)
group by d.dname;
To fix it, use CTE (Common Table Expression a.k.a. the WITH factoring clause) or an inline view; I'll show you the first option, with yet another addition: I'll rank counts and find the "highest" one, and use it later to select only desired row.
SQL> with tcnt as
2 (select d.deptno,
3 d.dname,
4 count(e.empno) cnt,
5 rank() over (order by count(e.empno) desc) rnk --> rank them DESC
6 from emp e join dept d on e.deptno = d.deptno
7 where d.deptno in (10, 20)
8 group by d.dname, d.deptno
9 )
10 select t.deptno, t.dname, t.cnt
11 from tcnt t
12 where rnk = 1; --> fetch only row(s) with highest rank
DEPTNO DNAME CNT
---------- -------------- ----------
20 RESEARCH 5
SQL>
Finally, add some more columns from other tables:
SQL> with tcnt as
2 (select d.deptno,
3 d.dname,
4 count(e.empno) cnt,
5 rank() over (order by count(e.empno) desc) rnk
6 from emp e join dept d on e.deptno = d.deptno
7 where d.deptno in (10, 20)
8 group by d.dname, d.deptno
9 )
10 select t.deptno, t.dname, t.cnt, e.ename, e.job
11 from tcnt t join emp e on e.deptno = t.deptno
12 where rnk = 1;
DEPTNO DNAME CNT ENAME JOB
---------- -------------- ---------- ---------- ---------
20 RESEARCH 5 SMITH CLERK
20 RESEARCH 5 JONES MANAGER
20 RESEARCH 5 SCOTT ANALYST
20 RESEARCH 5 ADAMS CLERK
20 RESEARCH 5 FORD ANALYST
SQL>
Based on your tables to find the max number of tripleg a driver has done(In this case driver with licence number 10002:
Select a.tripno, max(a.trips)
From
(Select Trip.T# as tripno, count(tripleg.leg#) as trips
From tripleg join trip on tripleg.T# = trip.T#
where trip.L# = 10002
Group by tripno
) a
Group by a.tripno

SQL select records common to a column

I have an emp table like this
EMPNO | JOB | DEPTNO
_____________________
1 | A | 10
2 | B | 20
3 | C | 10
4 | A | 20
I want to write a query to List the jobs common to department 10 and 20.
So the output as per the data provided should be
A
which is common to both deptno 10 and 20
I am trying this but it doesn't give me any output.
select job, deptNo from emp group by job having deptno = 20 and deptno = 10; /*6*/
Try this
select job
from emp e1
where e1.deptno = 10
and exists ( select 1
from emp e2
where e2.deptno = 20
and e1.job = e2.job
)
group by job;
You can use an aggregate function in the HAVING clause with the condition you need:
SELECT job
FROM emp
GROUP BY job
HAVING COUNT(CASE WHEN deptNo IN (10, 20) THEN 1 END) = 2
The fields can't be both values at the same time. Use an Or instead of And. Also, the Having doesn't quite make sense, just use Where.
select job, deptNo
from emp
Where deptno = 20
Or deptno = 10
group by job
You can also use an In, which would have the same effect as an Or:
select job, deptNo
from emp
Where deptno In (10, 20)
group by job
Edit: You can do this to get the Jobs that have both values:
Select Distinct a.job
From emp a
Where DeptNo = 10
And Exists
(
Select 1
From Emp b
Where a.Job = b.Job
And b.deptNo = 20
)