Getting distinct values of induvidual columns - sql

I have a table A as below
id| Name|Subject
--|-----|-------
1 |Mano |Science
2 |Pavi |Maths
3 |Mano |Social
1 |Kalai|Maths
4 |Kalai|Science
I want distinct values for each column.
So My output be like
id|Name | Subject
--|-----|--------
1 |Mano |Science
2 |Pavi |Maths
3 |Kalai|Social
4 | |
I have tried using cursors. But I didn't get what I needed.
Anybody help me in getting this

You seem to just want a list of the distinct values, without regards to what appears together. This isn't very SQL'ish, but can be done:
select row_number() over (order by n.seqnum) as firstcol, n.name, s.subject
from (select name, row_number() over (order by name) as seqnum
from t
group by name
) n full outer join
(select subject, row_number() over (order by subject) as seqnum
from t
group by subject
) s
on s.seqnum = n.seqnum;

select *
from (select col,val,dense_rank () over (partition by col order by val) as dr
from mytable unpivot (val for col in (name,subject)) u
) pivot (min(val) for col in ('NAME','SUBJECT'))
order by dr
+----+-------+---------+
| DR | NAME | SUBJECT |
+----+-------+---------+
| 1 | Kalai | Maths |
| 2 | Mano | Science |
| 3 | Pavi | Social |
+----+-------+---------+

Related

How to use pivot to select and flatten table?

I'm trying to select from a table that essentially is a hierarchy of groups and fields in each group. Each row has a group id column and I'm trying to flatten it into rows of each group id and their fields.
For example
group id | field1
1 | a
1 | b
1 | a
1 | b
2 | c
2 | d
2 | c
2 | d
3 | e
3 | f
3 | g
3 | e
3 | f
3 | g
4 | h
It is guaranteed that a group will map to the same fields values so group 1 will always have the same number of rows with field 'a' as with field 'b'.
The target is this:
group id | field1 | field2 | field 3
1 | a | b | null
2 | c | d | null
3 | e | f | g
4 | h | null | null
I have been playing with over (order by group id) but I haven't made any progress with that or pivots either.
I wouldn't use pivot. I would use conditional aggregation and dense_rank():
select group_id,
max(case when seqnum = 1 then field1 end) as field1,
max(case when seqnum = 2 then field1 end) as field2,
max(case when seqnum = 3 then field1 end) as field3
from (select t.*,
dense_rank() over (partition by group_id order by field1) as seqnum
from t
) t
group by group_id
Not sure this will solve your problem. If you are generating any report then you can use LISTAGG function.
select listagg( field_1 , ',') within group (order by group_id)
from (
select distinct group_id, field_1 from table
);

How to list the latest series with no gaps of a given clause?

Given the following example table:
+-----------+
| Id | Name |
+----+------+
| 1 | A |
| 2 | B |
| 3 | B |
| 4 | C |
| 5 | A |
| 6 | B |
| 7 | B |
| 8 | B |
| 9 | B |
| 10 | X |
+----+------+
I would like a query to get the following result:
+----+------+
| 6 | B |
| 7 | B |
| 8 | B |
| 9 | B |
+----+------+
The best query I could do was:
SELECT * FROM
(SELECT id, name, LEAD(id) OVER (ORDER BY id) t
FROM test WHERE name = 'B' ORDER BY id)
WHERE ID <> t-1;
sqlfiddle here
If you want the length and where it starts:
select min(id), max(id)
from (select t.*,
row_number() over (order by id) as seqnum,
row_number() over (partition by name order by id) as seqnum_1
from test t
) t
where name = 'B'
group by (seqnum - seqnum_1)
order by min(id) desc
fetch first 1 row only;
You can join back to the table to get the original rows.
Another method using window functions to count the number of non-Bs after a given row . . . and then choose the first:
select t.*
from (select t.*,
dense_rank() over (order by nonbs_after asc) as grp
from (select t.*,
sum(case when name <> 'B' then 1 else 0 end) over (order by id desc) as nonbs_after
from test t
) t
where name = 'B'
) t
where grp = 1;
Here is a db<>fiddle.

Using ROW_NUMBER () to Compare 1st ARRAY to 2nd, 3rd, 4th, etc

I'm using ROW_NUMBER and I'm trying to compare arr in rn 1 to arr in rn 2,3,4,etc
to see if they overlap. I can do this with a subquery / simple join. Is there a way that AVOIDS a join?
rn | id | job | arr |desired_result
---+----+-----+--------+---------
1 | 1 | 100 | {1,2} | {1,2}
2 | 1 | 101 | {2,3} | {1,2}
3 | 1 | 102 | {5,6,8}| {1,2}
4 | 1 | 103 | {2,7} | {1,2}
I made a dbfiddle
--USING JOIN
WITH a AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY id ORDER by job) as rn
,*
FROM a_table
)
SELECT *
FROM (
SELECT id,arr
FROM a
WHERE rn = 1
) x
JOIN a
ON a.id=x.id
You can use first_value():
SELECT a.*, first_value(arr) over (partition by id order by job)
FROM a_table a;
row_number() does not seem necessary.

Select top 1 row with aggregate function

I have data in table like this:
UserData table:
|ID| Name | TeamName |
| 1| Peter | Alpha |
| 1| Peter | Beta |
| 1| Peter | Gamma |
| 2| Mary | Gamma |
| 2| Mary | Omega |
| 3| John | Kappa |
| 3| John | Delta |
Combinations of Name and TeamName are always unique. I need for each unique ID and Name get the top 1 TeamName and number of Team relations, like this:
table #FinalTable
|ID| Name | TeamName | NumberOfRelations |
| 1| Peter | Alpha | 3 |
| 2| Mary | Gamma | 2 |
| 3| John | Kappa | 2 |
Question - is there a way of doing this in one query, or do I have to use temporary tables for selection top 1 team and for counting number of relations and then select data indo separate final table?
I tried something like this:
;WITH cte AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY TeamName Asc) AS rn
FROM UserData
)
SELECT * into #tempTable1
FROM cte
WHERE rn = 1
and this:
insert into #tempTable2 (ID, Name, NumberOfRelations)
select ID, Name, count(*) as NumberOfRelations
from UserData
group by ID, Name
...and then selecting data from two temp tables.
I wonder if there's more simple way of doing it.
For SQLserver:
You don't have order by,so i choose one below...
select top 1 with ties id,playen,count(id) over (partition by id,playen) as countt
,temaname
from #temp t1
order by row_number() over (partition by id,playen order by id,playen,temaname)
Output:
id playen countt temaname
1 Peter 3 Alpha
2 Mary 2 Gamma
3 John 2 Delta
Assuming this is SQL Server try this :
Select t.ID, t.Name, team.TeamName, count(t.TeamName) countt
from #temp t join
(Select id, TeamName, Row_Number() over (Partition By ID Order By TeamName asc) as rn
from #temp) team on (team.ID = t.ID and team.rn=1)
Group by t.ID, t.Name, team.TeamName
SQL tables represent unordered sets. There is no first team name, unless a column specifies the ordering. You don't seem to have such a column.
If you had such a column:
WITH cte AS (
SELECT ud.*,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ??) as seqnum,
COUNT(*) OVER (PARTITION BY ID) as cnt
FROM UserData ud
)
SELECT cte.*
FROM cte
WHERE seqnum = 1;
Note the ??. This is to specify the ordering for getting the team name. Depending on the database, you can use NULL or (SELECT NULL) to get an arbitrary team name.

oracle group related subquery

Helo,
I have 2 simple tables and I must make one query which group row "GRO" from PROD and return "gro" which is used most often by coid. I try use subquery but i have error, I dont know how to transform this query to work.
Please help me :)
I use Oracle SQL .
TABLE CO
ID |name|param|
---+----+-----+
1 |AA | X |
2 |BB | X |
3 |CC | X |
4 |DD | X |
5 |EE | |
Table PROD
id| coid |gro
--+------+------
1 | 1 | a
2 | 1 | a
3 | 1 | b
4 | 1 | c
5 | 1 | d
6 | 2 | b
7 | 2 | c
8 | 2 | c
9 | 3 | a
10| 3 | a
11| 4 | b
12| 4 | b
13| 4 | b
14| 4 | a
15| 1 | a
Result
ID |name|best_gro|
---+----+--------+
1 |AA | a |
2 |BB | c |
3 |CC | a |
4 |DD | b |
My Query
select c.id, c.name,
(
select gro from(
SELECT GRO, count(GRO) as m FROM HR.PROD pro
where coid = c.id --<------ ERROR
group by gro order by m desc
) where rownum <=1
) as best_gro
from HR.co c
where c.param = 'X'
You can't refer to your c column more than one level of subquery below where it is defined.
You can rewrite this with a join, using the analytic row_number() function, something like:
select id, name, gro
from (
select id, name, gro,
row_number() over (partition by id, name order by cnt desc) rnk
from (
select c.id, c.name, p.gro, count(*) as cnt
from HR.co c
join HR.prod p on p.coid = c.id
where c.param = 'X'
group by c.id, c.name, p.gro
)
)
where rnk = 1
Depending on what you want to do with ties - if two or more gro values have the same count for a compary - you need the order by to specify how to choose which to use; or use rank or dense_rank instead of row_number to get all tied values.
Or you can use the keep dense rank approach which will be shorter.
Thanks Alex,
i modified a little and i got correct result !
select id, name, gro from(
select id, name, gro,
rank() over (partition by coid order by cnt desc) rnk
from (
select c.id, c.name, p.gro, p.coid, count(*) as cnt
from HR.co c
join HR.prod p on p.coid = c.id
where c.param = 'X'
group by c.id, c.name, p.gro, p.coid
)
)
where rnk = 1