How to show which students are still in school using sql - sql

This table shows the records of students entering and leaving the school. IN represents student entering school and OUT represents student leaving school. I wondering how to show which students are still in school.
I'm trying so much but still cannot figure it out, does anyone can help me, Thank you so much.
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL auto_increment,
`time` varchar(128) default NULL,
`status` varchar(128) default NULL,
`stu_id` varchar(128) default NULL,
PRIMARY KEY (`id`)
)
INSERT INTO `student` (`id`, `time`, `status`, `stu_id`) VALUES
(1,'11AM','IN','1'),
(2,'11AM','IN','2'),
(3,'12AM','OUT','1'),
(4,'12AM','IN','3'),
(5,'1PM','OUT','3'),
(6,'2PM','IN','3'),
(11,'2PM','IN','4');
I expect the answer is 2, 3, 4

The number of students in the school is the sum of the ins minus the sum of the outs:
select sum(case when status = 'in' then 1
when status = 'out' then -1
else 0
end)
from student;
Basically to see the students who are in the school, you want the students whose last status is in. One way uses a correlated subquery:
select s.stu_id
from student s
where s.time = (select max(s2.time)
from student s2
where s2.stu_id = s.stu_id
) and
s.status = 'in';

If status is either only IN or OUT can't you do
SELECT * from student WHERE status="IN"

here's the query considering the auto increment id
select t2.* from
student t2
left join (select ROW_NUMBER() OVER(PARTITION by stu_id ORDER BY id desc) as row_num, id from student) t1 on t1.id = t2.id
where t1.row_num = 1 and [status] = 'IN'

Related

Accessing to total number in each second level(Postgres Hierarchical Query Practice)

I was practicing on Postgres and stuck on a point that I couldn't find a way to achieve. I have a simple database which are the attributes:
CREATE TABLE public.department
(
"deptId" integer NOT NULL PRIMARY KEY,
name character varying(30) COLLATE pg_catalog."default" NOT NULL,
"parentId" integer,
"numEmpl" integer NOT NULL,
CONSTRAINT "department_parentId_fkey" FOREIGN KEY ("parentId")
REFERENCES public.department ("deptId") MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
and then I have some data in the table. Short example is
insert into department values (1, 'Headquarter', 1, 10);
insert into department values (2, 'Sales', 1, 15);
insert into department values (3, 'Logistics', 1, 25);
...
I was trying to get the total number of people who are employeed in each second level department.
I am able to get the total number of employeed people in each department but according to my search in the internet this is possible with "Hierarchical Queries". Currently, I am using
parentId=1
while querying.
Any solutions for this? Thank you.
Here is one option:
with recursive cte as (
select deptid as rootid, deptid from department where parentid = 1 and deptid <> 1
union all
select c.rootid, d.deptid
from cte c
inner join department d on d.parentid = c.deptid and d.deptid <> 1
)
select rootid, count(*) cnt from cte group by rootid

SQL- How to select data from two different table?

I am working to select data from 2 different table but I can't figured out. If I use INNER JOIN it show noting. Any help are welcome and Thanks.
My First table:
CREATE TABLE P_N(
PN_ID int NOT NULL,
PN VARCHAR (1000),
primary key (PN_ID)
);
My second Table:
CREATE TABLE NAME (
NAME_ID VARCHAR(60) PRIMARY key,
NAME VARCHAR (40)
);
My select code :
SELECT DISTINCT NAME.NAME_ID, PN.PN_ID
FROM NAME
FULL JOIN P_N
ON PN.PN =NAME.NAME_ID;
If I use left or full Join this is the result:
NAME_ID PN_ID
nm0006300 NULL
nm0006400 NULL
nm0006500 NULL
nm0006600 NULL
nm0006700 NULL
AND if I use right join:
NAME_ID PN_ID
null 921691
null 921692
null 921693
null 921694
This is what I want the result to looks like For example:
NAME_ID PN_ID
nm0006300 921691
nm0006400 921692
nm0006500 921693
nm0006600 921694
You don't seem to have a JOIN key. You can add one with ROW_NUMBER():
SELECT n.NAME_ID, PN.PN_ID
FROM (SELECT n.*, ROW_NUMBER() OVER (ORDER BY NAME_ID) as seqnum
FROM NAME n
) n JOIN
(SELECT pn.*, ROW_NUMBER() OVER (ORDER BY PN) as seqnum
FROM P_N pn
) pn
ON PN.seqnum = n.seqnum;
try this
select DISTINCT NAME.NAME_ID, PN.PN_ID
from NAME,P_N as PN
where PN.PN =NAME.NAME_ID

Problem in querying specific data based on values present in different rows of the same column

I am creating a database of a college and the tables are given as:
create table depts(
deptcode char(3) primary key,
deptname char(70) not null);
create table students(
rollno number(2) primary key,
name char(50),
bdate date check(bdate < TO_DATE('2004-01-01','YYYY-MM-DD')),
deptcode char(3) references depts(deptcode)
on delete cascade,
hostel number check(hostel<20),
parent_inc number(8,1));
create table faculty(
fac_code char(2) primary key,
fac_name char(50) not null,
fac_dept char(3) references depts(deptcode)
on delete cascade);
//for courses offered by the college
create table crs_offrd(
crs_code char(5) primary key,
crs_name char(35) not null,
crs_credits number(2,1),
crs_fac_cd char(2) references faculty(fac_code)
on delete cascade);
// for course registered by students*
create table crs_regd(
crs_rollno number(2) references students(rollno),
crs_cd char(5) references crs_offrd(crs_code)
on delete cascade,
marks number(5,2),
primary key(crs_rollno,crs_cd));
I am trying to find out name , subject and marks of students who have marks more than rollno 92005102 for course CS103 and CS106.
I believe the table should look like this:
Name Subject Marks
XYZ CS103 92
XYZ CS106 95
I am confused how to check for marks in both CS103 and CS106 at the same time as the marks for two subjects are present in the same column in a different row, and query processes one row at a time.
I followed the question posted here but it works only for a column and it would list values separated by , but I need to get the corresponding subject name in which that marks was obtained.
If any other information required, please comment.
There are probably many ways to achieve this. One is this:
select
crs_rollno,
max(case when crs_cd = 'CS103' then marks end) as marks103,
max(case when crs_cd = 'CS106' then marks end) as marks106
from crs_regd cr
where crs_cd in ('CS103', 'CS106')
and marks >
(
select marks
from crs_regd cr92005102
where cr92005102.crs_rollno = 92005102 -- student 92005102
and cr92005102.crs_cd = cr.crs_cd -- same class
)
group by crs_rollno
having count(*) = 2 /* both courses better than 92005102 */ ;
You can join to the students table to get their name.
Just for the fun of it another approach:
with cs103 as (select * from crs_regd where crs_cd = 'CS103')
, cs106 as (select * from crs_regd where crs_cd = 'CS106')
select crs_rollno, cs103.marks as cs103_marks, cs106.marks as cs106_marks
from cs103 join cs106 using (crs_rollno)
where cs103.marks > (select marks from cs103 where crs_rollno = 92005102)
and cs106.marks > (select marks from cs106 where crs_rollno = 92005102);
You can use group by with having as following:
-- CTE IS USED TO FETCH THE REQUIRED DATA FROM TABLE USING JOINS
WITH CTE AS (
SELECT
S.NAME,
CO.CRS_NAME,
CO.CRS_CODE,
CR.MARKS,
S.ROLLNO
FROM
STUDENTS S
JOIN CRS_REGD CR ON ( S.ROLLNO = CR.CRS_ROLLNO )
JOIN CRS_OFFRD CO ON ( CR.CRS_CD = CO.CRS_CODE )
WHERE
CO.CRS_NAME IN (
'CS103',
'CS106'
)
)
-- ACTUAL LOGIC START FROM HERE
SELECT
S.NAME,
CO.CRS_NAME,
CR.MARKS
FROM
CTE C3
WHERE
C3.ROLLNO IN (
SELECT
C2.ROLLNO
FROM
CTE C1
JOIN CTE C2 ON ( C1.ROLLNO = 92005102
AND C1.ROLLNO <> C2.ROLLNO
AND C1.CRS_CODE = C2.CRS_CODE )
GROUP BY
C2.ROLLNO
HAVING ( MAX(CASE
WHEN C1.CRS_NAME = 'CS103'
AND C2.MARKS >= C1.MARKS THEN 1
END) = 1
AND MAX(CASE
WHEN C1.CRS_NAME = 'CS106'
AND C2.MARKS >= C1.MARKS THEN 1
END) = 1 )
);
Cheers!!

Querying across multiple tables avoiding a union all

I have the following DB tables that I am trying to query:
t_shared_users
user_id
user_category
folder_id
expiry
t_documents
id
folder_id
user_id
user_category
description
created
updated
t_folder
id
type
user_id
user_category
created
updated
I would like to find all the documents you own and have shared access to. ie. search for all documents in t_documents where user_id = 1 AND user_category = 100 but also include those documents in the folder you have access to in t_shared_users. Here is my attempt at the query:
SELECT
id,
folder_id,
user_id,
user_category,
description,
created,
updated
FROM
t_documents
WHERE
user_category = 100
AND user_id = 1
UNION ALL
SELECT
d.id,
d.folder_id,
d.user_id,
d.user_category,
d.description,
d.created,
d.updated
FROM
t_documents d
JOIN
t_shared_users s
ON
d.folder_id = s.folder_id
WHERE
d.user_category = 100
d.AND user_id = 1
ORDER BY
created ASC
LIMIT
10
Is there any better/more performant/concise way to write this query? The above seems a little verbose and slow.
edit:
CREATE TABLE t_folder (
id SERIAL NOT NULL,
user_category SMALLINT NOT NULL,
user_id INTEGER NOT NULL,
type INTEGER NOT NULL,
description TEXT,
created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
PRIMARY KEY (id)
);
CREATE TABLE t_documents (
id BIGSERIAL NOT NULL,
folder_id INTEGER,
user_category SMALLINT NOT NULL,
user_id INTEGER NOT NULL,
description TEXT NOT NULL,
created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
PRIMARY KEY (id)
);
CREATE TABLE t_shared_users (
id SERIAL,
folder_id INTEGER NOT NULL,
user_category INTEGER NOT NULL,
user_id INTEGER NOT NULL,
expiry TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
PRIMARY KEY (id)
);
This is your query:
SELECT
id,
folder_id,
user_id,
user_category,
description,
created,
updated
FROM
t_documents
WHERE
user_category = 100
AND user_id = 1
UNION ALL
SELECT
d.id,
d.folder_id,
d.user_id,
d.user_category,
d.description,
d.created,
d.updated
FROM
t_documents d
JOIN
t_shared_users s
ON
d.folder_id = s.folder_id
WHERE
d.user_category = 100
AND d.user_id = 1 -- your query actually has a typo here
What I don't understand about the above query is why you are filtering on d.user_category and d.user_id (t_documents table) in the bottom part of the query. Are you sure you didn't mean s.user_category and s.user_id (t_shared_users)? If not, what is the point of joining with t_shared_users?
Assuming that I am correct that your query is in error, this is how I would rewrite it:
select d.*
from t_documents d
where d.user_category = 100
and d.user_id = 1
union
select d.*
from t_shared_users s
join t_documents d
on d.folder_id = s.folder_id
where s.user_category = 100
and s.user_id = 1
Notice that I use union instead of union all, as I believe it's technically possible to get possibly unwanted duplicate documents otherwise.
Also, just as a rough approximation, these are the indexes I would define for good performance:
t_documents (user_id, user_category)
t_documents (folder_id)
t_shared_users (user_id, user_category, folder_id)
Starting from the query, you have given, I would replace join with left join
select
d.id,
d.folder_id,
d.user_id,
d.user_category,
d.description,
d.created,
d.updated
from t_documents d
left join t_shared_users s on d.folder_id = s.folder_id
where (d.user_category = 100 and d.user_id = 1)
or (s.user_category = 100 and s.user_id = 1)
This would give you all entries from t_documents with user_id = 1 and user_category = 100, and also all entries with the same where clause, where you have access to shared documents.

Inner join and possibly a cte? Inner join and partial match in same row

I currently match #teacher to #coursesCSV using TeacherId, So with the INNER JOIN there's a one-one and I get one row. Once I get this match, I need to display the possible #coursesCsv.IsExpired for that particular TeacherId in that same row. So I match the first 3 chars and the last 4 chars, but ignore the 3 chars in the middle. With this criteria, there would only be two matches, and that's why the result displays 'OK/NOK'. The maximum number of matches here will be 2.
So the result should look like the following:
teacherid isexpired WhatMatched
ABC-001-1225 OK OK/NOK
If that's too difficult, another possible result would be a count:
teacherid isexpired WhatMatched
ABC-001-1225 OK 2
I've been trying get a '2' for WhatMatched but I keep getting 3. And I'mt stuck there. The important thing is that the result can only consist of 1 row.
The reason I'm doing this is that we have a grid that populates using #teacher.TeacherId inner join #coursesCSV, and this row is evaluated and approved by a user. In this case, he will naturally see 1 row: ABC-001-1225 and OK. The website will not let him approve because there's a NOK (ABC-002-1225). I'm adding this so that he knows he needs to check something instead of having to ask me why he can't approve since it says OK.
This is the query:
IF OBJECT_ID('tempdb..#teacher') IS NOT NULL DROP TABLE #teacher
IF OBJECT_ID('tempdb..#coursesCsv') IS NOT NULL DROP TABLE #coursesCsv
create table #teacher
(
TeacherID varchar(20),
FullName varchar(30),
DeptId int
)
insert into #teacher select 'ABC-001-1225', 'Roy Brown', 3
create table #coursesCsv
(
IsExpired varchar(3),
TeacherID varchar(20),
DeptId int
)
insert into #coursesCsv select 'OK', 'ABC-001-1225', 3
insert into #coursesCsv select 'NOK', 'ABC-002-1225', 3
insert into #coursesCsv select 'OK', 'XYZ-002-1225', 3
select t.teacherid, c.isexpired, c.coursecnt, c.prefix
from #teacher t
inner join
(
select
teacherid,
left(teacherid, 3) as 'Prefix',
isexpired,
count(*)
over (partition by right(teacherid,4)) as coursecnt
from #coursesCsv
) as c
on t.teacherid = c.teacherid
and left(t.teacherid, 3) = left(c.teacherid, 3)
I may not understand this 100% .... but I think you need to partition by the first 3 and last 4 characters of teacherid. So...
select t.teacherid, c.isexpired, c.coursecnt, c.prefix
from #teacher t
inner join
(
select
teacherid,
left(teacherid, 3) as 'Prefix',
isexpired,
count(*)
over (partition by left(teacherid, 3), right(teacherid,4)) as coursecnt
from #coursesCsv
) as c
on t.teacherid = c.teacherid
and left(t.teacherid, 3) = left(c.teacherid, 3)