DB ORACLE QUERY - sql

I have a table where storing details
ID NAME
1 A
2 A
1 A
I need the output like
ID Name Count
1,2 A 3
Please help to get the output like that in oracle select query

In Oracle, you can use listagg(), but it has no distinct option. So, use a subquery and two levels of aggregation:
select listagg(id, ',') within group (order by id) as id, name, sum(cnt)
from (select id, name, count(*) as cnt
from t
group by id, name
) x
group by name;

Related

How to display duplicates in SQL only if another column is different?

So say I have this table:
Name
Role
First
Science
First
Math
First
Science
First
Math
Second
Science
Third
Math
Third
Math
I want to display a column of duplicates for Name/Role ONLY if role is different in each group. So the final result should be like this:
Name
Role
First
Science
First
Math
This is the only person that has a different role for the same name (no matter how many times that specific combination is duplicated). That's why even though Third/Math is also duplicated, it doesn't matter because it's the same combination.
I tried doing a CTE as follows:
;with cte as (
Select Name, Role, ROW_NUMBER() over (partition by name order by name) as 'rownum1'
from U.Users
group by u.name, u.role)
so then select * from cte where rownum > 1 gets me my names of people that have this issue but it doesn't display the duplicate roles for that user. Not sure how I should approach it differently?
If I join the CTE table to the original Users table, I also get the single entries.
You can take advantage of the fact that window functions are applied after aggregation:
select name, role
from (
select name, role, count(1) over (partition by name) c
from user_role
group by name, role
) r
where c > 1
https://www.db-fiddle.com/f/vzRDgBXwYp3VpgNyfn9qzL/0
You can try something like this:
WITH cte1 as (
SELECT distinct *
FROM
table1
),
cte2 as
(
Select Name, Role, ROW_NUMBER() over (partition by name order by name) as rnk
from cte1 u
group by u.name, u.role
)
SELECT * FROM cte2
where name in
(select name
from cte2
WHERE rnk > 1
group by name
)
I used a distinct function to remove any duplicates, then use the ROW_NUMBER() like you to find Names with multiple rows.
db fiddle link
So after I posted question I tried this which isn't as elegant as Kurt's answer but did also work:
;with cte as (select name, role, row_number() over (partition by name order by name) rownum
from user_role
group by name, role)
select distinct user_role.name, user_role.role from user_role
join cte on cte.name=user_role.name and cte.role=user_role.role
where user_role.name in (select name from cte where rownum =2)
https://www.db-fiddle.com/f/vzRDgBXwYp3VpgNyfn9qzL/2

How do I create a new SQL table with custom column names and populate these columns

So I currently have an SQL statement that generates a table with the most frequent occurring value as well as the least frequent occurring value in a table. However this table has 2 rows with the row values as well as the fields. I need to create a custom table with 2 columns with min and max. Then have one row with one value for each. The value for these columns needs to be from the same row.
(SELECT name, COUNT(name) AS frequency
FROM firefighter_certifications
GROUP BY name
ORDER BY frequency DESC limit 1)
UNION
(SELECT name, COUNT(name) AS frequency
FROM firefighter_certifications
GROUP BY name
ORDER BY frequency ASC limit 1);
So for the above query I would need the names of the min and max values in one row. I need to be able to define the name of new columns for the generated SQL query as well.
Min_Name | Max_Name
Certif_1 | Certif_2
I think this query should give you the results you want. It ranks each name according to the number of times it appears in the table, then uses conditional aggregation to select the min and max frequency names in one row:
with cte as (
select name,
row_number() over (order by count(*) desc) as maxr,
row_number() over (order by count(*)) as minr
from firefighter_certifications
group by name
)
select max(case when minr = 1 then name end) as Min_Name,
max(case when maxr = 1 then name end) as Max_Name
from cte
Postgres doesn't offer "first" and "last" aggregation functions. But there are other, similar methods:
select distinct first_value(name) over (order by cnt desc, name) as name_at_max,
first_value(name) over (order by cnt asc, name) as name_at_min
from (select name, count(*) as cnt
from firefighter_certifications
group by name
) n;
Or without any subquery at all:
select first_value(name) over (order by count(*) desc, name) as name_at_max,
first_value(name) over (order by count(*) asc, name) as name_at_min
from firefighter_certifications
group by name
limit 1;
Here is a db<>fiddle

how to achieve count(distinct) without group by in hive

I want to find the count(distinct column name) wihtout using group by in hive.
my input is :
name id
a 2
a 3
a 4
b 1
c 4
c 4
d 7
d 9
my expected output is
name count
a 3
b 1
c 1
d 2
can some tell me how to achieve this without using group by. please help
A canonical solution with no explicit group by is select distinct with window functions:
select distinct name, count(distinct id) over (partition by name)
from t;
In your case, I strongly recommend the group by version:
select name, count(distinct id)
from t
group by name;
You can use subquery :
select distinct t.name,
(select count(distinct id) from table t1 where t1.name = t.name) as count
from table t;
However, GROUP BY is really appropriate way to do this.
just use count aggregate function with distinct keyword
select name,count(distinct id) as cnt from table
group by name

Finding top count of a value in a table using SQL

I'm looking for a way to find the top count value of a column by SQL.
If for example this is my data
id type
----------
1 A
1 B
1 A
2 C
2 D
2 D
I would like the result to be:
1 A
2 D
I'm looking for a way to do it without groping by the column I count (type in the example)
Thanks
Statistically, this is called the "mode". You can calculate it using window functions:
select id, type, cnt
from (select id, type, count(*) as cnt,
row_number() over (partition by id order by count(*) desc) as seqnum
from t
group by id, type
) t
where seqnum = 1;
If there are ties, then an arbitrary value is chosen from among the ties.
You are looking for the statistic mode (the most often ocurring value):
select id, stats_mode(type)
from mytable
group by id
order by id;
Not all DBMS support this however. Check your docs, wheher this function or a similar one is available in your DBMS.
Just GROUP BY id, type and keep the rows with the maximum counter:
select id, type
from tablename
group by id, type
having count(*) = (
select count(*) from tablename group by id, type order by count(*) desc limit 1
)
See the demo
Or
select id, type
from tablename
group by id, type
having count(*) = (
select max(t.counter) from (select count(*) counter from tablename group by id, type) t
)
See the demo

SQL for counting rows and categorize

Is it possible to do the following for count >=3,4,5,6,7,8 etc.
rather than repeating the entire code for each count category
Insert into OnePlus (SELECT DISTINCT Id, Name, COUNT(DISTINCT StartDate) AS OnePlusDays
FROM DataTable
HAVING OnePlusDays >= 1
GROUP BY Id, Name)
Insert into TwoPlus (SELECT DISTINCT Id, Name, COUNT(DISTINCT StartDate) AS TwoPlusDays
FROM DataTable
HAVING TwoPlusDays >= 2
GROUP BY Id, Name)
Finally
SELECT Id, Name, "1+" AS Categories
FROM OnePlus
UNION
SELECT Id, Name, "2+" AS Categories
FROM TwoPlus
You mention only sql in the tags. Depending on MySql or SQL Server, you may need to change the Cast/Convert and Concatenation. But this query may help. You really don't need to put a Distinct on top a group by, the fact that you are grouping by, means only distinct values and their counts will be fetched.
Of course, the table OnePlus, is really what you call Categories.
Insert into OnePlus
SELECT Id, Name, convert(varchar(10), COUNT(DISTINCT StartDate) ) + "+" AS Categories
FROM DataTable
GROUP BY Id, Name
In T-SQL you can write as:
SELECT Id,
NAME , -- make sure you write case statement in desc order
CASE WHEN PlusDays > = 2 THEN '2+'
WHEN PlusDays > = 1 THEN '1+' END AS Categories
FROM
(
SELECT DISTINCT Id, Name, COUNT(DISTINCT StartDate) PlusDays
FROM #DataTable
GROUP BY Id, Name
) AS T
ORDER BY Id asc