How to count across all levels in PIVOT query? - sql

How can I calculate count for all the levels in PIVOT query? E.g. this counts across ename and job, but how to calculate count for all job levels, not just for CLERK, SALESMAN and MANAGER?
with pivot_data as (
select ename, job
from scott.emp
)
select * from pivot_data
pivot (count(*) for job in('CLERK', 'SALESMAN', 'MANAGER'));

To include all jobs without listing them explicitly in the IN clause of the PIVOT, besides dynamic sql, you can use PIVOT XML and rewrite the query as follows:
-- sample of data
with t1(col) as(
select 'CLERK' from dual union all
select 'SALESMAN' from dual union all
select 'MANAGER' from dual
)
select col_xml
from t1
pivot xml(
count(*) for col in(select col from t1)
)
XML Result:
COL_XML
--------------------------------------------------------------------------------
<PivotSet><item><column name = "COL">CLERK</column><column name = "COUNT(*)">1</
column></item><item><column name = "COL">MANAGER</column><column name = "COUNT(*
)">1</column></item><item><column name = "COL">SALESMAN</column><column name = "
COUNT(*)">1</column></item></PivotSet>
But then, to get the friendly representation of data you will have to explicitly extract the values:
SQL> with t1(col) as(
2 select 'CLERK' from dual union all
3 select 'SALESMAN' from dual union all
4 select 'MANAGER' from dual
5 )
6 select extractvalue(col_xml,'/PivotSet/item[1]/column[2]') col_1
7 , extractvalue(col_xml,'/PivotSet/item[2]/column[2]') col_2
8 , extractvalue(col_xml,'/PivotSet/item[3]/column[2]') col_3
9 from ( select col_xml
10 from t1
11 pivot xml(
12 count(*) for col in(select col from t1)
13 )
14 )
15 ;
Result:
COL_1 COL_2 COL_3
-----------------
1 1 1
SQLFiddle Demo

Related

SQL select two same column on same table but with different ORDER

Trainee here.
I need to create a list last names from the same table.
say we have a table named "sample", this table only consists of:
What I want to do here is both last name column will be selected but have different order, the first column would be ascending and the second column would be descending like the photo below
Here's one option: it splits first and last name into two subqueries which use row_number analytic function. It is then used to join rows.
Lines #1 - 6 represent your sample data. Query you really need begins at line #7.
SQL> with test (last_name, first_name) as
2 (select 'L1one' , 'F1one' from dual union all
3 select 'L2two' , 'F2two' from dual union all
4 select 'L3three', 'F3hthree' from dual union all
5 select 'L4four' , 'F4four' from dual
6 ),
7 ln as
8 (select last_name,
9 row_Number() over (order by last_name) rn
10 from test
11 ),
12 fn as
13 (select first_name,
14 row_number() over (order by first_name desc) rn
15 from test
16 )
17 select l.last_name, f.first_name
18 from ln l join fn f on f.rn = l.rn
19 order by l.last_name
20 /
LAST_NA FIRST_NA
------- --------
L1one F4four
L2two F3hthree
L3three F2two
L4four F1one
SQL>
[EDIT: both last names? I thought it was a typo]
If that's so, self-join is a better option:
SQL> with test (last_name, first_name) as
2 (select 'L1one' , 'F1one' from dual union all
3 select 'L2two' , 'F2two' from dual union all
4 select 'L3three', 'F3hthree' from dual union all
5 select 'L4four' , 'F4four' from dual
6 ),
7 temp as
8 (select last_name,
9 row_number() over (order by last_name asc) rna,
10 row_number() over (order by last_name desc) rnd
11 from test
12 )
13 select a.last_name, d.last_name
14 from temp a join temp d on a.rna = d.rnd
15 order by a.last_name;
LAST_NA LAST_NA
------- -------
L1one L4four
L2two L3three
L3three L2two
L4four L1one
SQL>
Here is one way to do it. In a single subquery, assign the ordinal number (rn) based on ascending order, but also keep track of the total row count. Then follow with a join.
with
test (last_name, first_name) as (
select 'L1one' , 'F1one' from dual union all
select 'L2two' , 'F2two' from dual union all
select 'L3three', 'F3hthree' from dual union all
select 'L4four' , 'F4four' from dual
)
, prep (last_name, rn, ct) as (
select last_name, row_number() over (order by last_name), count(*) over ()
from test
)
select a.last_name as last_name_asc, b.last_name as last_name_desc
from prep a inner join prep b on a.rn + b.rn = a.ct + 1
;
LAST_NAME_ASC LAST_NAME_DESC
-------------- --------------
L1one L4four
L2two L3three
L3three L2two
L4four L1one

Oracle 12c Analytic Function

Is there a way to obtain the corresponding value X for a minimum value Y in a given dataset, in the same record, using Oracle Analytic functions, and without using a subquery?
For example:
If I have the following dataset "ds1":
Col1 Col2
A 1
B 2
C 3
D 4
E 4
A 10
Normally, in order to find the value "A" in Col1, which corresponds to the minimum value "1" in Col2, I would write the following query:
select ds1.col1
from ds1
, (select min (col2) col2
from ds1) min_ds1
where ds1.col2 = min_ds1.col2
/
Here is the executed code for such a Test Case:
### 1014.010, Start time is: 10/30/2019 11:39:35am
MYUN#MYDB-C1>>create table ds1 (col1 varchar2 (1), col2 number)
2 /
Table created.
Elapsed: 00:00:00.01
MYUN#MYDB-C1>>insert into ds1 (col1, col2)
2 select 'A', 1 from dual
3 union all select 'B', 2 from dual
4 union all select 'C', 3 from dual
5 union all select 'D', 4 from dual
6 union all select 'E', 4 from dual
7 union all select 'A', 10 from dual
8 /
6 rows created.
Elapsed: 00:00:00.02
MYUN#MYDB-C1>>commit
2 /
Commit complete.
Elapsed: 00:00:00.01
MYUN#MYDB-C1>>col col1 format a10
MYUN#MYDB-C1>>select ds1.col1
2 from ds1
3 , (select min (col2) col2
4 from ds1) min_ds1
5 where ds1.col2 = min_ds1.col2
6 /
COL1
----------
A
1 row selected.
Elapsed: 00:00:00.01
MYUN#MYDB-C1>>drop table ds1
2 /
Table dropped.
Elapsed: 00:00:00.03
The time now: 10/30/2019 11:39:36am
My question is:
Is it possible to derive the value "A" using an Analytic Function and without requiring a subquery? I am aware I can use the analytic function "ROW_NUMBER", sort the result in the ORDER BY clause, all in a subquery and then add a WHERE clause on the outer query where I say something like "WHERE RN = 1", where "RN" is the alias for the column in the subquery where the ROW_NUMBER function is used.
Use an aggregation function with KEEP to get the minimum values for another column:
Oracle Setup:
create table ds1 ( col1, col2 ) AS
select 'A', 1 from dual
union all select 'B', 2 from dual
union all select 'C', 3 from dual
union all select 'D', 4 from dual
union all select 'E', 4 from dual
union all select 'F', 10 from dual;
Aggregation Query:
SELECT MIN( col1 ) KEEP ( DENSE_RANK FIRST ORDER BY col2 ) AS col1
FROM ds1
Output:
| COL1 |
| :--- |
| A |
Analytic Query:
If you particularly want an analytic function then:
SELECT col1, col2
FROM (
SELECT ds1.*,
DENSE_RANK() OVER ( ORDER BY col2 ASC ) AS rnk
FROM ds1
)
WHERE rnk = 1
This has a sub-query but there is only a single table-scan.
You can easily integrate it into a huge query:
WITH my_huge_query AS (
<paste your huge query here>
)
SELECT *
FROM (
SELECT m.*,
DENSE_RANK() OVER( ORDER BY col2 ASC ) AS rnk
FROM my_huge_query m
)
WHERE rnk = 1
Output:
COL1 | COL2
:--- | ---:
A | 1
db<>fiddle here

Get duplicate employee count department wise in single sql

ID Name dep_id
1 A 1
2 B 2
3 A 1
4 A 2
5 B 2
6 A 2
I think you want to have such a SQL
with tab( ID, Name, dep_id) as
(
select 1,'A',1 union all
select 2,'B',2 union all
select 3,'A',1 union all
select 4,'A',2 union all
select 5,'B',2 union all
select 6,'A',2
)
select name,
count(dep_id) as dept_count
from tab t
group by name
having count(name)>1;
NAME DEPT_COUNT
---- ----------
A 4
B 2
Due to you last edit( which you wanted to add to this answer ), consider grouping also by dept_id :
with tab( ID, Name, dep_id) as
(
select 1,'A',1 union all
select 2,'B',2 union all
select 3,'A',1 union all
select 4,'A',2 union all
select 5,'B',2 union all
select 6,'A',2
)
select name, dept_id,
count(dept_id) as dept_count
from tab t
group by name, dept_id
having count(name)>1;
NAME DEPT_ID DEPT_COUNT
---- ------ ----------
A 2 2
A 1 2
B 2 2

Oracle select pivot query to put row adjacent to their counter part using generic column name

I have a table which each entry has a counter pair
Customer
Name Value
Bob 3
Bob 4
Sam 0
Sam 1
Joe 9
I want the following result
Customer
Name Value1 Value2
Bob 3 4
Sam 0 1
Joe 9
I have read this thread, Oracle query to put rows at odd number adjacent to even number, but I want to avoid using the MOD function instead possible using pivot instead.
You can't use the pivot statement here, if you have only two value for each name (it also works with dates, because we can use max and min for dates):
select name, min(value) value1, nullif(max(value), min(value)) value2
from customer_tables
group by name
If Bob, Sam and other have more that two value:
with t (Name, Value) as (
select 'Bob',3 from dual union all
select 'Bob',4 from dual union all
select 'Sam',0 from dual union all
select 'Sam',1 from dual union all
select 'Joe',9 from dual
), t1 (name, value, rn) as (
select name, value, ROW_NUMBER() OVER(partition by name order by value) from t
)
select * from t1
pivot XML (
max(value)
for rn in (ANY)
)
SQL> l
1 with t (Name, Value) as (
2 select 'Bob',3 from dual union all
3 select 'Bob',4 from dual union all
4 select 'Sam',0 from dual union all
5 select 'Sam',1 from dual union all
6 select 'Joe',9 from dual
7 ), t1 (name, value, rn) as (
8 select name, value, ROW_NUMBER() OVER(partition by name order by value) from t
9 )
10 select * from t1
11 pivot XML (
12 max(value)
13 for rn in (ANY)
14* )
SQL> /
NAM RN_XML
--- --------------------------------------------------------------------------------
Bob <PivotSet><item><column name = "RN">1</column><column name = "MAX(VALUE)">3</col
umn></item><item><column name = "RN">2</column><column name = "MAX(VALUE)">4</co
lumn></item></PivotSet>
Joe <PivotSet><item><column name = "RN">1</column><column name = "MAX(VALUE)">9</col
umn></item></PivotSet>
Sam <PivotSet><item><column name = "RN">1</column><column name = "MAX(VALUE)">0</col
umn></item><item><column name = "RN">2</column><column name = "MAX(VALUE)">1</co
lumn></item></PivotSet>
Read more about pivot here

How to do select count(*) group by and select * at same time?

For example, I have table:
ID | Value
1 hi
1 yo
2 foo
2 bar
2 hehe
3 ha
6 gaga
I want my query to get ID, Value; meanwhile the returned set should be in the order of frequency count of each ID.
I tried the query below but don't know how to get the ID and Value column at the same time:
SELECT COUNT(*) FROM TABLE group by ID order by COUNT(*) desc;
The count number doesn't matter to me, I just need the data to be in such order.
Desire Result:
ID | Value
2 foo
2 bar
2 hehe
1 hi
1 yo
3 ha
6 gaga
As you can see because ID:2 appears most times(3 times), it's first on the list,
then ID:1(2 times) etc.
you can try this -
select id, value, count(*) over (partition by id) freq_count
from
(
select 2 as ID, 'foo' as value
from dual
union all
select 2, 'bar'
from dual
union all
select 2, 'hehe'
from dual
union all
select 1 , 'hi'
from dual
union all
select 1 , 'yo'
from dual
union all
select 3 , 'ha'
from dual
union all
select 6 , 'gaga'
from dual
)
order by 3 desc;
select t.id, t.value
from TABLE t
inner join
(
SELECT id, count(*) as cnt
FROM TABLE
group by ID
)
x on x.id = t.id
order by x.cnt desc
How about something like
SELECT t.ID,
t.Value,
c.Cnt
FROM TABLE t INNER JOIN
(
SELECT ID,
COUNT(*) Cnt
FROM TABLE
GROUP BY ID
) c ON t.ID = c.ID
ORDER BY c.Cnt DESC
SQL Fiddle DEMO
I see the question is already answered, but since the most obvious and most simple solution is missing, I'm posting it anyway. It doesn't use self joins nor subqueries:
SQL> create table t (id,value)
2 as
3 select 1, 'hi' from dual union all
4 select 1, 'yo' from dual union all
5 select 2, 'foo' from dual union all
6 select 2, 'bar' from dual union all
7 select 2, 'hehe' from dual union all
8 select 3, 'ha' from dual union all
9 select 6, 'gaga' from dual
10 /
Table created.
SQL> select id
2 , value
3 from t
4 order by count(*) over (partition by id) desc
5 /
ID VALU
---------- ----
2 bar
2 hehe
2 foo
1 yo
1 hi
6 gaga
3 ha
7 rows selected.