How do you join tables sharing the same column? - sql

I made an SQL Fiddle and what I would like to do is join these two queries by using the departmentid.
What I would like to show is the departmentname and not_approved_manager.
Would it be best to use a union or join in this case?
Tables
create table cserepux
(
status int,
comment varchar(25),
departmentid int,
approveddate datetime
);
insert into cserepux (status, comment, departmentid, approveddate)
values (1, 'testing1', 1, NULL), (1, 'testing2', 1, NULL),
(1, 'testing2', 2, NULL), (0, 'testing2', 1, NULL),
(0, 'tesitng2', 1, NULL), (0, 'testing2', 1, NULL),
(0, 'tesitng2', 1, NULL), (0, 'testing3', 2, NULL),
(0, 'testing3', 3, NULL);
create table cseDept
(
departmentid int,
department_name varchar(25)
);
insert into cseDept (departmentid,department_name)
values (1, 'department one'), (2, 'department two'),
(3, 'department three'), (4, 'department four');
Query
select
departmentid,
COUNT(*) AS 'not_approved_manager'
from
cserepux
where
approveddate is null
group by
departmentid
SELECT * FROM cseDept

You need to do a join. A union will not get you what you want.
select d.department_name, COUNT(*) AS 'not_approved_manager'
from cserepux c
inner join cseDept d on c.departmentid = d.departmentid
where approveddate is null
group by d.department_name

Do you need just a join and a correct group by
select dep.department_name, COUNT(*) AS 'not_approved_manager'
from cseDept dep
join cserepux cs on cs.departmentid = dep.departmentid
where approveddate is null
group by dep.department_name
Fiddle: http://sqlfiddle.com/#!3/5cf4e/30
Since joins and group by are really basic things in SQL I can suggest you do take a look on some tutorials to get a bit more proficiency whit it. You can try SQL Server Central stairway articles series

Related

SQL Pivot Half of table

I have a table that consists of time information. It's basically:
Employee, Date, Seq, Time In, Time Out.
They can clock out multiple times a day, so I'm trying to get all of the clock outs in a day on one row. My result would be something like:
Employee, Date, TimeIn1, TimeOut1, TimeIn2, TimeOut2, TimeIn3, TimeOut3....
Where the 1, 2, and 3 are the sequence numbers. I know I could just do a bunch of left joins to the table itself based on employee=employee, date=date, and seq=seq+1, but is there a way to do it in a pivot? I don't want to pivot the employee and date fields, just the time in and time out.
The short answer is: Yes, it's possible.
The exact code will be updated if/when you provide sample data to clarify some points, but you can absolutely pivot the times out while leaving the employee/work date alone.
Sorry for the wall of code; none of the fiddle sites are working from my current computer
declare #test table (
pk int,
workdate date,
seq int,
tIN time,
tOUT time
)
insert into #test values
(1, '2020-11-25', 1, '08:00', null),
(1, '2020-11-25', 2, null, '11:00'),
(1, '2020-11-25', 3, '11:32', null),
(1, '2020-11-25', 4, null, '17:00'),
(2, '2020-11-25', 5, '08:00', null),
(2, '2020-11-25', 6, null, '09:00'),
(2, '2020-11-25', 7, '09:15', null),
-- new date
(1, '2020-11-27', 8, '08:00', null),
(1, '2020-11-27', 9, null, '08:22'),
(1, '2020-11-27', 10, '09:14', null),
(1, '2020-11-27', 11, null, '12:08'),
(1, '2020-11-27', 12, '01:08', null),
(1, '2020-11-27', 13, null, '14:40'),
(1, '2020-11-27', 14, '14:55', null),
(1, '2020-11-27', 15, null, '17:00')
select *
from (
/* this just sets the column header names and condenses their values */
select
pk,
workdate,
colName = case when tin is not null then 'TimeIn' + cast(empDaySEQ as varchar) else 'TimeOut' + cast(empDaySEQ as varchar) end,
colValue = coalesce(tin, tout)
from (
/* main query */
select
pk,
workdate,
/* grab what pair # this clock in or out is; reset by employee & date */
empDaySEQ = (row_number() over (partition by pk, workdate order by seq) / 2) + (row_number() over (partition by pk, workdate order by seq) % 2),
tin,
tout
from #test
) i
) a
PIVOT (
max(colValue)
for colName
IN ( /* replace w/ dynamic if you don't know upper boundary of max in/out pairs */
[TimeIn1],
[TimeOut1],
[TimeIn2],
[TimeOut2],
[TimeIn3],
[TimeOut3],
[TimeIn4],
[TimeOut4]
)
) mypivotTable
generates these results.
(I would provide a fiddle demo but they're not working for me today)

find the common Class ID for a list of students

I try to get the class id from student and class_student tables using jpql.
I give a list of students of students like this ('name1', 'name2', 'name5'), then if this list was assign to class, then I will have the class ID (class id = 2).
Note: the list ist unique, that means this list is only in one class.
CREATE DATABASE test;
USE test;
CREATE TABLE CLASS
( ID BIGINT NOT NULL PRIMARY KEY,
NAME VARCHAR(255) NOT NULL
) CHARSET = utf8mb4;
CREATE TABLE STUDENT
( ID BIGINT NOT NULL PRIMARY KEY,
NAME VARCHAR(255) NOT NULL,
constraint UK_NAME
unique (NAME)
)CHARSET = utf8mb4;
CREATE TABLE CLASS_STUDENT
(ID BIGINT NOT NULL PRIMARY KEY,
CLASS_ID BIGINT NOT NULL,
STUDENT_ID BIGINT NOT NULL,
CONSTRAINT FK_STUDENT
FOREIGN KEY (STUDENT_ID) REFERENCES STUDENT (ID),
CONSTRAINT FK_CLASS
FOREIGN KEY (CLASS_ID) REFERENCES CLASS (ID)
)CHARSET = utf8mb4;
INSERT INTO CLASS (ID, name)
VALUES (1, 'class1'),
(2, 'class2'),
(3, 'class3');
INSERT INTO STUDENT (ID, name)
VALUES (1, 'name1'),
(2, 'name2'),
(3, 'name3'),
(4, 'name4'),
(5, 'name5');
INSERT INTO CLASS_STUDENT (ID, CLASS_ID, STUDENT_ID)
VALUES (1, 1, 1),
(2, 1, 2),
(3, 1, 3),
(4, 1, 4),
(5, 2, 1),
(6, 2, 3),
(7, 2, 5),
(8, 3, 1),
(9, 3, 3),
(10, 3, 4),
(11, 3, 5);
This is a relational division problem. You can use aggregation and a having clause for filtering.
select cs.class_id
from class_student cs
inner join student s on s.id = cs.student_id
where s.name in ('name1', 'name2', 'name3')
group by cs.class_id
having count(*) = 3
The join brings in the students of each class, and the where clause filters on the three students we are interested in. Then, we group by class, and retain groups that contain three rows (one per student).
Edit:
If you want classes that have these three students and no other, then:
select cs.class_id
from class_student cs
inner join student s on s.id = cs.student_id
where s.name in ('name1', 'name2', 'name3')
group by cs.class_id
having sum(case when s.name in ('name1', 'name2', 'name3') then 1 else 0 end) = 3
and sum(case when s.name not in ('name1', 'name2', 'name3') then 1 else 0 end) = 0

Select duplicate persons with duplicate memberships

SQL Fiddle with schema and my intial attempt.
CREATE TABLE person
([firstname] varchar(10), [surname] varchar(10), [dob] date, [personid] int);
INSERT INTO person
([firstname], [surname], [dob] ,[personid])
VALUES
('Alice', 'AA', '1/1/1990', 1),
('Alice', 'AA', '1/1/1990', 2),
('Bob' , 'BB', '1/1/1990', 3),
('Carol', 'CC', '1/1/1990', 4),
('Alice', 'AA', '1/1/1990', 5),
('Kate' , 'KK', '1/1/1990', 6),
('Kate' , 'KK', '1/1/1990', 7)
;
CREATE TABLE person_membership
([personid] int, [personstatus] varchar(1), [memberid] int);
INSERT INTO person_membership
([personid], [personstatus], [memberid])
VALUES
(1, 'A', 10),
(2, 'A', 20),
(3, 'A', 30),
(3, 'A', 40),
(4, 'A', 50),
(4, 'A', 60),
(5, 'T', 70),
(6, 'A', 80),
(7, 'A', 90);
CREATE TABLE membership
([membershipid] int, [memstatus] varchar(1));
INSERT INTO membership
([membershipid], [memstatus])
VALUES
(10, 'A'),
(20, 'A'),
(30, 'A'),
(40, 'A'),
(50, 'T'),
(60, 'A'),
(70, 'A'),
(80, 'A'),
(90, 'T');
There are three tables (as per the fiddle above). Person table contains duplicates, same people entered more than once, for the purpose of this exercise we assume that a combination of the first name, surname and DoB is enough to uniquely identify a person.
I am trying to build a query which will show duplicates of people (first name+surname+Dob) with two or more active entries in the Person table (person_membership.person_status=A) AND two or more active memberships (membership.mestatus=A).
Using the example from SQL Fiddle, the result of the query should be just Alice (two active person IDs, two active membership IDs).
I think I'm making progress with the following effort but it looks rather cumbersome and I need to remove Katie from the final result - she doesn't have a duplicate membership.
SELECT q.firstname, q.surname, q.dob, p1.personid, m.membershipid
FROM
(SELECT
p.firstname,p.surname,p.dob, count(*) as cnt
FROM
person p
GROUP BY
p.firstname,p.surname,p.dob
HAVING COUNT(1) > 1) as q
INNER JOIN person p1 ON q.firstname=p1.firstname AND q.surname=p1.surname AND q.dob=p1.dob
INNER JOIN person_membership pm ON p1.personid=pm.personid
INNER JOIN membership m ON pm.memberid = m.membershipid
WHERE pm.personstatus = 'A' AND m.memstatus = 'A'
Since you are using SQL Server windows function will be handy for this scenario. The following will give you the expected output.
SELECT firstname,surname,dob,personid,memberid
from(
SELECT firstname,surname,dob,p.personid,memberid
,Rank() over(partition by p.firstname,p.surname,p.dob order by p.personid) rnasc
,Rank() over(partition by p.firstname,p.surname,p.dob order by p.personid desc) rndesc
FROM [StagingGRG].[dbo].[person] p
INNER JOIN person_membership pm ON p.personid=pm.personid
INNER JOIN membership m ON pm.memberid = m.membershipid
where personstatus='A' and memstatus='A')a
where a.rnasc+rndesc>2
You have to add Group by and Having clause to return duplicate items only-
SELECT
person.firstname,person.surname,person.dob
FROM
person, person_membership, membership
WHERE
person.personid=person_membership.personid AND person_membership.memberid = membership.membershipid
AND
person_membership.personstatus = 'A' AND membership.memstatus = 'A'
GROUP BY
person.firstname,person.surname,person.dob
HAVING COUNT(1) > 1

COUNT of different tables in GROUP

I have a table that holds a list of classes available at a school. Each class can have a number of sessions. And each class can have pupils assigned to it.
What I need to do is get a count of all sessions for each class, as well as the number of students attending the class. I have done the first bit, but if I join to the pupil allocation table, my counts will be wrong.
I have conjured up some fake SQL that you can use.
I'm stuck with efficiently getting a count from the Pupil link table.
DECLARE #Class TABLE
(
ClassID INT NOT NULL,
ClassName VARCHAR(20) NOT NULL
)
INSERT INTO #Class VALUES (1, 'English')
INSERT INTO #Class VALUES (2, 'Maths')
DECLARE #ClassSession TABLE
(
ClassSessionID INT NOT NULL,
ClassID INT NOT NULL,
Description VARCHAR(100) NOT NULL
)
INSERT INTO #ClassSession VALUES (1, 1, 'Basic English')
INSERT INTO #ClassSession VALUES (2, 1, 'Advanced English')
INSERT INTO #ClassSession VALUES (3, 1, 'Amazing English')
INSERT INTO #ClassSession VALUES (4, 2, 'Basic English')
INSERT INTO #ClassSession VALUES (5, 2, 'Basic English')
DECLARE #ClassPupil TABLE
(
ClassPupilID INT NOT NULL,
ClassID INT NOT NULL,
PupilID INT NOT NULL -- FK to the Pupils table.
)
INSERT INTO #ClassPupil VALUES (1, 1, 1000)
INSERT INTO #ClassPupil VALUES (2, 1, 1001)
INSERT INTO #ClassPupil VALUES (3, 1, 1002)
INSERT INTO #ClassPupil VALUES (4, 1, 1003)
INSERT INTO #ClassPupil VALUES (5, 1, 1004)
INSERT INTO #ClassPupil VALUES (6, 2, 1005)
INSERT INTO #ClassPupil VALUES (7, 2, 1006)
INSERT INTO #ClassPupil VALUES (8, 2, 1007)
SELECT ClassName, COUNT(*) AS Sessions, '??' AS NumerOfPupils
FROM #Class c
INNER JOIN #ClassSession cs
ON cs.ClassID = c.ClassID
GROUP BY c.ClassID, c.ClassName
It can maybe be done with a sub query? Is that the best way?
You have two independent dimensions for each class. You need to aggregat them separately:
SELECT c.ClassName, cs.Sessions, cp.Pupils
FROM #Class c INNER JOIN
(SELECT ClassId, COUNT(*) as sessions
FROM #ClassSession cs
GROUP BY ClassId
) cs
ON cs.ClassID = c.ClassID INNER JOIN
(SELECT ClassId, COUNT(*) as pupils
FROM #ClassPupil cp
GROUP BY ClassId
) cp
ON cp.ClassId = c.ClassId;
Another method is to use CROSS APPLY to get the count of pupils:
SELECT
ClassName, COUNT(*) AS Sessions, cp.NumberOfPupils
FROM #Class c
INNER JOIN #ClassSession cs
ON cs.ClassID = c.ClassID
CROSS APPLY (
SELECT COUNT(*) AS NumberOfPupils
FROM #ClassPupil
WHERE
ClassID = c.ClassID
) cp
GROUP BY c.ClassID, c.ClassName, cp.NumberOfPupils
SELECT ClassName, COUNT(distinct cs.ClassSessionID) AS Sessions, /*'??'*/ count( distinct cp.PupilID) AS NumerOfPupils
FROM #Class c
INNER JOIN #ClassSession cs
ON cs.ClassID = c.ClassID
inner join #ClassPupil cp on c.ClassID=cp.ClassID
GROUP BY /*c.ClassID,*/ c.ClassName
count(distinct...) solves (works around) the problem.
Generally, this is (A -> B, C) issue.

SQL IF condition from other tables

I'm new here in the site and I need a help from you guys. Below is the schema i have which can be run in this site http://sqlfiddle.com/#!3/134c3. The name of my database is vehicle inspections. My question is after this schema.
CREATE TABLE Car
([CarID] varchar(36),
[PlateNo] varchar(6),
[Package] int);
INSERT INTO Car([CarID], [PlateNo], [Package])
VALUES('A57D4151-BD49-4B44-AF10-000F1C298E05', '8112AG', 4);
CREATE TABLE Event
([EventID] int,
[CarID] varchar(36),
[EventTime] smalldatetime,
TicketStatus varchar (10)) ;
INSERT INTO Event([EventID], [CarID], [EventTime], TicketStatus)
VALUES (1, 'A57D4151-BD49-4B44-AF10-000F1C298E05', '20130701', 'Open'),
(2, 'A57D4151-BD49-4B44-AF10-000F1C298E05', '20130702', 'Close') ;
CREATE TABLE EventDefects
([EventDefectsID] int,
[EventID] int,
[Status] varchar(15),
[DefectID] int) ;
INSERT INTO EventDefects ([EventDefectsID], [EventID], [Status], [DefectID])
VALUES (1, 1, 'YES', 1),
(2, 1, 'NO', 2),
(3, 1, 'N/A', 3),
(4, 1, 'N/A', 4),
(5, 2, 'N/A', 1),
(6, 2, 'N/A', 2),
(7, 2, 'N/A', 5),
(8, 2, 'YES', 3),
(9, 2, 'NO', 4) ;
CREATE TABLE Defects
([DefectID] int,
[DefectsName] varchar (36),
[DefectClassID] int) ;
INSERT INTO Defects ([DefectID], [DefectsName], [DefectClassID])
VALUES (1, 'TYRE', 1),
(2, 'BRAKING SYSTEM', 1),
(3, 'OVER SPEEDING', 3),
(4, 'NOT WEARING SEATBELTS', 3),
(5, 'MIRRORS AND WINDSCREEN', 2) ;
CREATE TABLE DefectClass
([Description] varchar (15),
[DefectClassID] int) ;
INSERT INTO DefectClass ([DefectClassID], [Description])
VALUES (1, 'CATEGORY A'),
(2, 'CATEGORY B'),
(3, 'CATEGORY C')
To clarify things. There are two conditions when we issue ticket to the driver.
When vehicle is inspected and found defects on any items under Class A or B (tick 'yes'). The ticket status of that is OPEN. On the other hand if all items on Class A and B are tick 'No' it means no defects are found. The ticket Status is CLOSE. Lastly items under Class C or (traffic violations) are tick N/A. Meaning its a mere vehicle inspection
Condition No. 2 is where vehicle is stopped because of traffic violation (ex. Over Speeding). Vehicle will NOT be inspected, The distinction of this issued ticket are all items under Class A and B are tick or mark 'N/A' while on Class C is tick either 'yes' or 'no'.
Now I have this SQL code below that can be use in the schema above where it will extract vehicles on its MAX(EventTime) with corresponding Ticket Status.
Select
PlateNo, TicketStatus, [EventTime]
FROM
(SELECT
ROW_NUMBER() OVER (PARTITION BY Event.CarID ORDER BY [EventTime] DESC) AS [index],
Event.CarID,
TicketStatus,
[EventTime],
plateNo
FROM
[Event]
Join
[Car] ON Event.CarID = Car.CarID) A
WHERE [index] = 1
Result:
RESULT: PlateNo - 8112AG ; EventTime - July 2, 2013; TicketStatus - Close.
THIS IS NOT THE CORRECT since on this particular date there were no inspection at all only the driver was caught for OVER SPEEDING (see the schema above) and items under Class A and B are marked N/A.
The correct result should be one step back which is July 1, 2013 and Ticket Status is OPEN since it was a clear inspection. Items under category A and B are inspected and found TIRES are defective and BRAKING SYSTEM has NO defects.
Somehow I was thinking code where if Event.TicketStatus = CLOSE it will examine if it is close because it was inspected or close because its a traffic violation.
Try this.
SELECT
PlateNo,
TicketStatus,
MAX(EventTime)
FROM
[Event] E
LEFT OUTER JOIN
[EventDefects] ED ON E.EventID = ED.EventID
LEFT OUTER JOIN
[Defects] D ON ED.DefectID = D.DefectID
LEFT OUTER JOIN
[Car] C ON E.CarID = C.CarID
WHERE ED.Status = 'YES' AND D.DefectClassID <> 3
GROUP BY PlateNo, TicketStatus
I think you can solve this that way:
SELECT C.PlateNo, E.EventID, E.TicketStatus, E.EventTime
FROM Car C
INNER JOIN Event E ON C.CarID = E.CarID
INNER JOIN (
SELECT CarID, MAX(E.EventTime) EventTime FROM Event E
LEFT JOIN EventDefects ED ON E.EventID = ED.EventID
LEFT JOIN Defects D ON ED.DefectID = D.DefectID
WHERE D.DefectClassID IN (1,2) AND ED.Status <> 'N/A'
GROUP BY CarID
) T ON E.CarID = T.CarID AND E.EventTime = T.EventTime
The subquery is filtering all events in class 1 and 2 (inspection) and where something happened (<> 'N/A'), and it's getting it's maximum date, so it will bring the last occurence of a real inspection of each car. Then, there's the join to bring the state on that date. From what I understood, that's what you want, right?