How to create a query using a view - sql

How do I use the view I created to get different data outputs. I have created the view VJobCustomerLaborCost. This view gives each job for each customer with total of labor cost for each job. I need to get each customer with total labor cost for each customer. I have given the View and sample outputs.
GO
CREATE VIEW VJobCustomerLaborCost
AS
SELECT DISTINCT
TJ.intJobID
,TJ.strJobDescription
,TC.intCustomerID
,TC.strLastName + ', ' + TC.strFirstName AS strCustomerName
,SUM(TJE.intHoursWorked * TE.monHourlyRate) AS monTotalLaborCost
FROM
TJobs AS TJ
,TJobCustomers AS TJC
,TCustomers AS TC
,TJobEmployees AS TJE
,TEmployees AS TE
WHERE
TJ.intJobID = TJC.intJobID
AND TJC.intCustomerID = TC.intCustomerID
AND TJ.intCustomerID = TJC.intCustomerID
AND TJE.intJobID = TJ.intJobID
GROUP BY
TJ.intJobID
,TJ.strJobDescription
,TC.intCustomerID
,TC.strLastName
,TC.strFirstName
GO
This is the output from my view. This gives me all customers and each jobs total labor cost
intJobID strJobDescription intCustomerID strCustomerName monTotalLaborCost
----------- ------------------------- ------------------- ----------------- ---------------------
1 Kitchen Remodel 1 Belcher, Bob 8740.00
8 Basement Remodel 1 Belcher, Bob 13300.00
9 Bathroom Remodel 1 Belcher, Bob 12065.00
10 Roof Replacement 1 Belcher, Bob 3325.00
11 Living Room Remodel 1 Belcher, Bob 0.00
3 Bedroom Remodel 3 Parker, Peter 3800.00
6 Roof Replacement 3 Parker, Peter 0.00
7 Basement Remodel 3 Parker, Peter 1710.00
4 Bedroom Remodel 4 Solo, Hans 2850.00
5 Basement Remodel 2 Stark, Tony 0.00
What I need is to use the View created to get total labor cost for each customer. So output would be
intCustomerID strCustomerName monTotalLaborCost
------------------- ----------------- ---------------------
1 Belcher, Bob 37,430
3 Parker, Peter 5,510
4 Solo, Hans 2,850
2 Stark, Tony 0.00
How do I use the above view to get this output?

It looks like you need just to group and sum your data:
select
intCustomerID,
strCustomerName,
sum(monTotalLaborCost) as monTotalLaborCost
from VJobCustomerLaborCost
group by intCustomerID, strCustomerName

Related

JOIN Tables based on Service Date

I have 2 Tables (History and Responsible). They need to be JOINED based on Service Date.
History Table:
Id
ServiceDate
Hours
ClientId
ClientName
1
2021-10-15
3
123
Tom Holland
2
2021-10-25
5
123
Tom Holland
3
2022-01-14
2
123
Tom Holland
Responsible Table:
2999-12-31 means Responsible has no end date (current)
ClientId
ClientName
ResponsibleId
ResponsibleName
ResponsibleStartDate
ResponsibleEndtDate
123
Tom Holland
77
Thomas Anderson
2020-09-17
2021-10-17
123
Tom Holland
88
Tom Cruise
2021-10-18
2999-12-31
123
Tom Holland
99
Sten Lee
2022-01-07
2999-12-31
My code produces multiple rows, because 2022-01-14 Service date falls under multiple date ranges from Responsible Table:
SELECT h.Id,
h.ServiceDate,
h.Hours,
h.ClientId,
h.ClientName,
r.ResponsibleName
FROM History AS h
LEFT JOIN Responsible AS r
ON (h.ClientId = r.ClientId AND h.ServiceDate BETWEEN r.ResponsibleStartDate AND r.ResponsibleEndtDate)
The output of the query above is:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3
123
Tom Holland
Thomas Anderson
2
2021-10-25
5
123
Tom Holland
Tom Cruise
3
2022-01-14
2
123
Tom Holland
Tom Cruise
3
2022-01-14
2
123
Tom Holland
Sten Lee
Technically, output is correct (because 2022-01-14 is between 2021-10-18 - 2999-12-31 as well between 2022-01-07 - 2999-12-31), but not what I need.
I would like to know if possible to achieve 2 outputs:
1) If Service Date falls in multiple date ranges from Responsible Table, Responsible Should be the person who's ResponsibleStartDate is closer to the ServiceDate:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3
123
Tom Holland
Thomas Anderson
2
2021-10-25
5
123
Tom Holland
Tom Cruise
3
2022-01-14
2
123
Tom Holland
Sten Lee
2) Keep all rows, if Service Date falls in multiple date ranges from Responsible Table, but split Hours evenly between Responsible:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3
123
Tom Holland
Thomas Anderson
2
2021-10-25
5
123
Tom Holland
Tom Cruise
3
2022-01-14
1
123
Tom Holland
Tom Cruise
3
2022-01-14
1
123
Tom Holland
Sten Lee
First one, we can use a window function to apply a row number, based on how close to ServiceDate the ResponsibleStartDate is, then we can just pick the first row per h.Id. If there is a tie we can break it by picking something that will give us deterministic order, e.g. ORDER BY {DATEDIFF expression}, ResponsibleName.
;WITH cte AS
(
SELECT h.Id,
h.ServiceDate,
h.Hours,
h.ClientId,
h.ClientName,
r.ResponsibleName,
RankOrderedByProximityToServiceDate = ROW_NUMBER() OVER
(PARTITION BY h.Id
ORDER BY ABS(DATEDIFF(DAY, ResponsibleStartDate, ServiceDate)))
FROM dbo.History AS h
LEFT JOIN dbo.Responsible AS r
ON (h.ClientId = r.ClientId
AND h.ServiceDate BETWEEN r.ResponsibleStartDate AND r.ResponsibleEndtDate)
)
SELECT Id, ServiceDate, Hours, ClientId, ClientName, ResponsibleName
FROM cte WHERE RankOrderedByProximityToServiceDate = 1;
Output:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3
123
Tom Holland
Thomas Anderson
2
2021-10-25
5
123
Tom Holland
Tom Cruise
3
2022-01-14
2
123
Tom Holland
Sten Lee
Second one doesn't require a CTE, we can simply divide the Hours in h by the number of rows that exist for that h.Id, then limit it to 2 decimal places:
SELECT h.Id,
h.ServiceDate,
Hours = CONVERT(decimal(11,2),
h.Hours * 1.0
/ COUNT(h.Id) OVER (PARTITION BY h.Id)),
h.ClientId,
h.ClientName,
r.ResponsibleName
FROM dbo.History AS h
LEFT JOIN dbo.Responsible AS r
ON (h.ClientId = r.ClientId
AND h.ServiceDate BETWEEN r.ResponsibleStartDate AND r.ResponsibleEndtDate);
Output:
Id
ServiceDate
Hours
ClientId
ClientName
ResponsibleName
1
2021-10-15
3.00
123
Tom Holland
Thomas Anderson
2
2021-10-25
5.00
123
Tom Holland
Tom Cruise
3
2022-01-14
1.00
123
Tom Holland
Tom Cruise
3
2022-01-14
1.00
123
Tom Holland
Sten Lee
Both demonstrated in this db<>fiddle.
My attempt at part 1 - it doesn't work if there's more than one Responsible as of the same start date.
WITH
"all_services" AS (
SELECT
h.Id,
h.ServiceDate,
h.Hours,
h.ClientId,
h.ClientName,
r.ResponsibleName,
r.ResponsibleStartDate
FROM History AS h
LEFT JOIN Responsible AS r
ON h.ClientId = r.ClientId
AND h.ServiceDate BETWEEN r.ResponsibleStartDate AND r.ResponsibleEndtDate
),
"most_recent_key" AS (
SELECT
ServiceDate,
ClientId,
MAX(ResponsibleStartDate) AS "ResponsibleStartDate"
FROM all_services
GROUP BY ServiceDate, ClientId
)
SELECT Id, ServiceDate, Hours, ClientId, ClientName, ResponsibleName
FROM all_services
INNER JOIN most_recent_key
USING (ServiceDate, ClientId, ResponsibleStartDate)
Posting it anyway as a contrast to Aaron's better solution as a learning point for myself.

3 Tables, JOIN query and alphabetic order

I am currently working with three tables where I am trying to figure out how to use a join to display once the title_id of any book with Dennis McCann as an editor. The tables have in common title_id and editor_id. Cant find a way to piece it all together. How to display once the title_id of any book with Dennis McCann as an editor?
SELECT * FROM title_editors;
EDITOR_ID TITLE_ EDITOR_ORDER
----------- ------ ------------
826-11-9034 Bu2075 2
826-11-9034 PS2091 2
826-11-9034 Ps2106 2
826-11-9034 PS3333 2
826-11-9034 PS7777 2
826-11-9034 pS1372 2
885-23-9140 MC2222 2
885-23-9140 MC3021 2
885-23-9140 Tc3281 2
885-23-9140 TC4203 2
885-23-9140 TC7777 2
321-55-8906 bU1032 2
321-55-8906 BU1111 2
321-55-8906 BU7832 2
321-55-8906 PC1035 2
321-55-8906 PC8888 2
321-55-8906 BU2075 3
777-02-9831 pc1035 3
777-02-9831 PC8888 3
943-88-7920 BU1032 1
943-88-7920 bu1111 1
943-88-7920 BU2075 1
943-88-7920 BU7832 1
943-88-7920 PC1035 1
943-88-7920 pc8888 1
993-86-0420 PS1372 1
993-86-0420 PS2091 1
993-86-0420 PS2106 1
993-86-0420 PS3333 1
993-86-0420 pS7777 1
993-86-0420 MC2222 1
993-86-0420 MC3021 1
993-86-0420 Tc3218 1
993-86-0420 TC4203 1
993-86-0420 TC7777 1
35 rows selected.
SQL> SELECT * FROM title_authors;
AUTHOR_ID TITLE_ AUTHOR_ORDER ROYALTY_SHARE
----------- ------ ------------ -------------
409-56-7008 Bu1032 1 .6
486-29-1786 PS7777 1 1
486-29-1786 pC9999 1 1
712-45-1867 MC2222 1 1
172-32-1176 Ps3333 1 1
213-46-8915 BU1032 2 .4
238-95-7766 PC1035 1 1
213-46-8915 Bu2075 1 1
998-72-3567 pS2091 1 .5
899-46-2035 PS2091 2 .5
998-72-3567 PS2106 1 1
722-51-5454 mc3021 1 .75
899-46-2035 MC3021 2 .25
807-91-6654 tC3218 1 1
274-80-9391 BU7832 1 1
427-17-2319 pC8888 1 .5
846-92-7186 PC8888 2 .5
756-30-7391 PS1372 1 .75
724-80-9391 PS1372 2 .25
724-80-9391 bu1111 1 .6
267-41-2394 bU1111 2 .4
672-71-3249 TC7777 1 .4
267-41-2394 TC7777 2 .3
472-27-2349 Tc7777 3 .3
648-92-1872 TC4203 1 1
25 rows selected.
SQL> SELECT * FROM editors;
EDITOR_ID EDITOR_LNAME EDITOR_FNAME EDITOR_POSITION PHONE ADDRESS CITY ST ZIP
----------- ----------------- ------------- --------------- ------------ -------------------- ------------ -- ------
321-55-8906 DeLongue Martinella Project 415 843-2222 3000 6th St. BERKELEY Ca 94710
723-48-9010 Sparks MANfred cOPY 303 721-3388 15 Sail DENVER Co 80237
777-02-9831 Samuelson Bernard proJect 415 843-6990 27 Yosemite OAKLAND Ca 94609
777-66-9902 Almond Alfred copy 312 699-4177 1010 E. DeVON CHICAGO Il 60018
826-11-9034 Himmel Eleanore pRoject 617 423-0552 97 Bleaker BOSTON Ma 02210
885-23-9140 Rutherford-Hayes Hannah PROJECT 301 468-3909 32 Rockbill Pike ROCKBILL MD 20852
993-86-0420 McCann Dennis acQuisition 301 468-3909 32 Rockbill Pike ROCKBill MD 20852
943-88-7920 Kaspchek Christof acquisitiOn 415 549-3909 18 Severe Rd. BERKELEY CA 94710
234-88-9720 Hunter Amanda acquisition 617 432-5586 18 Dowdy Ln. BOSTON MA 02210
You can try join on the table Editors and Ttile_Editors using the Editor_ID that will give you the matching records and you can filter out Only for the ' Dennis McCann ' using either multiple conditions in join or the where clause as,
WITHOUT WHERE
SELECT DISTINCT te.title_id,ed.EDITOR_ID,ed.EDITOR_LNAME,ed.EDITOR_FNAME
FROM
title_editors te JOIN editors ed
ON te.EDITOR_ID = ed.EDITOR_ID
AND ed.EDITOR_LNAME = 'McCann'
AND ed.EDITOR_FNAME = 'Dennis'
ORDER BY te.title_id
USing WHERE
SELECT DISTINCT te.title_id,ed.EDITOR_ID,ed.EDITOR_LNAME,ed.EDITOR_FNAME
FROM
title_editors te JOIN editors ed
ON te.EDITOR_ID = ed.EDITOR_ID
WHERE
ed.EDITOR_LNAME = 'McCann'
AND ed.EDITOR_FNAME = 'Dennis'
ORDER BY te.title_id
It would be easier with the in operator:
SELECT DISTINCT title_id
FROM title_editors
WHERE editor_id IN (SELECT editor_id
FROM editors
WHERE editor_fname = 'Dennis' AND
editor_lname = 'McCann')
ORDER BY title_id ASC

SQL get latest record for each ID

I have three tables that contains data as below:
Users
Id Name Other_Columns
---------------------------
1 John Blah
2 Ricky Blah
3 Stella Blah
4 Bob Blah
Saldo
Id User_id Saldo
--------------------
1 3 0.00
2 1 9.00
3 2 0.15
4 4 3.50
Payments
Id User_id Amount Paid_date
------------------------------------------
1 2 10.00 2014-09-01 08:10
2 2 25.00 2014-09-01 09:00
3 3 100.00 2014-05-10 12:47
4 1 20.50 2014-02-23 15:30
How to get result like this:
Id Name Saldo Last Payment
------------------------------------------
1 John 9.00 23.02.2014 20.50
2 Ricky 0.15 01.09.2014 25.00
3 Stella 0.00 0000-00-00 0.00
4 Bob 3.50 10.05.2014 100.00
Thank you.
select u.id, u.name, s.saldo, p.last_paid_date, p2.amount
from users u
join saldo s
on u.id = s.user_id
join (select user_id, max(paid_date) as last_paid_date
from payments
group by user_id) p
on u.id = p.user_id
join payments p2
on p.last_paid_date = p2.paid_date
and p.user_id = p2.user_id
This answer assumes:
(1) On table SALDO, there is one row per USER_ID
(2) On table PAYMENTS, there can be multiple rows per USER_ID
(I'm pretty confident about #2 being true, I don't know about #1, as you didn't say and your sample data doesn't indicate one way or the other)

Why does my view query split into two?

I am trying to create a view that records the selected attributes for all Computer Science majors.
This is my query to create a view:
DROP VIEW CS_grade_report;
CREATE VIEW CS_grade_report AS
SELECT Student.student_id AS "ID",
student_name AS "Name",
course_number AS "Course #",
credit AS "Credit",
grade AS Grade
FROM Student, Class, Enrolls
WHERE major = 'CSCI'
AND Student.student_id = Enrolls.student_id
AND Class.schedule_num = Enrolls.schedule_num;
SELECT *
FROM CS_grade_report;
And this is what is generated:
ID Name Course # Credit GR
------ ------------------------- -------- ---------- --
600000 John Smith CSCI3200 4 B+
600000 John Smith CSCI3700 3 C
600000 John Smith SPAN1004 3 A-
600000 John Smith CSCI4300 3 A+
600001 Andrew Tram MUSC2406 2 A+
600001 Andrew Tram SPAN1004 3 A
600001 Andrew Tram CSCI3700 3 B-
600002 Jane Doe CSCI4200 3 D+
600003 Michael Jordan CSCI4300 3 A+
600004 Tiger Woods MUSC1000 1 A
600007 Dominique Davis CSCI4300 3 F
ID Name Course # Credit GR
------ ------------------------- -------- ---------- --
600009 Will Smith CSCI3200 4 A
600010 Papa Johns CSCI3200 4 B
600011 John Doe CSCI3200 4 C
600012 Jackie Chan CSCI3200 4 D
600013 Some Guy CSCI3200 4 E
16 rows selected.
I am assuming this is output from sqlplus. There is a "pagesize" option to define when breaks are added. If you only want to see one heading, set the size to a large enough value prior to running your SELECT statement as such:
set pagesize 500
(or whatever size you want)
There are many command options for sqlplus. This link is a good cheat-sheet.

Retrieve top 48 unique records from database based on a sorted Field

I have database table that I am after some SQL for (Which is defeating me so far!)
Imagine there are 192 Athletic Clubs who all take part in 12 Track Meets per season.
So that is 2304 individual performances per season (for example in the 100Metres)
I would like to find the top 48 (unique) individual performances from the table, these 48 athletes are then going to take part in the end of season World Championships.
So imagine the 2 fastest times are both set by "John Smith", but he can only be entered once in the world champs. So i would then look for the next fastest time not set by "John Smith"... so on and so until I have 48 unique athletes..
hope that makes sense.
thanks in advance if anyone can help
PS
I did have a nice screen shot created that would explain it much better. but as a newish user i cannot post images.
I'll try a copy and paste version instead...
ID AthleteName AthleteID Time
1 Josh Lewis 3 11.99
2 Joe Dundee 4 11.31
3 Mark Danes 5 13.44
4 Josh Lewis 3 13.12
5 John Smith 1 11.12
6 John Smith 1 12.18
7 John Smith 1 11.22
8 Adam Bennett 6 11.33
9 Ronny Bower 7 12.88
10 John Smith 1 13.49
11 Adam Bennett 6 12.55
12 Mark Danes 5 12.12
13 Carl Tompkins 2 13.11
14 Joe Dundee 4 11.28
15 Ronny Bower 7 12.14
16 Carl Tompkin 2 11.88
17 Nigel Downs 8 14.14
18 Nigel Downs 8 12.19
Top 4 unique individual performances
1 John Smith 1 11.12
3 Joe Dundee 4 11.28
5 Adam Bennett 6 11.33
6 Carl Tompkins 2 11.88
Basically something like this:
select top 48 *
from (
select athleteId,min(time) as bestTime
from theRaces
where raceId = '123' -- e.g., 123=100 meters
group by athleteId
) x
order by bestTime
try this --
select x.ID, x.AthleteName , x.AthleteID , x.Time
(
select rownum tr_count,v.AthleteID AthleteID, v.AthleteName AthleteName, v.Time Time,v.id id
from
(
select
tr1.AthleteName AthleteName, tr1.Time time,min(tr1.id) id, tr1.AthleteID AthleteID
from theRaces tr1
where time =
(select min(time) from theRaces tr2 where tr2.athleteId = tr1.athleteId)
group by tr1.AthleteName, tr1.AthleteID, tr1.Time
having tr1.Time = ( select min(tr2.time) from theRaces tr2 where tr1.AthleteID =tr2.AthleteID)
order by tr1.time
) v
) x
where x.tr_count < 48