Join logic from two separate tables in sql - sql

We returned a list of cardID's after a query and those cardID's belong to two tables Student and Personnel. So how can I join those cardID's with Student and Personnel so I can return a table that shows name of Student and Personnel according to cardID's?
Personnel table:
PERSONNELID NUMBER(9,0)
PERSONNELNAME VARCHAR2(20)
PERSONNELSURNAME VARCHAR2(20)
PERSONNELJOB VARCHAR2(40)
PERSONNELCARDID NUMBER(4,0)
Student table:
STUDENTID NUMBER(9,0)
STUDENTNAME VARCHAR2(20)
STUDENTSURNAME VARCHAR2(20)
STUDENTDEPT VARCHAR2(40)
STUDENTFACULTY VARCHAR2(20)
STUDENTCARDID NUMBER(4,0)
CardID table
CARDID NUMBER(4,0)
USERTYPE VARCHAR2(20)
CHARGE NUMBER(3,2)
CREDIT NUMBER(4,2)
PaymentDevice table:
ORDERNO NUMBER
PAYDEVIP NUMBER(8,0)
PAYDEVDATE DATE No
PAYDEVTIME VARCHAR2(8)
CHARGEDCARDID NUMBER(9,0)
MEALTYPE VARCHAR2(10)
I tried to return first 10 person's name and surname that eat at cafeteria on 27/12/2012
SELECT C.CARDID
FROM CARD C, PAYMENTDEVICE P
WHERE P.ORDERNO
BETWEEN (SELECT MIN(ORDERNO)
FROM PAYMENTDEVICE
WHERE PAYDEVDATE='27/12/2012') AND (SELECT MIN(ORDERNO)
FROM PAYMENTDEVICE
WHERE PAYDEVDATE='27/12/2012')+10 AND C.CARDID=P.CHARGEDCARDID;
Our orderNo isn't reset everyday but keeps increasing so we found the min orderNo that day and add 10 to this value to find first 10 person who eat on that day between those order numbers.
So what return from this query:
CARDID
1005
1000
1002
1003
1009
2000
2001
1007
2002
1004
1006
and those some of those cardId (start with 1) are studentCardId and some of them (starts with 2) are PersonnelCardId. So how can I match and write names accordingly?

SELECT *
FROM Personel p INNER JOIN Student s
ON p.PersonnelCardId = s.StudentCardId
INNER JOIN ReturnedQuery rq
ON rq.CardId = p.PersonnelCardId
updated:
SELECT p.PersonnelName, rq.CardId
FROM Personel p INNER JOIN ReturnedQuery rq
ON rq.CardId = p.PersonnelCardId
UNION
SELECT s.StudentName, rq.Cardid
FROM Student s INNER JOIN ReturnedQuery rq
ON s.StudentCardId = rq.Cardid

Your original query is actually pretty fragile. I'd rewrite it like so (and added the needed joins):
WITH First_Daily_Purchase as (SELECT chargedCardId,
MIN(payDevTime) as payDevTime,
MIN(orderNo) as orderNo
FROM PaymentDevice
WHERE payDevDate >=
TO_DATE('2012-12-27', 'YYYY-MM-DD')
AND payDevDate <
TO_DATE('2012-12-28', 'YYYY-MM-DD')
GROUP BY chargedCardId),
First_10_Daily_Purchasers as (SELECT chargedCardId
FROM (SELECT chargedCardId,
RANK() OVER(ORDER BY payDevTime,
orderNo) as rank
FROM First_Daily_Purchase) a
WHERE a.rank < 11)
SELECT a.chargedCardId, b.personnelName, b.personnelSurname
FROM First_10_Daily_Purchasers a
JOIN Personnel b
ON b.personnelCardId = a.chargedCardId
UNION ALL
SELECT a.chargedCardId, b.studentName, b.studentSurname
FROM First_10_Daily_Purchasers a
JOIN Student b
ON b.studentCardId = a.chargedCardId
(Have a working SQL Fiddle - generally bullet-proofing this took me a while.)
This should get you the first 10 people who made a purchase (not the first 11 purchases, which is what you were actually getting). This of course assumes that payDevTime is actually stored in a sortable format (if it isn't you have bigger problems than this query not working quite right).
That said, there's a number of troubling things about your schema design.

Related

How to join Views with aggregate functions?

My problem:
In #4, I'm having trouble joining two Views because the other has an aggregate function. Same with #5
Question:
Create a view name it as studentDetails, that would should show the student name, enrollment date, total price per unit and subject description of students who are enrolled on the subject Science or History.
Create a view, name it as BiggestPrice, that will show the subject id and highest total price per unit of all the subjects. The view should show only the highest total price per unit that are greater than 1000.
--4.) Create a view name it as studentDetails, that would should show the student name,
-- enrollment date the total price per unit and subject description of students who are
-- enrolled on the subject Science or History.
CREATE VIEW StudentDetails AS
SELECT StudName, EnrollmentDate
--5.) Create a view, name it as BiggestPrice, that will show the subject id and highest total
-- price per unit of all the subjects. The view should show only the highest total price per unit
-- that are greater than 1000.
CREATE VIEW BiggestPrice AS
SELECT SubjId, SUM(Max(Priceperunit)) FROM Student, Subject
GROUP BY Priceperunit
Here is my table:
CREATE TABLE Student(
StudentId char(5) not null,
StudName varchar2(50) not null,
Age NUMBER(3,0),
CONSTRAINT Student_StudentId PRIMARY KEY (StudentId)
);
CREATE table Enrollment(
EnrollmentId varchar2(10) not null,
EnrollmentDate date not null,
StudentId char(5) not null,
SubjId Number(5) not null,
constraint Enrollment_EnrollmentId primary key (EnrollmentId),
constraint Enrollment_StudentId_FK foreign key (StudentId) references Student(StudentId),
constraint Enrollment_SubjId_Fk foreign key (SubjId) references Subject(SubjId)
);
Create table Subject(
SubjId number(5,0) not null,
SubjDescription varchar2(200) not null,
Units number(3,0) not null,
Priceperunit number(9,0) not null,
Constraint Subject_SubjId_PK primary key (SubjId)
);
Since this appears to be a homework question.
You need to use JOINs. Your current query:
CREATE VIEW StudentDetails AS
SELECT StudName, EnrollmentDate
Does not have a FROM clause and the query you have for question 5 uses the legacy comma join syntax with no WHERE filter; this is the same as a CROSS JOIN and will connect every student to every subject and is not what you want.
Don't use the legacy comma join syntax and use ANSI joins and explicitly state the join condition.
SELECT <expression list>
FROM student s
INNER JOIN enrollment e ON ...
INNER JOIN subject j ON ...
Then you can fill in the ... based on the relationships between the tables (typically the primary key of one table = the foreign key of another table).
Then for the <expression list> you need to include the columns asked for in the question: student name and enrolment date and subject name would just be those columns from the appropriate tables; and total price-per-unit (which I assume is actually total-price-per-subject) would be a calculation.
Then for the last part of question 4.
who are enrolled on the subject Science or History.
Add a WHERE filter to only include rows for those subjects.
For question 5, you do not need any JOINS as the question only asks about details in the SUBJECT table.
You need to add a WHERE filter to show "only the highest total price per unit that are greater than 1000". This is a simple multiplication and then you can filter by comparing if it is > 1000.
Then you need to limit the query to return only the row with the "highest total price per unit of all the subjects". From Oracle 12, this would be done with an ORDER BY clause in descending order of total price and then using FETCH FIRST ROW ONLY or FETCH FIRST ROW WITH TIES.
Not sure if i get it fully, but i think its this :
Notes:
Always use Id's to filter records:
where su.SubjId in (1,2)
You can find max record using max() at subquery and join it with main query like this :
where su2.SubjId = su.SubjId
You cannot use alias as filter so you can filter it like:
( su.Units * su.Priceperunit ) > 1000
CREATE VIEW StudentDetails AS
select s.StudName,
e.EnrollmentDate,
su.SubjDescription,
su.Units * su.Priceperunit TotalPrice
from student s
inner join Enrollment e
on e.StudentId = s.StudentId
inner join Subject su
on su.SubjId = e.SubjId
where su.SubjId in (1,2)
CREATE VIEW BiggestPrice AS
select su.SubjId, ( su.Units * su.Priceperunit ) TotalPrice
from Subject su
where ( su.Units * su.Priceperunit ) =
(
select max(su2.Units * su2.Priceperunit)
from Subject su2
where su2.SubjId = su.SubjId
)
and ( su.Units * su.Priceperunit ) > 1000

Cross Reference Columns ORACLE SQL

I am having trouble selecting all values from a table.
I need to select all vehicles where their vRentTimes value is the same as the times they appear in the Renting table.
So basically just checking if the recordings of rentings are correct.
This is the description of tables:
Vehicle
Name Null? Type
------------ -------- ------------
VPLATENUMBER NOT NULL VARCHAR2(7)
VCOLOR NOT NULL VARCHAR2(10)
VCC NOT NULL NUMBER
VHORSEPOWER NOT NULL NUMBER
VRENTTIMES NUMBER
VEHCATNAME NOT NULL VARCHAR2(20)
Renting
Name Null? Type
------------ -------- -----------
CAFM NOT NULL VARCHAR2(9)
VPLATENUMBER NOT NULL VARCHAR2(7)
OUTDATE NOT NULL DATE
INDATE DATE
I see. vRentTimes is supposed to be a count for the second table. You can use JOIN and `aggregation. To get the rows that do not match:
select v.*, r.cnt
from vehicles v left join
(select VPLATENUMBER, count(*) as cnt
from renting r
group by VPLATENUMBER
) r
on r.VPLATENUMBER = v.VPLATENUMBER
where v.VRENTTIMES <> coalesce(cnt, 0);

How to solve this SQL query (header and detail)?

I am working with SQL Server 2008 R2.
I have a SQL query question related to header and detail tables. I have a header table where I am storing location & department & week_start_date. I have a detail table where I am storing employee_id & job_code & work_date & hours.
I want to find those employees who are in different headers with same week_start_date but different location and/or department.
Here is the explanation:
I have a header table:
CREATE TABLE header (
header_id bigint not null PRIMARY KEY,
location_code int not null,
department_code int not null,
week_start_date datetime not null )
I have a detail table:
CREATE TABLE detail (
detail_id bigint not null PRIMARY KEY,
header_id bigint not null FOREIGN KEY header(header_id),
employee_id int not null,
job_code int not null,
work_date datetime not null,
hours decimal(8,2) not null )
header table has the unique key as location_code + department_code + week_start_date.
For example this is the data in header table:
header_id=11, location_code=22, department_code=33,
week_start_date='2016-02-08'
header_id=12, location_code=22, department_code=39,
week_start_date='2016-02-08'
header_id=13, location_code=22, department_code=33,
week_start_date='2016-02-15'
header_id=14, location_code=21, department_code=33,
week_start_date='2016-02-08'
Each row in header table can have multiple rows in detail table.
detail table has the unique key as header_id + employee_id + job_code + work_date.
For example this is the data in detail table for 1000598 employee_id:
detail_id=101, header_id=11, employee_id=1000598, job_code=77,
work_date='2016-02-08', hours=5.00
detail_id=102, header_id=11, employee_id=1000598, job_code=77,
work_date='2016-02-09', hours=4.00
detail_id=109, header_id=12, employee_id=1000598, job_code=79,
work_date='2016-02-11', hours=4.50
For example this is the data in detail table for 1000599 employee_id:
detail_id=121, header_id=11, employee_id=1000599, job_code=78,
work_date='2016-02-10', hours=8.00
detail_id=122, header_id=14, employee_id=1000599, job_code=75,
work_date='2016-02-12', hours=3.00
For example this is the data in detail table for 1000600 employee_id:
detail_id=131, header_id=11, employee_id=1000600, job_code=72,
work_date='2016-02-11', hours=7.00
detail_id=132, header_id=13, employee_id=1000600, job_code=75,
work_date='2016-02-17', hours=3.00
The SQL query should return 1000598 employee_id as 1000598 has data for both department_code=33 and department_code=39 for the same week_start_date='2016-02-08'.
The SQL query should return 1000599 employee_id as 1000599 has data for both location_code=22 and location_code=21 for the same week_start_date='2016-02-08'.
The SQL query should not return 1000600 employee_id.
This is the start I have come up with:
select
h.employee_id,
d.week_start_date
from
header h (nolock)
inner join detail d (nolock)
on h.header_id = d.header_id
group by
h.employee_id,
d.week_start_date
order by
1,
2
Not much.
I want to find those employees who are in different headers with same
week_start_date but different location and/or department.
The query below will return all (employee_id, week_start_date) pairs that have more than 1 location_code or department_code
select d.employee_id, h.week_start_date
from detail d
join header h on h.header_id = d.header_id
group by d.employee_id, h.week_start_date -- employees with same week_start_date
having (
count(distinct h.location_code) > 1 -- have more than 1 location
or count(distinct h.department_code) > 1 -- or more than 1 department
)
select dtl.* from
(select * from detail d1 where EXISTS(select count(header_id) from detail d2 where d1.employee_id=d2.employee_id having count(header_id)>1))
inner join
header hd
on hd.header_id=dtl.header_id
group by dtl.employee_id, hd.week_start_date
having count(*)>1

Making Queries for relaional model

I have some troubles with making queries for this task. anyone please help me.
Cars(License, Brand, Year);
Employees(EID, Firstname, Lastname, Wage);
Customers(CID, Firstname, Lastname, Phone);
OfficeStaff(EID, OfficeNumber);
Own(CID, License);
Mechanic(EID, HourlyPrice);
Repairs(License, EID, PartCost, Hours);
Create SQL statements performing the following tasks:
(a) Create the OfficeStaff-table while taking into account that the OfficeNumber may not be NULL and must be in
the range [1..10].
(b) Find the name(s), i.e. firstname(s) and lastname(s), of the owner(s) of the car(s), which have been repaired for
the most times.
(c) Find the average number of hours spent (i.e. Repairs.Hours) repairing cars of brand “Opel”.
(d) Update the HourlyPrice to be 20 EUR for all mechanics with a wage of 100 EUR or more.
My tries:
(a) Create the OfficeStaff-table while taking into account that the OfficeNumber may not be NULL and must be in the range [1..10].
CREATE TABLE OfficeStaff (
EID INT PRIMARY KEY,
Firstname TEXT,
Lastname TEXT,
Wage REAL,
OfficeNumber INT NOT NULL,
CONSTRAINT CK_OfficeNumber CHECK (OfficeNumber BETWEEN 1 AND 10)
b) no idea?!
(c) Find the average number of hours spent (i.e. Repairs.Hours) repairing cars of brand “Opel”.
SELECT AVG(R.Hours)
FROM Repairs R, Cars C
WHERE R.License = C.License AND C.Brand = “Opel”
(d) Update the HourlyPrice to be 20 EUR for all mechanics with a wage of 100 EUR or more.
UPDATE Mechanic
SET HourlyPrice = 20
WHERE Wage >= 100
(a) how to create can look here
CREATE TABLE OfficeStaff (
EID INT PRIMARY KEY,
Firstname varchar(100),
Lastname varchar(100),
Wage decimal(15,2),
OfficeNumber INT NOT NULL,
CONSTRAINT CK_OfficeNumber CHECK (OfficeNumber BETWEEN 1 AND 10)
)
(b) Not sure about this one, but you have to use rank to get not only 1 value among same values. For this you can look here
WITH cte AS (
SELECT a.Firstname, a.Lastname, rank() OVER (ORDER BY count(c.Hours)) AS rnk
from Customers as a
join Own as b
on a.CID=b.CID
join Repairs as c
on b.License = c.License
group by a.Firstname, a.Lastname
)
SELECT *
FROM cte
WHERE rnk <= 1;
(c) join usage here
SELECT AVG(R.Hours)
FROM Repairs R
join Cars C
on R.license=C.license
WHERE C.Brand = 'Opel'
(d) update usage here and here
UPDATE Mechanic
SET HourlyPrice = 20
from Employees
WHERE Mechanic.EID = Employees.EID
AND Employees.Wage >= 100

Add or delete repeated row

I have an output like this:
id name date school school1
1 john 11/11/2001 nyu ucla
1 john 11/11/2001 ucla nyu
2 paul 11/11/2011 uft mit
2 paul 11/11/2011 mit uft
I would like to achieve this:
id name date school school1
1 john 11/11/2001 nyu ucla
2 paul 11/11/2011 mit uft
I am using direct join as in:
select distinct
a.id, a.name,
b.date,
c.school
a1.id, a1.name,
b1.date,
c1.school
from table a, table b, table c,table a1, table b1, table c1
where
a.id=b.id
and...
Any ideas?
We will need more information such as what your tables contain and what you are after.
One thing I noticed is you have a school and then school1. 3nf states that you should never duplicate fields and append numbers to them to get more information even if you think that the relationship will only be 1 or 2 additional items. You need to create a second table that stores a user associated with 1 to many schools.
I agree with everyone else that both your source table and your desired output are poor design. While you probably can't do anything about your source table, I recommend the following code and output:
Select id, name, date, school from MyTable;
union
Select id, name, date, school1 from MyTable;
(repeat as necessary)
This will give you results in the format:
id name date school
1 john 11/11/2001 nyu
1 john 11/11/2001 ucla
2 paul 11/11/2011 mit
2 paul 11/11/2011 uft
(Note: in my version of SQL, union queries automatically select distinct records so the distinct flag isn't needed)
With this format, you could easily count the number of schools per student, number of students per school, etc.
If processing time and/or storage space is a factor here, you could then split this into 2 tables, 1 with the id,name & date, the other with the id & school (basically what JonH just said). But if you're just working up some simple statistics, this should suffice.
This problem was just too irresistable, so I just took a guess at the data structures that we are dealing with. The technology wasn't specified in the question. This is in Transact-SQL.
create table student
(
id int not null primary key identity,
name nvarchar(100) not null default '',
graduation_date date not null default getdate(),
)
go
create table school
(
id int not null primary key identity,
name nvarchar(100) not null default ''
)
go
create table student_school_asc
(
student_id int not null foreign key references student (id),
school_id int not null foreign key references school (id),
primary key (student_id, school_id)
)
go
insert into student (name, graduation_date) values ('john', '2001-11-11')
insert into student (name, graduation_date) values ('paul', '2011-11-11')
insert into school (name) values ('nyu')
insert into school (name) values ('ucla')
insert into school (name) values ('uft')
insert into school (name) values ('mit')
insert into student_school_asc (student_id, school_id) values (1,1)
insert into student_school_asc (student_id, school_id) values (1,2)
insert into student_school_asc (student_id, school_id) values (2,3)
insert into student_school_asc (student_id, school_id) values (2,4)
select
s.id,
s.name,
s.graduation_date as [date],
(select max(name) from
(select name,
RANK() over (order by name) as rank_num
from school sc
inner join student_school_asc ssa on ssa.school_id = sc.id
where ssa.student_id = s.id) s1 where s1.rank_num = 1) as school,
(select max(name) from
(select name,
RANK() over (order by name) as rank_num
from school sc
inner join student_school_asc ssa on ssa.school_id = sc.id
where ssa.student_id = s.id) s2 where s2.rank_num = 2) as school1
from
student s
Result:
id name date school school1
--- ----- ---------- ------- --------
1 john 2001-11-11 nyu ucla
2 paul 2011-11-11 mit uft