Find rows with the same values in a column - sql

I've a table LESSON like this
| Student_id | Lesson_id |
| ---------- |- -------- |
| 352-03-3624| 10359427 |
| 352-03-3624| 10359449 |
| 805-17-4144| 58149917 |
| 805-17-4144| 58149968 |
I have to look for students who have taken the same lessons, ie who have the same lesson_id.
I used this query:
select * from lesson e
where exists
(select null from lesson i
where e.lesson_id = i.lesson_if and e.student_id <> i.student_id)
order by lesson_id
but it doesn't work very well.
Is there someone who can help me?
thanks
I'm finding for every studend if there is another student who follows the same lessons.

Just use the HAVING clause:
WITH lessons AS
(
SELECT '352-03-3624' as student_id, '10359427' as lesson_id FROM dual UNION ALL
SELECT '352-03-3624', '10359449' FROM dual UNION ALL
SELECT '805-17-4144', '58149917' FROM dual UNION ALL
SELECT '805-17-4144', '58149968' FROM dual UNION ALL
SELECT '805-17-4144', '10359427' FROM dual UNION ALL
SELECT '805-17-4143', '10359427' FROM dual UNION ALL
SELECT '805-17-4144', '10359449' FROM dual
)
SELECT lsns.lesson_id, lsns.student_id
FROM lessons lsns
, (SELECT COUNT(1), lesson_id
FROM lessons
GROUP BY lesson_id
HAVING COUNT(1) > 1) lsns_cnt
WHERE lsns_cnt.lesson_id = lsns.lesson_id;
PS. I added more data in order to have a result set. It contains the course and the student that are matching the criteria.

Another option might be to use count in its analytic form and then fetch rows whose count is larger than 1 (which means that there are two or more students who took the same lesson).
Sample data:
SQL> WITH lessons (student_id, lesson_id) AS
2 (
3 SELECT '352-03-3624', '10359427' FROM dual UNION ALL
4 SELECT '352-03-3624', '10359449' FROM dual UNION ALL
5 SELECT '805-17-4144', '58149917' FROM dual UNION ALL
6 SELECT '805-17-4144', '58149968' FROM dual UNION ALL
7 SELECT '805-17-4144', '10359427' FROM dual UNION ALL
8 SELECT '805-17-4143', '10359427' FROM dual UNION ALL
9 SELECT '805-17-4144', '10359449' FROM dual
10 )
Query begins here:
11 select lesson_id, student_id
12 from (select lesson_id,
13 student_id,
14 count(*) over (partition by lesson_id) cnt
15 from lessons
16 )
17 where cnt > 1
18 order by lesson_id, student_id;
LESSON_I STUDENT_ID
-------- -----------
10359427 352-03-3624
10359427 805-17-4143
10359427 805-17-4144
10359449 352-03-3624
10359449 805-17-4144
SQL>

Related

Concat multiple rows based on If/Then to poulate a 5 digit code

Hoping someone can help.
I have data as follows in two seperate columns in a table called StudentRace
Student_ID RaceCD
---------- ------
123456 1
123456 2
589645 4
987654 3
987654 4
I am looking for a way to combine the data for the students by student id to output into 00000 format. example: Student_ID 123456 RACE: 12000; Student_ID 589645 Race: 00040; Student_ID 987654 Race = 00340. I need to have it be a sub query as it is part of a large report that pulls 50+ fields. If anyone is able to help I would greatly appreciate it. I am using Toads Data Point for Oracle to create my query.
The result can be achieved with a single group by without any joins.
Setup
CREATE TABLE studentrace
(
Student_ID,
RaceCD
)
AS
SELECT 123456, 1 FROM DUAL
UNION ALL
SELECT 123456, 2 FROM DUAL
UNION ALL
SELECT 589645, 4 FROM DUAL
UNION ALL
SELECT 987654, 3 FROM DUAL
UNION ALL
SELECT 987654, 4 FROM DUAL;
Query
SELECT student_id, LPAD (SUM (racecd * POWER (10, 5 - racecd)), 5, '0') AS race
FROM studentrace
GROUP BY student_id;
Result
STUDENT_ID RACE
_____________ ________
589645 00040
987654 00340
123456 12000
You can use a partitioned outer join:
SELECT t.Student_id,
LISTAGG( COALESCE( t.raceCD, 0 ) ) WITHIN GROUP ( ORDER BY r.race )
AS RaceCDs
FROM ( SELECT LEVEL AS race
FROM DUAL
CONNECT BY LEVEL <= 5 ) r
LEFT OUTER JOIN table_name t
PARTITION BY ( t.Student_ID )
ON ( r.race = t.RaceCD )
GROUP BY t.student_id
Which, for your test data:
CREATE TABLE table_name ( Student_ID, RaceCD ) AS
SELECT 123456, 1 FROM DUAL UNION ALL
SELECT 123456, 2 FROM DUAL UNION ALL
SELECT 589645, 4 FROM DUAL UNION ALL
SELECT 987654, 3 FROM DUAL UNION ALL
SELECT 987654, 4 FROM DUAL
Outputs:
STUDENT_ID | RACECDS
---------: | :------
123456 | 12000
589645 | 00040
987654 | 00340
db<>fiddle here

Print message when no data is found

Need a query to get the Employee name, total fuel used by each employee.
If fuel is not used by an employee then the second column should have a
text “No fuel used”.
These are the following two tables:
Table1: EmployeeID, FirstName
1 Vikas
2 nikita
3 Ashish
4 Nikhil
5 anish
Table2: ID, Fuel
1 10
2 9
3 8
4 6
5 12
6 11
7 10
8 9
9 8
10 10
11 9
12 12
13 7
14 15
where The column table2.ID is a foreign key to table1.EmployeeID.
This is code which I have written, Which is most probably wrong.
select ID, FirstName, sum(table2.Fuel) sum_fuel
from table2,table1
where EmployeeID=ID IN (
select ID, coalesce(ID, 'No-fuel used') as ID
from table1 t1
left join table2 t2 on t2.ID = t1.EmployeeID
)
group by fuel
order by ID DESC;
As you can see from two tables that employee with from 1 to 5 of table1 are in table2. So for these employee I need to show total fuel used by every individual. And for employee with ID from 6 to 14 are not available in table1 so for these employee “No fuel used” message should be printed.
You can use a left join. This way, whenever the Id values for tables don't match you'll get null values for sum(fuel) value, and will assign the string 'No fuel used'for sum_fuel column by using nvl() function:
with table1( EmployeeID, FirstName ) as
(
select 1,'Vikas' from dual union all
select 2,'nikita' from dual union all
select 3,'Ashish' from dual union all
select 4,'Nikhil' from dual union all
select 5,'anish' from dual union all
select 15,'pratteek' from dual
), table2( ID, Fuel ) as
(
select 1, 10 from dual union all
select 2, 9 from dual union all
select 3, 8 from dual union all
select 4, 6 from dual union all
select 5, 12 from dual union all
select 6, 11 from dual union all
select 7, 10 from dual union all
select 8, 9 from dual union all
select 9, 8 from dual union all
select 10, 10 from dual union all
select 11, 9 from dual union all
select 12, 12 from dual union all
select 13, 7 from dual union all
select 14, 15 from dual
)
select EmployeeID, FirstName, nvl(to_char(sum(t2.Fuel)),'No fuel used') as sum_fuel
from table1 t1
left join table2 t2
on t1.EmployeeID = t2.ID
group by EmployeeID, FirstName
order by EmployeeID desc;
EMPLOYEEID FIRSTNAME SUM_FUEL
---------- --------- ------------
15 pratteek No fuel used
5 anish 12
4 Nikhil 6
3 Ashish 8
2 nikita 9
1 Vikas 10
Demo
This may work---
SELECT ID
, FirstName
, CASE
WHEN SUM(f.Fuel) > 0 THEN CAST(SUM(f.Fuel) AS NVARCHAR(25))
ELSE 'No fuel used'
END sum_fuel
FROM #emp e
LEFT JOIN #fuel f ON e.EmployeeID = f.id
GROUP BY ID,FirstName
ORDER BY ID DESC

Oracle - count records if number of token less than 2

I have records like...
ID | KEY
-------|---------
1 | 123_456_abc
1 | 123_xyz
1 | 456_abc
2 | 123_abc
2 | 122_73_zcc
3 | 123_wer
4 | 345_23_fhd
4 | 3453_abc
5 | ad1fr2h3_abcasd
5 | ers2g45bb_abc2rtd
5 | asf23g_abc1_sf45
I want count(ID) where count(tokanize(numeric(KEY),'_')) < 2
As count(ID) will be 6
You can try something like this
SELECT COUNT(ID) FROM xyz WHERE key NOT LIKE '%_%_%';
This should filter all elements which have less than two underscores.
Try this :
select Count(1) from
(with abc(id,key) as (select '1','123_456_abc' from dual
Union all
select '1','123_xyz' from dual
UNion all
select '1','456_abc' from dual
Union all
select '2','123_abc' from dual
UNion all
select '2','123_73_zcc' from dual
Union all
select '3','123_wer' from dual
UNion all
select '1','345_23_fhd' from dual
UNion all
select '1','345_abc' from dual
)
select key, length(regexp_replace(key,'[^_]*','')) cntr
from abc )
where cntr = 1
eliminate all records which has more than 1 underscores
then eliminate the ones which do not start with a number
then sum it up
select sum(cnt) from (
select key, cnt, id from (
select key, length(regexp_replace(key,'[^_]*','')) cnt, id from table_name
) where cnt < 2
) where regexp_like(key,'[1-9]+(.)*')

Need to fetch rows which having lowest plus some number and with out using inner or sub query

CREATE TABLE "User" ( Name, Age ) AS
SELECT 'Ira1', 10 FROM DUAL
UNION ALL SELECT 'Ira2', 11 FROM DUAL
UNION ALL SELECT 'Ira3', 15 FROM DUAL
UNION ALL SELECT 'Ira4', 16 FROM DUAL
UNION ALL SELECT 'Ira5', 17 FROM DUAL
I want those rows whose Age is greater than lowest Age +5. Lowest Age is 10.
So i want all those having Age greater than 15.
The inner query which I have is.
select * from user
where age > (select age+5 from (select age from user order by age asc) where rownum=1);
Which returns:
Ira4 16
Ira5 17
Is there a way we can do it using single query. I mean no inner or sub query.
You can simplify the code slightly by using the MIN aggregation function (2 table scans):
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE "User" ( Name, Age ) AS
SELECT 'Ira1', 10 FROM DUAL
UNION ALL SELECT 'Ira2', 11 FROM DUAL
UNION ALL SELECT 'Ira3', 15 FROM DUAL
UNION ALL SELECT 'Ira4', 16 FROM DUAL
UNION ALL SELECT 'Ira5', 17 FROM DUAL
Query 1:
SELECT *
FROM "User"
WHERE Age > ( SELECT MIN( Age ) + 5 FROM "User" )
Results:
| NAME | AGE |
|------|-----|
| Ira4 | 16 |
| Ira5 | 17 |
Query 2:
And you can get a completely different explain plan using an analytic function (only 1 table scan):
SELECT Name, Age
FROM (
SELECT u.*,
MIN( Age ) OVER ( ORDER BY Age ) AS min_age
FROM "User" u
)
WHERE Age > Min_Age + 5
Results:
| NAME | AGE |
|------|-----|
| Ira4 | 16 |
| Ira5 | 17 |
You could use an analytic function to get the minimum age, but you'd still need a subquery. It would only do one pass through the table though.
with usr ( Name, Age ) AS ( SELECT 'Ira1', 10 FROM DUAL
UNION ALL SELECT 'Ira2', 11 FROM DUAL
UNION ALL SELECT 'Ira3', 15 FROM DUAL
UNION ALL SELECT 'Ira4', 16 FROM DUAL
UNION ALL SELECT 'Ira5', 17 FROM DUAL)
select name,
age
from (select name,
age,
min(age) over () min_age
from usr)
where age > min_age + 5;
NAME AGE
---- ----------
Ira4 16
Ira5 17

How to do select count(*) group by and select * at same time?

For example, I have table:
ID | Value
1 hi
1 yo
2 foo
2 bar
2 hehe
3 ha
6 gaga
I want my query to get ID, Value; meanwhile the returned set should be in the order of frequency count of each ID.
I tried the query below but don't know how to get the ID and Value column at the same time:
SELECT COUNT(*) FROM TABLE group by ID order by COUNT(*) desc;
The count number doesn't matter to me, I just need the data to be in such order.
Desire Result:
ID | Value
2 foo
2 bar
2 hehe
1 hi
1 yo
3 ha
6 gaga
As you can see because ID:2 appears most times(3 times), it's first on the list,
then ID:1(2 times) etc.
you can try this -
select id, value, count(*) over (partition by id) freq_count
from
(
select 2 as ID, 'foo' as value
from dual
union all
select 2, 'bar'
from dual
union all
select 2, 'hehe'
from dual
union all
select 1 , 'hi'
from dual
union all
select 1 , 'yo'
from dual
union all
select 3 , 'ha'
from dual
union all
select 6 , 'gaga'
from dual
)
order by 3 desc;
select t.id, t.value
from TABLE t
inner join
(
SELECT id, count(*) as cnt
FROM TABLE
group by ID
)
x on x.id = t.id
order by x.cnt desc
How about something like
SELECT t.ID,
t.Value,
c.Cnt
FROM TABLE t INNER JOIN
(
SELECT ID,
COUNT(*) Cnt
FROM TABLE
GROUP BY ID
) c ON t.ID = c.ID
ORDER BY c.Cnt DESC
SQL Fiddle DEMO
I see the question is already answered, but since the most obvious and most simple solution is missing, I'm posting it anyway. It doesn't use self joins nor subqueries:
SQL> create table t (id,value)
2 as
3 select 1, 'hi' from dual union all
4 select 1, 'yo' from dual union all
5 select 2, 'foo' from dual union all
6 select 2, 'bar' from dual union all
7 select 2, 'hehe' from dual union all
8 select 3, 'ha' from dual union all
9 select 6, 'gaga' from dual
10 /
Table created.
SQL> select id
2 , value
3 from t
4 order by count(*) over (partition by id) desc
5 /
ID VALU
---------- ----
2 bar
2 hehe
2 foo
1 yo
1 hi
6 gaga
3 ha
7 rows selected.