Oracle database subquery results in multiple rows - sql

Hi am having an SQL problem, and I hope the answer is pretty easy.
I have a database with the following structure.
State Gender birthyear birthname count
----- ------ --------- --------- ------
AK F 1923 Helen 15
AK F 1926 Helen 35
AK F 1927 Susan 25
AK F 1920 Helen 15
There are thousands of records and I would like the output to look like this:
birthname 1910 1911 1912 -->2012
----- ------ --------- --------- ------
Helen 5 6 12 800
Using MS Access I was able to get some results with this:
SELECT DISTINCT as1.birthname AS Expr1,
(select totalcount from AK as2 where as1.birthname=as2.birthname and as1.gender=as2.gender and as1.state=as2.state and as1.birthyear=as2.birthyear and birthyear=1910) as 1910,
(select totalcount from AK as2 where as1.birthname=as2.birthname and as1.gender=as2.gender and as1.state=as2.state and as1.birthyear=as2.birthyear and birthyear=1911) as 1911,
(select totalcount from AK as2 where as1.birthname=as2.birthname and as1.gender=as2.gender and as1.state=as2.state and as1.birthyear=as2.birthyear and birthyear=2012) as 2012
FROM AK AS as1

You should do this using conditional aggregation:
SELECT as1.birthname AS Expr1,
SUM(case when birthyear = 1910 then `count` else 0 end) as yr_1910,
SUM(case when birthyear = 1911 then `count` else 0 end) as yr_1911,
SUM(case when birthyear = 1912 then `count` else 0 end) as yr_1912
FROM AK AS as1
GROUP BY as1.birthname;
I am not sure where gender and state come in. These are not included in the outer query, which is probably the cause of your syntax error. You might want to include these in the aggregation:
SELECT as1.birthname, as1.gender, as1.state,
SUM(case when birthyear = 1910 then `count` else 0 end) as yr_1910,
SUM(case when birthyear = 1911 then `count` else 0 end) as yr_1911,
SUM(case when birthyear = 1912 then `count` else 0 end) as yr_1912
FROM AK AS as1
GROUP BY as1.birthname, as1.gender, as1.state;

If you are using Oracle 11g+ you can use the SQL PIVOT syntax to generate a crosstab report. With sample data the query would be something like this:
with sample_data as
(select 'AK' state, 'F' gender, 1923 birthyear, 'Helen' birthname, 15 namecount from dual union all
select 'AK', 'F', 1926, 'Helen', 35 from dual union all
select 'AK', 'F', 1927, 'Susan', 25 from dual union all
select 'AK', 'F', 1920, 'Helen', 15 from dual)
select * from (
select * from sample_data
)
pivot
(
sum(namecount)
for birthyear in (1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1230)
);
Unfortunately the list of years in the IN clause must be hardcoded, you cannot generate that list dynamically with a subquery. However, that should be not be too hard to initially populate.

Related

How to aggregate multiple outcome of case condition in sql?

Here is a sample code:
SELECT DISTINCT salary, planet,
sum(case when company LIKE '%Google%' then 1 end) `Google`,
sum(case when company LIKE '%IBM%' then 1 end) `IBM`,
sum(case when company LIKE '%Cisco%' then 1 end) `Cisco`
from industries
where planet = 'Earth' ;
Can someone give me advice how to summarize amount of multiple variables defined outside case condition? I tried to use simple math, but it did not work.
SELECT DISTINCT salary, planet,
sum(case when company LIKE '%Google%' then 1 end) `Google`,
sum(case when company LIKE '%IBM%' then 1 end) `IBM`,
sum(case when company LIKE '%Cisco%' then 1 end) `Cisco`,
-- similar math to count multiple columns,
sum(`Google` + `IBM` + `Cisco`) AS Total_amount
from industries
where planet = 'Earth' ;
The result should like this:
------------------------------------------------------------
| salary | Planet| Google | IBM | Cisco | Total_amount |
|----------------------------------------------------------|
| 3000.00 | Earth | 26 | 26 | 25 | 77 |
------------------------------------------------------------
Thanks in advance!
It's just in front of your eyes. It happens to me, too. Just COUNT(*) for the total amount.
WITH industries(salary,planet,company) AS (
SELECT 3000.00,'Earth','Google'
UNION ALL SELECT 3000.00,'Earth','Google'
UNION ALL SELECT 3000.00,'Earth','Google'
UNION ALL SELECT 3000.00,'Earth','Google'
UNION ALL SELECT 3000.00,'Earth','IBM'
UNION ALL SELECT 3000.00,'Earth','IBM'
UNION ALL SELECT 3000.00,'Earth','IBM'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
UNION ALL SELECT 3000.00,'Earth','Cisco'
)
SELECT
salary
, planet
, SUM(CASE company WHEN 'Google' THEN 1 END) AS Google
, SUM(CASE company WHEN 'IBM' THEN 1 END) AS IBM
, SUM(CASE company WHEN 'Cisco' THEN 1 END) AS Cisco
, COUNT(*) AS total_amount
FROM industries
WHERE planet = 'Earth'
GROUP BY
salary
, planet
;
-- out salary | planet | Google | IBM | Cisco | total_amount
-- out ---------+--------+--------+-----+-------+--------------
-- out 3000.00 | Earth | 4 | 3 | 6 | 13
Consider below (BigQuery)
SELECT salary, planet,
COUNTIF(company LIKE '%Google%') AS Google,
COUNTIF(company LIKE '%IBM%') AS IBM ,
COUNTIF(company LIKE '%Cisco%') AS Cisco,
COUNTIF(REGEXP_CONTAINS(company, 'Google|IBM|Cisco')) AS Total_amount
FROM industries
WHERE planet = 'Earth'
GROUP BY salary, planet
Yet another approach (BigQuery)
SELECT *, Google + IBM + Cisco AS Total_amount
FROM (
SELECT * EXCEPT(company),
REGEXP_EXTRACT(company, 'Google|IBM|Cisco') as col
FROM industries
WHERE planet = 'Earth'
)
PIVOT (COUNT(*) FOR col IN ('Google','IBM','Cisco'))

How to aggregate functions with groupby in SQL

I have tables like this.
I would like to groupbyand aggregate this in diffrent aggregate functions.
product sex age
A M 10
B F 20
A F 30
C M 40
my desired result is like below.
Now I can group in productkey, but in this case, I must group them byproductandsex.
Are there any way to achieve it?
count(M) count(F) avarage(M) average(F)
A 1 1 10 30
B 0 1 NA 20
C 1 0 40 NA
Thanks
With conditional aggregation:
select product,
sum(case when sex = 'M' then 1 else 0 end),
sum(case when sex = 'F' then 1 else 0 end),
avg(case when sex = 'M' then age end),
avg(case when sex = 'F' then age end)
from tablename
group by product
You can simply use the PIVOT as follows:
select * from your_Table
pivot
(count(1) as cnt, avg(age) as average for sex in ('M','F'))

SQL: alternatives and substitutions for GROUPING SETS and PIVOT

I've got code like this:
SELECT id, YEAR(datek) AS YEAR, COUNT(*) AS NUM
FROM Orders
GROUP BY GROUPING SETS
(
(id, YEAR(datek)),
id,
YEAR(datek),
()
);
It gives me this output:
1 NULL 4
2 NULL 11
3 NULL 6
NULL NULL 21
1 2006 36
2 2006 56
3 2006 51
NULL 2006 143
1 2007 130
2 2007 143
3 2007 125
NULL 2007 398
1 2008 79
2 2008 116
3 2008 73
NULL 2008 268
NULL NULL 830
1 NULL 249
2 NULL 326
3 NULL 255
What I need to do is write it without "grouping sets" (nor cube or rollup) but with the same result. I thought about writing three different queries and join them with "union". I try something like "null" in group by settings but it does not work.
SELECT id, YEAR(datek) AS rok, COUNT(*) AS NUM
FROM Orders
GROUP BY id, YEAR(datek)
UNION
SELECT id, YEAR(datek) AS rok, COUNT(*) AS NUM
FROM Orders
GROUP BY id, null
order by id, YEAR(datek)
I also have a question about "PIVOT". What kind of syntax can replace query with "PIVOT"?
Thanks for your time and all the answers!
You are right in that you need separate queries, although you actually need 4, and rather than GROUP BY NULL, just group by the columns in the corresponding grouping set, and replace the column in the SELECT with NULL:
SELECT id, YEAR(datek) AS rok, COUNT(*) AS NUM
FROM Orders
GROUP BY id, YEAR(datek)
UNION ALL
SELECT id, NULL, COUNT(*) AS NUM
FROM Orders
GROUP BY id
UNION ALL
SELECT NULL, YEAR(datek), COUNT(*) AS NUM
FROM Orders
GROUP BY YEAR(datek)
UNION ALL
SELECT NULL, NULL, COUNT(*) AS NUM
FROM Orders
ORDER BY ID, Rok
With regard to a replacement for PIVOT I think the best alternative is to use a conditional aggregate, e.g. instead of:
SELECT pvt.SomeGroup,
pvt.[A],
pvt.[B],
pvt.[C]
FROM T
PIVOT (SUM(Val) FOR Col IN ([A], [B], [C])) AS pvt;
You would use:
SELECT T.SomeGroup,
[A] = SUM(CASE WHEN T.Col = 'A' THEN T.Val ELSE 0 END),
[B] = SUM(CASE WHEN T.Col = 'B' THEN T.Val ELSE 0 END),
[C] = SUM(CASE WHEN T.Col = 'C' THEN T.Val ELSE 0 END)
FROM T
GROUP BY T.SomeGroup;

Oracle pivot function with two columns [duplicate]

This question already has answers here:
Advice Using Pivot Table in Oracle
(4 answers)
Closed 8 years ago.
How to display the number of employees in each department with each salary in oracle
something like this
department_id salar_equal_1000 salar_equal_20000 city_boston city_detroit city_none
10 2 3 2 1 2
20 1 2 1 2 0
table contains data like this
department_id salary city
10 1000 boston
10 1000 boston
10 2000 detroit
10 2000
10 2000
20 1000 boston
20 2000 detroit
20 2000 detroit
I guess i have to use the pivot statement but i am not sure need some help on this
Try this:
select
department_id,
sum(case when salary = 1000 then 1 else 0 end) as salar_equal_1000,
sum(case when salary = 2000 then 1 else 0 end) as salar_equal_2000,
sum(case when city = 'boston' then 1 else 0 end) as city_boston,
sum(case when city = 'detroit' then 1 else 0 end) as city_detroit,
sum(case when city is null then 1 else 0 end) as city_none
from
employees
group by department_id
order by department_id
Edited again:
See it here on fiddle: http://sqlfiddle.com/#!4/3564c/6
Hi please go through the following example, Might be it will help you.
If you are working with oracle10g this is the format,
if 11g pivot function is there
WITH cte(dept_id,salary) AS (SELECT 10,1000 FROM dual
UNION ALL
SELECT 10,1000 FROM dual
UNION ALL
SELECT 10,1000 FROM dual
UNION ALL
SELECT 20,1000 FROM dual
UNION ALL
SELECT 10,2000 FROM dual
UNION ALL
SELECT 10,2000 FROM dual
UNION ALL
SELECT 20,1000 FROM dual
UNION ALL
SELECT 20,2000 FROM dual
UNION ALL
SELECT 20,2000 FROM dual
),
TEMP as(SELECT
dept_id,
count(salary) cnt,salary
FROM
cte
GROUP BY salary, dept_id)
SELECT dept_id,sum(CASE WHEN salary=1000 THEN cnt ELSE 0 END)salary_1000,
sum(CASE WHEN salary=2000 THEN cnt ELSE 0 END) salary_2000 FROM TEMP
GROUP BY dept_id

Using SELECT UNION and returning output of two columns from one table

I am creating a query that counts the amount of male and female actors in my table. My current statement is as such:
Select COUNT(ActorGender) “Male Actors”
from (tblActor ta WHERE ta.ActorGender in(‘m’)
UNION
Select COUNT(ActorGender) “Female Actors”
from tblActor ta
WHERE ta.ActorGender in(‘f’);
The output ends up being:
Male Actors
-----------
7
21
I want the output to look like:
Male Actors Female Actors
----------- -------------
7 21
I am looking for an alternative to go about this without using the CASE WHEN or THEN clauses.
Thanks in advance for the help as usual.
Another way (without CASE expression):
SELECT
( SELECT COUNT(*)
FROM tblActor
WHERE ActorGender = 'm'
) AS MaleActors
, ( SELECT COUNT(*)
FROM tblActor
WHERE ActorGender = 'f'
) AS FemaleActors
FROM
dual ;
and more solution with CROSS join:
SELECT m.MaleActors, f.FemaleActors
FROM
( SELECT COUNT(*) AS MaleActors
FROM tblActor
WHERE ActorGender = 'm'
) m
CROSS JOIN
( SELECT COUNT(*) AS FemaleActors
FROM tblActor
WHERE ActorGender = 'f'
) f ;
This would do:
SELECT COUNT(CASE WHEN ActorGender = 'm' THEN 1 ELSE NULL END) MaleActors,
COUNT(CASE WHEN ActorGender = 'f' THEN 1 ELSE NULL END) FemaleActors
FROM tblActor
WHERE ActorGender IN ('m','f')
another way without using case:
select sum(males) as "Male Actors", sum(females) as "Female Actors"
from
(select count(actorGender) as Males, 0 as Females
from tblActor
where actorGender = 'm'
union all
select 0 as males, count(actorGender) as Females
from tblActor
where actorGender = 'f')
should result in
Male Actors Female Actors
----------- -------------
7 21
If you are using Oracle 11g+, then you can use PIVOT:
select *
from
(
select actorgender
from tblActor
) src
pivot
(
count(actorgender)
for actorgender in ('m' MaleActors, 'f' FemaleActors)
) piv
See SQL Fiddle with Demo
The result would be:
| MALEACTORS | FEMALEACTORS |
-----------------------------
| 4 | 5 |
Or you can use a CROSS JOIN to get the same result:
select m.MaleActors, f.FemaleActors
from
(
select count(ActorGender) MaleActors, 'm' Gender
from tblActor
where ActorGender = 'm'
) m
cross join
(
select count(ActorGender) FemaleActors, 'f' Gender
from tblActor
where ActorGender = 'f'
) f