ORACLE display count to zero, when row is null in column - sql

I have a table with many of columns, I count the specific names on column and if that name not on list, I want to display it zero, but not get in to list.
SELECT Names, COUNT (*)
FROM NAMESTABLE
WHERE names IN
('Jenny',
'Penny',
'George',
'James',
'Jessica',
'Monica',
'Erica')
AND
adeddate BETWEEN '2014/10/15' AND '2014/10/16'
GROUP BY names
NAMES || COUNT(*)
Jenny || 33
Penny || 4
George || 25
James || 87
so i want to Jessica, Monica, Erica as ZERO even these names are not in COLUMN
Names || Count(*)
Jenny || 33
Penny || 4
George || 25
James || 87
Jessica || 0
Monica || 0
Erica || 0
nvl(count(*),0) does not work

You could group your results by name and then left join the result to a table of your names to fill in the blanks:
SELECT rn.name, NVL(cnt, 0)
FROM (SELECT 'Jenny' AS name FROM dual
UNION ALL
SELECT 'Penny' FROM dual
UNION ALL
SELECT 'George' FROM dual
UNION ALL
SELECT 'James' FROM dual
UNION ALL
SELECT 'Jessica' FROM dual
UNION ALL
SELECT 'Monica' FROM dual
UNION ALL
SELECT 'Erica' FROM dual) rn
LEFT JOIN (SELECT name, COUNT(*) AS cnt
FROM namestable
WHERE adeddate BETWEEN '2014/10/15' AND '2014/10/16'
GROUP BY name) n ON n.name = rn.name

Then you must use other syntax:
SELECT t.Names, COUNT(n.Names)
FROM (
SELECT 'Jenny' AS names FROM DUAL UNION ALL
SELECT 'Penny' FROM DUAL UNION ALL
SELECT 'George' FROM DUAL UNION ALL
SELECT 'James' FROM DUAL UNION ALL
SELECT 'Jessica' FROM DUAL UNION ALL
SELECT 'Monica' FROM DUAL UNION ALL
SELECT 'Erica' FROM DUAL
) t
LEFT OUTER JOIN NAMESTABLE n
ON n.names = t.names AND n.adeddate BETWEEN '2014/10/15' AND '2014/10/16'
GROUP BY t.names

This should work with a CTE..
WITH CTE AS
(SELECT 'Jenny' Names FROM dual UNION ALL
SELECT 'Penny' FROM dual UNION ALL
SELECT 'George' FROM dual UNION ALL
SELECT 'James' FROM dual UNION ALL
SELECT 'Jessica' FROM dual UNION ALL
SELECT 'Monica' FROM dual UNION ALL
SELECT 'Erica' FROM dual)
SELECT CTE.names, coalesce(count(NT.Names),0)
FROM CTE
LEFT JOIN NAMESTABLE NT
on CTE.Names = NT.Names
and adeddate BETWEEN '2014/10/15' AND '2014/10/16'
GROUP BY CTE.Names

You could create an a table containing all valid names, eg valid_names table. Then join to your namestable using an outerjoin. Eg:
select valid_names.name, count(1)
from namestable, valid_names
where valid_names.name=namestable.name (+)
group by valid_names.name
order by valid_names.name;

Related

How to get the max count of an attribute with 3 tables?

I need to query which author sold the most books and how many books the author sold.
select a.firstname ||''|| a.lastname as fullname,
max(count(datesold))
from author a,
transaction t,
book b
where a.authorid = b.authorid
and b.bookid = t.bookid
group by
a.firstname,
a.lastname;
It gave me an error of not a single-group group function.
Any idea what is the issue here?
With some sample data
SQL> with
2 author (authorid, firstname, lastname) as
3 (select 1, 'Stephen', 'King' from dual union all
4 select 2, 'Jo' , 'Nesbo' from dual),
5 book (bookid, authorid) as
6 (select 100, 1 from dual union all
7 select 200, 1 from dual union all
8 select 300, 2 from dual
9 ),
10 transaction (trans_id, bookid) as
11 (select 1, 100 from dual union all
12 select 2, 100 from dual union all
13 select 3, 100 from dual union all
14 select 4, 300 from dual
15 ),
query uses the RANK analytic function which ranks rows by number of rows in the transaction table (it says how many books were sold). Finally, fetch row(s) that rank as highest:
16 temp as
17 (select a.firstname || ' ' || a.lastname AS fullname,
18 count(t.bookid) cnt,
19 rank() over (order by count(t.bookid) desc) rnk
20 from author a join book b on a.authorid = b.authorid
21 join transaction t on t.bookid = b.bookid
22 group by a.firstname, a.lastname
23 )
24 select fullname, cnt
25 from temp
26 where rnk = 1;
FULLNAME CNT
------------- ----------
Stephen King 3
SQL>
You can use:
select MAX(a.firstname ||' '|| a.lastname) as fullname,
COUNT(datesold)
from author a
INNER JOIN book b
ON (a.authorid = b.authorid)
INNER JOIN transaction t
ON (b.bookid = t.bookid)
GROUP BY
a.authorid
ORDER BY
COUNT(datesold) DESC
FETCH FIRST ROW ONLY;
Do not aggregate by firstname and lastname as there are many people in the world with identical names and you do not want to count everyone with the same name as a single person.
Which, for the sample data:
CREATE TABLE author (authorid, firstname, lastname, dateofbirth) AS
SELECT 1, 'Alice', 'Adams', DATE '1900-01-01' FROM DUAL UNION ALL
SELECT 2, 'Alice', 'Adams', DATE '1910-01-01' FROM DUAL UNION ALL
SELECT 3, 'Betty', 'Baron', DATE '1920-01-01' FROM DUAL UNION ALL
SELECT 4, 'Carol', 'Corrs', DATE '1930-01-01' FROM DUAL UNION ALL
SELECT 5, 'Carol', 'Corrs', DATE '1940-01-01' FROM DUAL;
CREATE TABLE book (bookid, authorid) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL UNION ALL
SELECT 4, 4 FROM DUAL UNION ALL
SELECT 5, 5 FROM DUAL;
CREATE TABLE transaction (bookid, datesold) AS
SELECT 1, DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 1, DATE '1970-01-02' FROM DUAL UNION ALL
SELECT 1, DATE '1970-01-03' FROM DUAL UNION ALL
SELECT 1, DATE '1970-01-04' FROM DUAL UNION ALL
SELECT 3, DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 4, DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 4, DATE '1970-01-02' FROM DUAL UNION ALL
SELECT 5, DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 5, DATE '1970-01-02' FROM DUAL UNION ALL
SELECT 5, DATE '1970-01-03' FROM DUAL;
Outputs:
FULLNAME
COUNT(DATESOLD)
Alice Adams
4
db<>fiddle here

How to count distinct flags in the sql

BELOW IS SNIPPET OF MY DATA
Here is the sample creation code of testing.
CREATE TABLE MYGROUP ( Category,PERSON,Flag ) AS
SELECT 'Cat1','A','1' FROM DUAL
UNION ALL SELECT 'Cat1','A','0' FROM DUAL
UNION ALL SELECT 'Cat1','A','1' FROM DUAL
UNION ALL SELECT 'Cat1','B','1' FROM DUAL
UNION ALL SELECT 'Cat1','B','0' FROM DUAL
UNION ALL SELECT 'Cat2','A','0' FROM DUAL
UNION ALL SELECT 'Cat2','A','0' FROM DUAL
UNION ALL SELECT 'Cat2','A','0' FROM DUAL
UNION ALL SELECT 'Cat2','B','1' FROM DUAL
UNION ALL SELECT 'Cat2','B','1' FROM DUAL
UNION ALL SELECT 'Cat2','B','0' FROM DUAL
UNION ALL SELECT 'Cat3','X','0' FROM DUAL
UNION ALL SELECT 'Cat3','Y','0' FROM DUAL;
Desired Output:
Category - Count of Distinct Persons with Flag = 1
Cat1 - 2
Cat2 - 1
Cat3 - 0
I need to get my code in Big query to get distinct counts of persons. It shouldnt double count.
You can use conditional aggregation
SELECT
Category,
COUNT(DISTINCT CASE WHEN Flag = 1 THEN PERSON END)
FROM MYGROUP
GROUP BY Category;

How to group columns based on specific text and sum them using ORACLE database

I am a newbie in oracle. Trying to group columns based on a specific text and sum other columns.
let's say I have below tables
I want to group by all people with the last name. Like below
I have no clue how should I proceed.
Note: There could be multiple names but I just want to filter for Sharma, Decosta and Liver.
Updating the question.
Sorry for updating the question. but in a real scenario search string is not always at last.
Let me give you another example.
Output.
The queries offered so far does seem to work to get what you need. But the below does:
With inputs as
(
select 'Rahul Kumar Sharma' as Name, 10 as No_of_beer_bottles from dual union all
select 'Rohith Kumar Sharma' as Name, 5 as No_of_beer_bottles from dual union all
select 'Sharma' as Name, 20 as No_of_beer_bottles from dual union all
select 'Rahul Kumar Varma' as Name, 10 as No_of_beer_bottles from dual
)
select SUBSTR(name,Instr(name,' ',-1)+1) as Name, sum(No_of_beer_bottles) as No_of_beer_bottles
from inputs
group by SUBSTR(name,Instr(name,' ',-1)+1);
Output:
You can use regexp_substr():
select regexp_substr(name, '[^ ]+$') as last_name, sum(num_beers)
from t
group by regexp_substr(name, '[^ ]+$');
If you use have a table with all the words you are searching for (or define them in a Common Table Expression like I did below), you can join on that search term then sum the numeric column.
Query 1
WITH
search_words (word)
AS
(SELECT 'Sharma' FROM DUAL
UNION ALL
SELECT 'Decosta' FROM DUAL
UNION ALL
SELECT 'Liver' FROM DUAL),
d (name, num_beers)
AS
(SELECT 'Ravi Sharma', 1 FROM DUAL
UNION ALL
SELECT 'Sam Decosta', 4 FROM DUAL
UNION ALL
SELECT 'Jhony Liver', 5 FROM DUAL
UNION ALL
SELECT 'Ravi Sharma', 2 FROM DUAL
UNION ALL
SELECT 'Rohul Sharma', 3 FROM DUAL
UNION ALL
SELECT 'Jhon Decosta', 3 FROM DUAL
UNION ALL
SELECT 'V Decosta', 3 FROM DUAL)
SELECT sw.word, SUM (d.num_beers) AS total_beer
FROM search_words sw, d
WHERE INSTR (UPPER (d.name), UPPER (sw.word)) > 0
GROUP BY sw.word;
Result 1
WORD TOTAL_BEER
__________ _____________
Decosta 10
Sharma 6
Liver 5
Query 2
WITH
search_words (word)
AS
(SELECT 'Pen' FROM DUAL
UNION ALL
SELECT 'Dog' FROM DUAL),
d (name, quantity)
AS
(SELECT 'Ravi have red pens', 2 FROM DUAL
UNION ALL
SELECT 'Sam''s grifriend have black dogs', 4 FROM DUAL
UNION ALL
SELECT 'Jhony has blue pen', 1 FROM DUAL
UNION ALL
SELECT 'Ravi has white dog', 1 FROM DUAL
UNION ALL
SELECT 'Rahul has small dogs', 3 FROM DUAL
UNION ALL
SELECT 'There are pens inside Jhon''s car', 3 FROM DUAL
UNION ALL
SELECT 'My dog and me are flying.', 1 FROM DUAL)
SELECT sw.word, SUM (d.quantity) AS total_quantity
FROM search_words sw, d
WHERE INSTR (UPPER (d.name), UPPER (sw.word)) > 0
GROUP BY sw.word;
Result 2
WORD TOTAL_QUANTITY
_______ _________________
Dog 9
Pen 6

converting comma separated value to multiple rows

I have a table like this:
ID NAME Dept_ID
1 a 2,3
2 b
3 c 1,2
Department is another table having dept_id and dept_name as columns. i want result like,
ID Name Dept_ID
1 a 2
1 a 3
2 b
3 c 1
3 c 2
any help please?
You can do it as:
--Dataset Preparation
with tab(ID, NAME,Dept_ID) as (Select 1, 'a', '2,3' from dual
UNION ALL
Select 2, 'b','' from dual
UNION ALL
Select 3, 'c' , '1,2' from dual)
--Actual Query
select distinct ID, NAME, regexp_substr(DEPT_ID,'[^,]+', 1, level)
from tab
connect by regexp_substr(DEPT_ID,'[^,]+', 1, level) is not null
order by 1;
Edit:
based on which column i need to join? in one table i have comma
separated ids and in other table i have just ids
with tab(ID, NAME,Dept_ID) as (Select 1, 'a', '2,3' from dual
UNION ALL
Select 2, 'b','' from dual
UNION ALL
Select 3, 'c' , '1,2' from dual) ,
--Table Dept
tbl_dept (dep_id,depname) as ( Select 1,'depa' from dual
UNION ALL
Select 2,'depb' from dual
UNION ALL
Select 3,'depc' from dual
) ,
--Seperating col values for join. Start your query from here using with clause since you already have the two tables.
tab_1 as (select distinct ID, NAME, regexp_substr(DEPT_ID,'[^,]+', 1, level) col3
from tab
connect by regexp_substr(DEPT_ID,'[^,]+', 1, level) is not null
order by 1)
--Joining table.
Select t.id,t.name,t.col3,dt.depname
from tab_1 t
left outer join tbl_dept dt
on t.col3 = dt.dep_id
order by 1
with tmp_tbl as(
select
1 ID,
'a' NAME,
'2,3' DEPT_ID
from dual
union all
select
2 ID,
'b' NAME,
'' DEPT_ID
from dual
union all
select
3 ID,
'c' NAME,
'1,2' DEPT_ID
from dual)
select
tmp_out.ID,
tmp_out.NAME,
trim(tmp_out.DEPT_ID_splited)
from(
select
tmp.ID,
tmp.NAME,
regexp_substr(tmp.DEPT_ID,'[^,]+', 1, level) DEPT_ID_splited
from
tmp_tbl tmp
connect by
regexp_substr(tmp.DEPT_ID,'[^,]+', 1, level) is not null) tmp_out
group by
tmp_out.ID,
tmp_out.NAME,
tmp_out.DEPT_ID_splited
order by
tmp_out.ID,
tmp_out.DEPT_ID_splited

ORACLE SQL JOINS

I have the two tables:
TABLE1:
id name values
1 john AB
2 marry CD
3 sreya YG
TABLE2:
pid country values
45 india JKABHJ
46 usa YURRRCD
47 uk YGHJJKLJL
output
name values country
john AB india
marry CD usa
sreya YG uk
I want to join these two tables on the common columns values, but the other table columns contain extra data. How to overcome this?
table2 column "values" contains data matching to table1 "values"
values
AB
CD
YG
values
JKABHJ
YURRRCD
YGHJJKLJL
You can use like operator in query for matching values in table1 and table2.
For this query:
WITH table1 as (
select 1 as id, 'john' as name, 'AB' as value from dual union all
select 2 as id, 'marry' as name, 'CD' as value from dual union all
select 3 as id, 'sreya' as name, 'YG' as value from dual
),
table2 as (
select 45 as id, 'india' as country, 'JKABHJ' as value from dual union all
select 46 as id, 'usa' as country, 'YURRRCD' as value from dual union all
select 47 as id, 'uk' as country, 'YGHJJKLJL' as value from dual
)
select a.name, a.value, b.country
from table1 a
join table2 b on b.value like '%'||a.value||'%';
Output:
NAME VALUE COUNTRY
john AB india
marry CD usa
sreya YG uk
But I would recommend you to change a structure to make it more efficient. For example, by adding new table table2_values with column id referenced to table2.id and split values:
WITH table1 as (
select 1 as id, 'john' as name, 'AB' as value from dual union all
select 2 as id, 'marry' as name, 'CD' as value from dual union all
select 3 as id, 'sreya' as name, 'YG' as value from dual
),
table2 as (
select 45 as id, 'india' as country from dual union all
select 46 as id, 'usa' as country from dual union all
select 47 as id, 'uk' as country from dual
),
table2_values as (
select 45 as id, 'JK' as value from dual union all
select 45 as id, 'AB' as value from dual union all
select 45 as id, 'HJ' as value from dual union all
select 46 as id, 'YU' as value from dual union all
select 46 as id, 'RRR' as value from dual union all
select 46 as id, 'CD' as value from dual union all
select 47 as id, 'YG' as value from dual union all
select 47 as id, 'HJ' as value from dual
)
select a.name, a.value, c.country
from table1 a
join table2_values b on b.value = a.value
join table2 c on c.id = b.id;
You should use like operator while joining the two tables.
As below
SELECT *
FROM TABLE1
JOIN TABLE2
ON TABLE1.values like CONCAT('%',TABLE2.values,'%')