Transfer to data in right place in Oracle - sql

I have a table like below. My aim is to count apple and mango under fruit.
I want to do this using case when statement. I run this query but it didn't work.
This works:
CASE
WHEN Food_type = 'vegetables'
AND Names IN ('apple', 'mango')
THEN 1
ELSE 0
END IN (0)
CASE
WHEN Names IN ('apple', 'orange', 'mango')
THEN 'fruit'
END Food_type2
Output:
Names
Food_type
apple
vegetables
eggplant
vegetables
carrot
vegetables
orange
fruit
onion
vegetables
spinach
vegetables
mango
vegetables
How can I do this ? Thank you.

This? Sample data till line #9; query begins at line #10.
SQL> with test (names, food_type) as
2 (select 'apple' , 'vegetables' from dual union all
3 select 'eggplant', 'vegetables' from dual union all
4 select 'carrot' , 'vegetables' from dual union all
5 select 'orange' , 'fruit' from dual union all
6 select 'onion' , 'vegetables' from dual union all
7 select 'spinach' , 'vegetables' from dual union all
8 select 'mango' , 'vegetables' from dual
9 )
10 select case when names in ('apple', 'mango') or food_type = 'fruit' then 'fruit'
11 else 'vegetables'
12 end food_type,
13 count(*)
14 from test
15 group by
16 case when names in ('apple', 'mango') or food_type = 'fruit' then 'fruit'
17 else 'vegetables'
18 end;
FOOD_TYPE COUNT(*)
---------- ----------
vegetables 4
fruit 3
SQL>

Related

Count the number of times word appears in a single column

I'm attempting to count the number of times apples and oranges appear in my fruit column.
The table looks like this:
Fruit
-------
Apples
Apples Oranges
Apples Oranges
Apples
Oranges
Expected output:
Apples 4
Oranges 3
My code thus far. I'm not sure how to do it when both appear and how to add them to the totals. I'm sure there is an easier way that this.
SELECT
COUNT (CASE WHEN Fruit LIKE '%Apples%' THEN '1' END) AS Apples
COUNT (CASE WHEN Fruit LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM Fruits
Cheers
If those fruits are single-worded and separated by a space, then such a generic approach might be interesting for you.
Lines #1 - 8 represent sample data; you already have that so you don't type it. Code you might need starts at line #10.
SQL> with fruit (fruit) as
2 -- sample data; you have that in a table
3 (select 'Apples' from dual union all
4 select 'Apples Oranges' from dual union all
5 select 'Apples Oranges' from dual union all
6 select 'Apples Lemon' from dual union all
7 select 'Oranges Plums' from dual
8 ),
9 -- split fruits to rows
10 temp as
11 (select regexp_substr(fruit, '[^ ]+', 1, column_value) fruit
12 from fruit cross join
13 table(cast(multiset(select level from dual
14 connect by level <= regexp_count(fruit, ' ') + 1
15 ) as sys.odcinumberlist))
16 )
17 select fruit, count(*)
18 from temp
19 group by fruit
20 order by fruit;
FRUIT COUNT(*)
-------------------------------------------------------- ----------
Apples 4
Lemon 1
Oranges 3
Plums 1
SQL>
Either with sum or count, it works
Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
SQL> with t as
(
select 'Apples' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples' as fruits from dual union all
select 'Oranges' as fruits 6 from dual
) select
SUM (CASE WHEN fruits LIKE '%Apples%' THEN '1' END) AS Apples ,
SUM (CASE WHEN fruits LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM t
;
APPLES ORANGES
---------- ----------
4 3
SQL> with t as
(
select 'Apples' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples Oranges' as fruits from dual union all
select 'Apples' as fruits from dual union all
select 'Oranges' as fruits from dual
) select
COUNT (CASE WHEN fruits LIKE '%Apples%' THEN '1' END) AS Apples ,
COUNT (CASE WHEN fruits LIKE '%Oranges%' THEN '1' END) AS Oranges
FROM t ;
APPLES ORANGES
---------- ----------
4 3
SQL>

(Oracle)Using asterisk in dataset

Code 1 works as expected. It returns the value '2', if 'apple' or 'banana'.
My goal is to use the asterisk '*' in fruit column of my table to express that if all the other then 'apple' or 'banana', the value is '1' .
I want Code 2 to return 1. How to modify?
--Code 1: returns 2 as expected. ok
with datatable as (
select 'apple' fruit, 2 value from dual
union
select 'banana' , 2 from dual
union
select '*' , 1 from dual
)
select value from datatable where fruit = 'banana';
The problem is,
--Code 2: Expected result is 1
with datatable as (
select 'apple' fruit, 2 value from dual
union
select 'banana' , 2 from dual
union
select '*' , 1 from dual
)
select value from datatable where fruit = 'cherry'; --How to modify here?
Like this?
select max(value) from datatable where fruit = 'cherry' or fruit = '*';
Examples:
SQL> with datatable as (
2 select 'apple' fruit, 2 value from dual
3 union
4 select 'banana' , 2 from dual
5 union
6 select '*' , 1 from dual
7 )
8 select max(value) from datatable where fruit = 'cherry' or fruit = '*';
MAX(VALUE)
----------
1
SQL> with datatable as (
2 select 'apple' fruit, 2 value from dual
3 union
4 select 'banana' , 2 from dual
5 union
6 select '*' , 1 from dual
7 )
8 select max(value) from datatable where fruit = 'banana' or fruit = '*';
MAX(VALUE)
----------
2
SQL>

Oracle pivot - one table field split into rows and columns

I have an Oracle 12c table that looks something like this:
Name Class Type
--------------------------
Alice Math S
Alice Female A
Bob Anthropology S
Bob Male A
Charlie Science S
Charlie Tennis A
Charlie Male A
Do Math S
Do Female A
Do Tennis A
Elmer Male A
Elmer Science S
I would like to pivot the table to produce a report that looks like below. Classes of type "S" are rows, and type "A" are columns. And then count the number of people that fall into each category. For example, there are two people that are both Female and take class Math - Alice and Do.
Female Male Tennis Total
-----------------------------------------------
Math 2 0 1 3
Anthropology 0 1 0 1
Science 0 2 1 3
Total 2 3 2 7
I am familiar with pivot and cube in Oracle but I am stuck with getting the Class field split between rows and columns in the report.
I would really appreciate your help.
You can use conditional aggregation as following:
SQL> with dataa(name, class, type) as
2 (
3 select 'Alice', 'Math' ,'S' from dual union all
4 select 'Alice', 'Female' ,'A' from dual union all
5 select 'Bob', 'Anthropology' ,'S' from dual union all
6 select 'Bob', 'Male' ,'A' from dual union all
7 select 'Charlie', 'Science' ,'S' from dual union all
8 select 'Charlie', 'Tennis' ,'A' from dual union all
9 select 'Charlie', 'Male' ,'A' from dual union all
10 select 'Do', 'Math' ,'S' from dual union all
11 select 'Do', 'Female' ,'A' from dual union all
12 select 'Do', 'Tennis' ,'A' from dual union all
13 select 'Elmer', 'Male' ,'A' from dual union all
14 select 'Elmer', 'Science' ,'S' from dual
15 ),
16 -- your query starts from here
17 CTE AS
18 (
19 SELECT S.CLASS AS CLASS,
20 COALESCE(SUM(CASE WHEN A.CLASS = 'Female' THEN 1 END),0) AS Female,
21 COALESCE(SUM(CASE WHEN A.CLASS = 'Male' THEN 1 END),0) AS Male,
22 COALESCE(SUM(CASE WHEN A.CLASS = 'Tennis' THEN 1 END),0) AS Tennis,
23 COALESCE(SUM(CASE WHEN A.CLASS IN ('Female','Male','Tennis') THEN 1 END),0) AS TOTAL
24 FROM DATAA S JOIN DATAA A ON (A.name = S.name)
25 WHERE S.type = 'S' AND A.TYPE = 'A'
26 GROUP BY S.CLASS
27 )
28 SELECT CLASS, Female, Male, Tennis, TOTAL FROM CTE
29 UNION ALL
30 SELECT 'Total' AS CLASS, SUM(Female), SUM(Male), SUM(Tennis), SUM(TOTAL) FROM CTE;
CLASS FEMALE MALE TENNIS TOTAL
------------ ---------- ---------- ---------- ----------
Science 0 2 1 3
Anthropology 0 1 0 1
Math 2 0 1 3
Total 2 3 2 7
SQL>
Cheers!!
You have the destination "row" value and the destination "column" value in separate rows. In order to do a GROUP BY CUBE, you need to put them together in the same input row using a JOIN. So do a JOIN, then a GROUP BY CUBE, then a PIVOT. To keep things straight, use the GROUPING function to see when you are grouping "rows" and when you are grouping "columns". This becomes a lot of work, and you wind up getting NULLs in some places where you want zeroes. Here is the solution for curiosity's sake:
with data(name, class, type) as (
select 'Alice', 'Math' ,'S' from dual union all
select 'Alice', 'Female' ,'A' from dual union all
select 'Bob', 'Anthropology' ,'S' from dual union all
select 'Bob', 'Male' ,'A' from dual union all
select 'Charlie', 'Science' ,'S' from dual union all
select 'Charlie', 'Tennis' ,'A' from dual union all
select 'Charlie', 'Male' ,'A' from dual union all
select 'Do', 'Math' ,'S' from dual union all
select 'Do', 'Female' ,'A' from dual union all
select 'Do', 'Tennis' ,'A' from dual union all
select 'Elmer', 'Male' ,'A' from dual union all
select 'Elmer', 'Science' ,'S' from dual
)
, joined_data as (
select s.class row_class, a.class col_class
from data s
join data a
on s.type = 'S' and a.type = 'A' and s.name = a.name
)
, cubed_data as (
select case grouping(col_class) when 1 then 'Total' else col_class end col_class,
row_class, grouping(row_class) gr_row, count(*) cnt
from joined_data
group by cube(row_class, col_class)
)
select case gr_row when 1 then 'Total' else row_class end " ",
"Female", "Male", "Tennis", "Total"
from cubed_data
pivot(
sum(cnt) for col_class in (
'Female' as "Female",'Male' as "Male",'Tennis' as "Tennis",'Total' as "Total"
)
)
order by gr_row, row_class;
Female Male Tennis Total
------------ ---------- ---------- ---------- ----------
Anthropology 1 1
Math 2 1 3
Science 2 1 3
Total 2 3 2 7
Regards,
Stew
For this requirement, I prefer the solution by Tejash to the PIVOT demonstration in my other answer. However, I would suggest using ROLLUP instead of UNION ALL. Two lines are changed and the UNION ALL is left out.
with data(name, class, type) as (
select 'Alice', 'Math' ,'S' from dual union all
select 'Alice', 'Female' ,'A' from dual union all
select 'Bob', 'Anthropology' ,'S' from dual union all
select 'Bob', 'Male' ,'A' from dual union all
select 'Charlie', 'Science' ,'S' from dual union all
select 'Charlie', 'Tennis' ,'A' from dual union all
select 'Charlie', 'Male' ,'A' from dual union all
select 'Do', 'Math' ,'S' from dual union all
select 'Do', 'Female' ,'A' from dual union all
select 'Do', 'Tennis' ,'A' from dual union all
select 'Elmer', 'Male' ,'A' from dual union all
select 'Elmer', 'Science' ,'S' from dual
)
SELECT case grouping(S.CLASS) when 1 then 'Total' else s.class end AS CLASS,
COALESCE(SUM(CASE WHEN A.CLASS = 'Female' THEN 1 END),0) AS Female,
COALESCE(SUM(CASE WHEN A.CLASS = 'Male' THEN 1 END),0) AS Male,
COALESCE(SUM(CASE WHEN A.CLASS = 'Tennis' THEN 1 END),0) AS Tennis,
count(*) AS TOTAL
FROM DATA S JOIN DATA A ON (A.name = S.name)
WHERE S.type = 'S' AND A.TYPE = 'A'
GROUP BY rollup(S.CLASS);
CLASS FEMALE MALE TENNIS TOTAL
------------ ---------- ---------- ---------- ----------
Anthropology 0 1 0 1
Math 2 0 1 3
Science 0 2 1 3
Total 2 3 2 7
Regards,
Stew

Transpose vertical input data to horizontal output in Oracle

I need to transpose rows to columns in Oracle. I've the data in this format:
Apple Orange Mango Banana
15 20 12 67
The required result is:
Fruit Number
Apple 15
Orange 20
Mango 12
Banana 67
I used Union to get this result but this is not the generic one.
SELECT ‘Apple’ AS Fruit, Apple AS Number FROM fruits_tbl UNION
SELECT ‘Orange’, Orange FROM fruits_tbl UNION
SELECT ‘Mango’, Mango FROM fruits_tbl UNION
SELECT ‘Banana’, Banana FROM fruits_tbl;
I want standard procedure to get the output as suggested.
Update: Figured out Pivot is the correct approach!
Since Oracle 11g (tab is your table name):
select * from tab
UNPIVOT (num for fruit in (apple as 'apple', orange as 'orange', mango as 'mango', banana as 'banana'));
Oracle 10g:
with col_names as (
select 'apple' fruit from dual
union all select 'orange' from dual
union all select 'mango' from dual
union all select 'banana' from dual
)
select c.fruit,
case c.fruit
when 'apple' then t.apple
when 'orange' then t.orange
when 'mango' then t.mango
when 'banana' then t.banana
end as num
from tab t
cross join col_names c;

Oracle - update , decode and set value

I have a table e.g.
**Fruit Number**
Apple 5
Grape 9
Orange 1
Coconut 54
Mango 22
I want to :
Select the whole list
Put 'Apple, Mango,Coconut' at top of the list.
Update the current order, where number = rownum
The list should look as follows :
**Fruit Number**
Apple 1
Mango 2
Coconut 3
Grape 4
Orange 5
I tried the following however getting syntax issues ..
update tablename
set id = rownum
where fruit in (select fruit from table order by decode(fruit,'Apple',1,'Mango',2,'Coconut',3))
UPDATE (
SELECT ROWNUM+5 AS r, fruit, number FROM TABLE
WHERE fruit NOT IN ('Apple', 'Mango', 'Coconut', 'Grape', 'Orange')
UNION
SELECT 1 AS r, 'Apple' AS fruit, number FROM TABLE
UNION
SELECT 2 AS r, 'Mango' AS fruit, number FROM TABLE
UNION
SELECT 3 AS r, 'Coconut' AS fruit, number FROM TABLE
UNION
SELECT 4 AS r, 'Grape' AS fruit, number FROM TABLE
UNION
SELECT 5 AS r, 'Orange' AS fruit, number FROM TABLE
)
SET number = r;
Following query will do that for you.
UPDATE table SET number =
CASE fruit
WHEN 'Apple' THEN 1
WHEN 'Mango' THEN 2
WHEN 'Coconut' THEN 3
WHEN 'Grape' THEN 4
WHEN 'Orange' THEN 5
END;