Get Limited Records For Sub Query Each Result - sql

I am trying to query some data from SQL Server 2012 using sub query. I am trying to get first 3 records for each Id returned by Sub Query but I am not getting the idea how to do so for now I write this query:
Select * from Student Where TeacherId in (Select TeacherId from Teacher)
I am not sure if this is achievable by using such query or do I have to write a function or any thing else ?
Any Suggestions would be great and sorry for my bad explanation skills.

You should join the Teacher to Student table, and then use an analytic function to get the first there records for each teacher:
SELECT *
FROM
(
SELECT
s.*, t.TeacherId, t.TeacherName,
ROW_NUMBER() OVER (PARTITION BY t.TeacherId ORDER BY some_col) rn
FROM Teacher t
INNER JOIN Student s
ON t.TeacherId = s.TeacherId
) t
WHERE rn = 3;
I assume that there is a column in one of the two tables some_col which you want to use for ordering. It does not make much sense to speak of the first three records without also defining some ordering.

I guess you want the top 3 rows for each ids, not top 3 rows for entire result set
WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY t.TeacherId ORDER BY ?) Seq
FROM Student s
INNER JOIN Teacher t ON t.TeacherId = s.TeacherId
)
SELECT * FROM CTE
WHERE Seq between 1 AND 3
? placeholder requires column_name to generate the sequence_number to get the result with boundary of 1 to 3 .

Related

Two select query from two tables

I want to join two select statements into one. How do look like query to combine two results?
SELECT Name FROM students WHERE No = 4
SELECT TOP 1 pchar FROM pcharacter ORDER BY NEWID();
I tried this;
SELECT TOP 1 students.Name, pcharacter.pchar
FROM students, pcharacter
WHERE No = 4
ORDER BY NEWID();
Is this the proper way or not? I get desired result.
It appears you're attempting to associate a random row from the pcharacter table with each row from the students table WHERE No=4. As such CROSS APPLY seems appropriate
SELECT s.[Name], pc.pchar
FROM students s
cross apply (select top 1 pchar
from pcharacter
order by newid()) pc
WHERE s.[No]=4;
Query results are tables again, so you can join them just like you join stored tables. For instance:
SELECT *
FROM
(SELECT Name FROM students WHERE No = 4) query1
CROSS JOIN
(SELECT TOP 1 pchar FROM pcharacter ORDER BY NEWID()) query2
;
As to your own query: The query is quite okay. You select one student and join it with all characters and then pick one of the resulting rows randomly. It would be better of course to pick one random character first and only then join with the student. Just as you have said: Join the two queries. And as Dale K has rightly pointed out in the request comments: comma-separated joins got replaced by proper joins (INNER JOIN, LEFT OUTER JOIN etc.) in 1992. You should not use that ancient syntax anymore.
You could use a union query:
SELECT Name FROM students WHERE No = 4
UNION ALL
SELECT pchar
FROM (SELECT TOP 1 pchar FROM pcharacter ORDER BY NEWID()) t;
This assumes that both the Name and pchar columns are text, i.e. they have the same type. If not, then we might need some casting to make the above work.

How to query top record group conditional on the counts and strings in a second table

I call on the SQL Gods of the internet!! O so desperately need your help with this query, my livelyhood depends on it. I've solved it in Alteryx in like 2 minutes but i need to write this query in SQL and I am relatively new to the language in terms of complex blending and syntax.
Your help would be so appreciated!! :) xoxox I cant begin to describe
Using SSMS I need to use 2 tables 'searches' and 'events' to query...
the TOP 2 [user]s with the highest count of unique search ids in Table 'searches'
Condition that the [user]s in the list have at least 1 eventid in 'events' where [event type] starts with "great"
Here is an example of what needs to happen
search event and end result example
So the only pieces i have so far are below but boy oh boy please don't Laugh :(
What i was trying to do is..
select a table of unique users with the searchcounts from the search table
inner join selected table from 1 on userid with a table described in 3
create table of unique user ids with counts of events with [type] starting with "great"
Filter the inner joined table for the top 2 search counts from step 1
SELECT userid, COUNT() as searchcount
FROM searches
GROUP BY userid
INNER JOIN (SELECT userid, COUNT() as eventcount
FROM events WHERE LEFT(type, 5) = "great" AND eventcount>0 Group by userid)
ON searches.userid=events.userId
Obviously, this doesn't work at all!!! I think my structure is off and my method of filtering for "great" is errored. Also i dont know how to add the "top 2" clause to the search table query without affecting the inner join. This code needs to be fairly efficient so if you have a better more computationally efficient idea...I love you long time
SELECT top(2) userid, COUNT() as searchcount FROM searches
where userid in (select userid from events where left(type, 5)='great')
GROUP BY userid
order by count() desc
hope above query will serve your purpose.
I think you need exists and windows function dense_rank as follows:
Select * from
(Select u.userid, dense_rank() over (partition by u.userid order by count(*) desc) as rn
From users u join searches s on u.userid = s.userid
Where exists
(select 1 from events e
Where e.userid = u.userid And LEFT(e.type, 5) = 'great')
Group by u.userid ) t Where rn <= 2

Inner join of table a and table b with selecting only one row of multiple in table b ( row with colum n = max value )

i am a total beginner in SQL. So i have two tables ( table a and table b )
table a holds people with unique IDs, table b holds multiple rows for each person of table a and also the persons ID ( for a possible join ) . the rows in table b are sorted by the columnn row_number.
How can i select all people but only the row of table b with the highest row_number ?
i hope you could somewhat understand me.
Cheers
If i got you right:
SELECT a.persons_ID
,b.rn
FROM A
INNER JOIN
(
SELECT MAX(row_number_column) AS rn
,persons_ID
FROM B
GROUP BY persons_ID
) sub
ON sub.persons_ID = A.persons_ID
The subselect in the inner joins groups your data of table B. So there will be just one row for each persons_ID - the row with the highest row_number_column.
Finally just a simple join on persons_ID.
If you don't need any other information than the person ID and the last row_number per person, then it is quite trivial. Let's call the first table person and the second visit:
select person_id,
max(row_number) max_row_number
from visit
group by person_id
If you need some other information from the first table, like person.name, then perform the join:
select person.person_id,
person.name,
max(visit.row_number) max_row_number
from person
inner join visit on visit.person_id = person.person_id
group by person.person_id,
person.name
If you need some other information from the second table, like visit.present, then modern databases support the row_number() window function (not to be confused with the column that you have):
select name,
base.row_number,
present
from (
select person.name,
row_number() over (partition by visit.person_id
order by visit.row_number desc) rn,
visit.row_number,
visit.present
from person
inner join visit on visit.person_id = person.person_id
) base
where rn = 1
NB: I would strongly advise to rename the column row_number to some other name, as row_number is an analytic function in many databases.

Returning the Min() of a Count()

I am studying for an SQL test and the previous year has the final question:
Name the student who has studied the least number of papers. How many
papers have they studied?
So far, this is the select query that I have created:
select min(Full_Name), min(Amount)
from (select st.ST_F_Name & ' ' & st.ST_L_Name as Full_Name, count(*) as Amount
from (student_course as sc
inner join students as st
on st.ST_ID=sc.SC_ST_ID)
group by st.ST_F_Name & ' ' & st.ST_L_Name)
This works perfectly for returning the result I want but I'm not sure if this is the way I should be doing this query? I feel like calling min() on the Full_Name could potentially backfire on me under certain circumstances. Is there a better way to be doing this? (this is in MS Access for unknown reasons)
If you want only 1 of such students if there are multiple, this is probably the simplest:
select st.ST_F_Name, st.ST_L_Name, count(*) as Amount
from student_course as sc
inner join students as st
on st.ST_ID=sc.SC_ST_ID
group by st.ST_ID
order by Amount ASC LIMIT 1
However, if you want to find all stuch students, you follow a different approach. We use a WITH clause to simplify things, that defines a CTE (Common Table Expression) computing the number of courses per-student. And then we select students where their number equals to the minimum in that CTE:
with per_student as (
select st.ST_F_Name, st.ST_L_Name, count(*) as Amount
from student_course as sc
inner join students as st
on st.ST_ID=sc.SC_ST_ID
group by st.ST_ID
)
select * from per_student
where amount = (select min(amount) from per_student)
But the real trick in that question is that there might be students that didn't take ANY courses. But with approaches presented so far you'll never see them. You want something like this:
with per_student as (
select st.ST_F_Name, st.ST_L_Name, count(sc.SC_ST_ID) as Amount
from student_course as sc
right outer join students as st
on st.ST_ID=sc.SC_ST_ID
group by st.ST_ID
)
select * from per_student
where amount = (select min(amount) from per_student)
You can order by count(*) to get the student with the least # of papers:
i.e.
select * from students where st_id in (
select top 1 sc_st_id
from student_course
group by sc_st_id
order by count(*)
)
if you also need the # of papers studied, then join a derived table containing the min count:
select * from students s
left join (
select top 1 sc_st_id, count(*)
from student_course
group by sc_st_id
order by count(*)
) t on t.sc_st_id = s.st_id

How to get order by with inner query

I am new to sql, can any one please help me with getting ordered data from the internal query
select
*
from emp
where id
(select order from department where name = 'testing' order by order asc).
I am getting order data from the inner query and with the id's I get I should get the result from the emp table to be in the same order of inner query. Can anyone help me with this. TIA.
If it's reasonable to re-write your query as a join:
select e.*
from emp e
inner join department d
on e.id = d.order
where d.name = 'testing'
order by d.order asc
Where this would change your results is if there are multiple rows in department with the same order value matching one or more rows in emp - in which case this query will return multiple rows, whereas the original would not. But if such a condition doesn't apply, then this is the query I'd recommend.
Damien's answer is quite cool and perfect. but if you still want to go with subquery then try this
select *
from emp
where id in (select order from department where name = 'testing' order by order asc)
order by id asc
May this help you
This will give the right number of rows in case there more than 1 match between emp.id and department.order
select * from emp e
where exists
(select 1 from department d where d.name = 'testing'
and e.id = d.order1) -- order is a reserved word in several sql languages
order by id
It seems there is something funny going on between your tables. Would would department contain any information about emp(I assume it is employee table) ?
There is no guarantee that there is an actual temporary table with the inner query and that it is sorted and processed in a certain way. However, you can sort the outer query by emp.id.
select * from emp where id in
(select order from department where name = 'testing')
order by id asc
select e.* from emp as e, department d where e.id=d.order and d.name='testing' order by d.order