I have a table (Vehicles) which contains a list of vehicles.
VehicleID
PlateNo
CurrentDriver
I also have a table (History) which contains a the driver history for the vehicles:
HistoryID
VehicleID
ReceivedDate (vehicle receiving date)
DriverName
I have another table (Repairs) which contains the repairs for all the vehicles:
RepairID
VehicleID
RepairDate
RepairCost
Using SQL Server and based on the History table, I want to get all the RepairCost values between two dates for a given DriverName.
For example, I want to get all the RepairCost values for driver 'John Doe', between 01.01.2013 and 01.05.2013, who was allocated to three different vehicles in that period.
My query so far is:
SELECT H.DriverName, R.RepairCost, R.RepairDate
FROM Repairs AS R
INNER JOIN Vehicles AS V ON R.VehicleID = V.VehicleID
INNER JOIN History H ON H.VehicleID = V.VehicleID
WHERE H.DriverName = 'John'
AND R.RepairDate BETWEEN '01.01.2013' AND '04.01.2013'
There's also some sample data in a SQL Fiddle.
The problem seems to be that I'm getting all the results twice.
LATER EDIT:
My progress so far:
DECLARE #Driver varchar(50),#StartDt datetime, #EndDt datetime
SELECT #Driver = 'John Doe',#StartDt = '20130101' ,#EndDt = '20130501'
;With VehicleAllocation
AS
(
SELECT h.*,h1.ChangeDate
FROM History h
OUTER APPLY (SELECT MIN(ReceivedDate) AS ChangeDate
FROM History
WHERE VehicleID = h.VehicleID
AND DriverName <> h.DriverName
AND ReceivedDate > h.ReceivedDate
)h1
WHERE h.DriverName = #Driver
)
SELECT *
FROM VehicleAllocation h
INNER JOIN Repairs r
ON r.VehicleID = h.VehicleID
WHERE DriverName = #Driver
AND RepairDate > = #StartDt
AND RepairDate < #EndDt + 1
AND RepairDate BETWEEN h.ReceivedDate AND COALESCE(h.ChangeDate,RepairDate)
I discoverd a problem with the line 'AND DriverName <> h.DriverName'. Why is that line useful? If I had the same driver name, one after the other, in the History table, it skipped to the last car delivery date for that driver name.
Sample data:
'History' table
ReceivedDate DriverName
04.11.2013 Mike
13.11.2013 Dan
15.11.2013 Dan
17.11.2013 Ryan
20.11.2013 Dan
22.11.2013 Ryan
25.11.2013 Mike
26.11.2013 Dan
29.11.2013 Ryan
04.12.2013 Dan
'Repairs' table
RepairDate RepairCost
05.11.2013 2615.30
14.11.2013 135.66
16.11.2013 4913.04
18.11.2013 538.92
21.11.2013 152.48
23.11.2013 5946.89
26.11.2013 3697.64
27.11.2013 734.01
30.11.2013 279.62
Query result
RepairDate RepairCost
07.11.2013 380.00
14.11.2013 135.66
16.11.2013 4913.04
16.11.2013 4913.04
21.11.2013 152.48
27.11.2013 734.01
As you can see in the query result, line 3 and 4 have the same value/date.
The query interval was 01-01-2013 <-> 31-12-2013.
Also, what if I want to get the SUM of different colums from different tables?
For example, SUM(Total) column from 'Repairs' table, SUM(Value) column from 'Tires' table...
How can I adapt the script?
Thanks!
I have no idea why you include you Vehicle table in your query, as you don't want any information from there.
You are getting "double" results because you match every Repair (e.g. the one on jan 15th) with every record with the same Vehicle id is History (there are three of those!). Two of those matches are for drive John, so you get two results.
What you want is to match only on the driver that, according to your history table, was the drive at the time of the repair!
So, I first matched each repairdate with the actually matching Receiveddate in the history table:
SELECT R1.Repairdate, Max(H1.ReceivedDate) as ReceivedDate
FROM Repairs R1
JOIN History H1
ON R1.VehicleID=H1.VehicleID
AND H1.ReceivedDate < R1.RepairDate
GROUP BY R1.RepairDate
I then used that query in a join to receive the wanted data:
SELECT R.RepairDate, H.DriverName, R.RepairCost
FROM Repairs AS R
JOIN History H ON R.VehicleID=H.VehicleID
JOIN (
SELECT R1.Repairdate, Max(H1.ReceivedDate) as ReceivedDate
FROM Repairs R1
JOIN History H1
ON R1.VehicleID=H1.VehicleID
AND H1.ReceivedDate < R1.RepairDate
GROUP BY R1.RepairDate)
AS H2
ON R.Repairdate = H2.Repairdate
AND H.ReceivedDate = H2.Receiveddate
WHERE R.RepairDate BETWEEN '01.01.2013' AND '04.01.2013'
AND H.DriverName = 'John'
This returns me 6 records : http://sqlfiddle.com/#!3/fcebf/62
As a sanity check, leave out the complete WHERE on date and name and include the vehicle number in teh select.
You will get 14 repairs listed with the name of the driver who was drivign the vehicle at that time. You can easily confirm that the driver linked to the vehicle at that time is correct according to your History data:
DRIVERNAME VEHICLEID REPAIRDATE REPAIRCOST
John 1 January, 15 2013 00:00:00+0000 10
Ryan 2 January, 18 2013 00:00:00+0000 15
Ryan 2 January, 22 2013 00:00:00+0000 15
John 1 February, 03 2013 00:00:00+0000 5
Ryan 2 February, 05 2013 00:00:00+0000 25
John 1 February, 10 2013 00:00:00+0000 10
John 2 February, 26 2013 00:00:00+0000 10
Ryan 1 March, 01 2013 00:00:00+0000 100
John 2 March, 03 2013 00:00:00+0000 30
John 2 March, 08 2013 00:00:00+0000 5
Ryan 1 March, 10 2013 00:00:00+0000 45
Ryan 1 March, 17 2013 00:00:00+0000 25
Ryan 2 March, 25 2013 00:00:00+0000 10
Ryan 2 March, 28 2013 00:00:00+0000 30
if you add HistoryID to your query you will notice that rows are not duplicate, but they have different HistoryID, try this query
SELECT H.DriverName, R.RepairCost, R.RepairDate , H.HistoryID
FROM
Repairs AS R
INNER JOIN Vehicles AS V ON R.VehicleID=V.VehicleID
INNER JOIN History H ON H.VehicleID=V.VehicleID
WHERE H.DriverName='John' AND R.RepairDate BETWEEN '01.01.2013' AND '04.01.2013'
so I think it's something wrong with your data.
you can eliminate these duplicate rows using DISTINCT , but I recommend you to double check your History table data
Related
I have two tables, The Instructor table, and the Department Table. The Instructor can be involved in many departments and the departments can contain many Instructors. I'm trying to populate the DepartmentInstructor table to create a many-to-many relationship. The tables are populated like so,
Department Table
DepartmentID DepartmentName
1 Aaron Copland School of Music
2 American Studies
3 Art
4 Classical, Middle Eastern, and Asian Languages and Cultures
5 Comparative Literature
6 Drama, Theatre & Dance
7 English
8 European Languages and Literatures
Instructor Table
InstructorID InstructorFullName
1 Abrams, Brian
2 Ciavarella, Peter
3 Franklin, Arnold
4 Shur, Mitchell
5 Reich, Toby
6 Meyers, Allison
7 Dana, Kathryn
8 Rhindress, Mindy
What I'm trying to do is,
DepartmentInstructor Table
DepartmentID InstructorID
1 3
3 7
2 7
6 4
Edit:
Responding to #GeorgeJoseph, We were also given a table that contains all of the data besides the IDs. This table is shown below,
Table X
Semester Sec Code Course(HR,CRD) Description Day Time Instructor Location Enrolled Limit ModeOfInstruction
Spring 2019 02 37366 ACCT 100 (3, 3) Fin & Mgr Acct T, TH 3:10 PM - 4:25 PM Milo, Michael KY 419 20 22 In-Person
Spring 2019 03 37823 ACCT 100 (3, 3) Fin & Mgr Acct M 3:10 PM - 6:00 PM Ho, Vivian HH 17 21 22 In-Person
Spring 2019 01 37365 ACCT 100 (3, 3) Fin & Mgr Acct T, TH 10:45 AM - 12:00 PM Milo, Michael KY 419 22 22 In-Person
Spring 2019 06 7351 ACCT 101 (4, 3) Int Theo & Prac Acct 1 T, TH 12:10 PM - 2:00 PM Feisullin, Anita RA 201 30 30 In-Person
Spring 2019 12 7357 ACCT 101 (4, 3) Int Theo & Prac Acct 1 SU 8:20 AM - 12:00 PM Mintz, Chana PH 204 39 55 In-Person
Spring 2019 11 7356 ACCT 101 (4, 3) Int Theo & Prac Acct 1 S 8:20 AM - 12:00 PM Chan, Joseph PH 110 54 55 In-Person
Spring 2019 10 7355 ACCT 101 (4, 3) Int Theo & Prac Acct 1 F 6:30 PM - 10:30 PM Solarsh, Eva PH 212 30 30 Hybrid
Spring 2019 09 7354 ACCT 101 (4, 3) Int Theo & Prac Acct 1 T, TH 8:50 PM - 10:30 PM Zapf, Michael PH 110 29 55 In-Person
I added the data to the Instructor Table and the Department table through this table. Let's call this table X. The DepartmentName was created by using a case statement over the Course(HR,CRD) column.
Now to answer your question, Table X should help us in forming that many-to-many relationship between the Instructor and the Department Table. I'm currently not sure how to map the relationship. What I tried doing was this,
SELECT DISTINCT [Description], Instructor
FROM Schema.X AS x
INNER JOIN [College].[Instructor] AS I
ON x.Instructor = I.InstructorFullName
This will then give me the corresponding course taught by a professor but I'm unsure of how to go from here.
Edit 2:
Here's how my DB design looks,
As George and yourself have mentioned, you are almost there. I am using SQL Server / T-SQL
In my example you have a course table, an instructor table and a department table.
The course table must have the instructorID and the departmentID as a column. This is how you bridge the gap between all the tables. It means that you have a distinct list of departments, courses (with the linking department and tutor IDs) and a distinct list of tutors. There are considerations where a course has more than one tutor (Could happen I suppose) but test out what suits your setup. Probably add a new row to courses with the same departmentId and the 2nd tutorID.
I have added some extra columns in the output.
You can also see not all departments have courses assigned to them. Lack of funding! Also note I have used left join where inner might work better depending on your situation or where clause. EG Where courseID is not null.
http://sqlfiddle.com/#!18/cf48b/1/0
Ok so, through some trial and error and thoroughly reading through the data. I've come to a solution that I believe to be correct,
INSERT INTO [College].[DepartmentInstructor]
(DepartmentInstructorID, DepartmentKey, InstructorKey)
SELECT
NEXT VALUE FOR [Project3].[SequenceObjectForDepartmentInstructorId],
DepartmentID,
InstructorID
FROM (
SELECT DISTINCT InstructorID, DepartmentID
FROM Uploadfile.CoursesSpring2019 AS CS
INNER JOIN [College].[Instructor] AS I
ON CS.Instructor = I.InstructorFullName
INNER JOIN [College].[Department] AS D
ON CS.[Course (hr, crd)] LIKE CONCAT('%', D.DepartmentName, '%')
) AS Result
I've been able to progress further in my project and I'm about 95% done. I've actually stumbled onto a somewhat similar problem. If you refer back to the database design that I posted, the courses table will need the IDs from multiple tables. This is what I've come up with,
SELECT DISTINCT
TS.TimeSlotID,
I.InstructorID,
BL.BuildingLocationID,
C.CourseID
FROM Uploadfile.CoursesSpring2019 AS CS
INNER JOIN [College].[TimeSlot] AS TS
ON CS.[Time] = TS.[ClassHours]
INNER JOIN [College].[Instructor] AS I
ON CS.[Instructor] = I.[InstructorFullName]
INNER JOIN [College].[BuildingLocation] AS BL
ON CS.[Location] LIKE CONCAT( BL.[BuildingName], '%')
INNER JOIN [College].[Course] AS C
ON CS.[Course (hr, crd)] LIKE CONCAT(C.CourseName, '%')
The problem here is that this query results in approximately 1mil rows. Table X has approximately 4700 rows which means that this query that I currently have is nowhere near the number of rows I should have since.
The lastReports subquery by itself, returns 10 rows. The "wrapper" records do exist, but when I join them, I get 0 rows returned in 12ms from: This is on SQLite.
My joins is good, I'm not making a selection mistake mistake(SQL LEFT JOIN Subquery Alias)... what am I missing, or not understanding?
SELECT
name
FROM
teachers
INNER JOIN (SELECT
teacher_id,
reportingPeriod,
ReportingType,
date('now') - 3 as AgeOfReport,
count(id) as NumberOf
FROM
reports
GROUP BY
teacher_id
ORDER BY
teacher_id ASC,
reportingPeriod asc
) AS lastReports
ON teachers.id = lastReports.teacher_id;
SAMPLE DATA
TEACHERS
ID NAME
-----------------
1 Mr John Smith
2 Ms Janet Smith
REPORTS
---------------------------------------------------
ID TEACHER_ID REPORTINGPERIOD REPORTINGTYPE
1 1 Jan 2017 Draft
2 1 Feb 2017 Draft
3 2 Jun 2018 Draft
4 2 Jul 2018 Draft
5 1 Mar 2017 Final
DESIRED RESULTS
------------------
Mr John Smith Final Mar 2017
Ms Janet Smith Draft Jul 2018
You can try to make row_number by TEACHER_ID column and order by REPORTINGPERIOD, which mean closer the date in subquery, get rn = 1 the data in each TEACHER_ID the max date and JOIN on TEACHERS.
CREATE TABLE TEACHERS(
ID INT,
NAME VARCHAR(50)
);
INSERT INTO TEACHERS VALUES (1, 'Mr John Smith');
INSERT INTO TEACHERS VALUES (2, 'Ms Janet Smith');
CREATE TABLE REPORTS(
ID INT,
TEACHER_ID int,
REPORTINGPERIOD DATE,
REPORTINGTYPE varchar(100)
);
INSERT INTO REPORTS VALUES (1,1,'Jan 2017', 'Draft');
INSERT INTO REPORTS VALUES (2,1,'Feb 2017', 'Draft');
INSERT INTO REPORTS VALUES (3,2,'Jun 2018', 'Draft');
INSERT INTO REPORTS VALUES (4,2,'Jul 2018', 'Draft');
INSERT INTO REPORTS VALUES (5,1,'Mar 2017', 'Final');
Query 1:
SELECT t.NAME,
t1.REPORTINGTYPE,
t1.REPORTINGPERIOD
FROM teachers as t INNER JOIN
(
SELECT *,(SELECT COUNT(*) FROM REPORTS tt WHERE tt.TEACHER_ID = t1.TEACHER_ID and tt.REPORTINGPERIOD>=t1.REPORTINGPERIOD) rn
FROM REPORTS t1
) as t1 on t1.TEACHER_ID = t.id and rn = 1
ORDER BY t.NAME
Results:
| t.NAME | t1.REPORTINGTYPE | t1.REPORTINGPERIOD |
|----------------|------------------|--------------------|
| Mr John Smith | Final | Mar 2017 |
| Ms Janet Smith | Draft | Jun 2018 |
The REPORTINGPERIOD values do not compare correctly, because Jul comes before Jun in the alphabet. You should use a format like yyyy-mm where the most significant field comes first.
In SQLite, you can simply use MAX() to select entire rows:
SELECT t.Name,
r.ReportingType,
max(r.ReportingPeriod)
FROM Teachers t
JOIN Reports r ON t.ID = r.Teacher_ID
GROUP BY r.Teacher_ID;
NAME REPORTINGTYPE max(r.ReportingPeriod)
-------------- ------------- ----------------------
Mr John Smith Final 2017-03
Ms Janet Smith Draft 2018-07
Using SQL Server 2008
I have a table A which has start date, end date and value. For each date within the start date and end date in Table A, I need to insert (or update if already exists) that date in table B such that the value in this table is value in A/DateDiff(Day,StartDate of A,EndDate of A).
Example:
Table A
ID StartDate EndDate Value
1 01 Jan 2014 03 Jan 2014 33
2 01 Feb 2014 02 Feb 2014 20
3 02 Jan 2014 03 Jan 2014 10
Table B
ID Date Value
1 01 Jan 2014 11
2 02 Jan 2014 16
3 03 Jan 2014 16
4 01 Feb 2014 10
5 02 Feb 2014 10
The way values are computed are - For ID 1, there are 3 days which means 11 units per day. So 1st, 2nd, 3rd Jan all get 11 units. Then because there are additional units with date range 2nd Jan to 3rd Jan which amount to 5 units per day, 2nd and 3rd Jan will be (11+5) 16. 1st and 2nd Feb just have one record so they will simply be 20/2 = 10.
I can think of a solution using loops, but want to avoid it entirely.
Is there any way I can achieve this through a set based solution? It is important for me to do this in bulk using set based approach.
I am trying to read through various articles and seems like CTE, Calendar Table or Tally Table might help but the examples I have seen require setting variables and passing start date and end date which I think will work for single record but not when doing all records at a time. Please suggest.
Thanks!
I think this should do it (DEMO):
;with cte as (
select
id
,startdate
,enddate
,value / (1+datediff(day, startdate, enddate)) as value
,startdate as date
from units
union all
select id, startdate, enddate, value, date+1 as date
from cte
where date < enddate
)
select
row_number() over (order by date) as ID
,date
,sum(value) as value
from cte
group by date
The idea is to use a Recursive CTE to explode the date ranges into one record per day. Also, the logic of value / (1+datediff(day, startdate, enddate)) distributes the total value evenly over the number of days in each range. Finally, we group by day and sum together all the values corresponding to that day to get the output:
| ID | DATE | VALUE |
|----|---------------------------------|-------|
| 1 | January, 01 2014 00:00:00+0000 | 11 |
| 2 | January, 02 2014 00:00:00+0000 | 16 |
| 3 | January, 03 2014 00:00:00+0000 | 16 |
| 4 | February, 01 2014 00:00:00+0000 | 10 |
| 5 | February, 02 2014 00:00:00+0000 | 10 |
From here you can join with your result table (Table B) by date, and update/insert the value as needed. That logic might look something like this (test it first of course before running in production!):
update B set B.VALUE = R.VALUE from TableB B join Result R on B.DATE = R.DATE
insert TableB (DATE, VALUE)
select DATE, VALUE from Result R where R.DATE not in (select DATE from TableB)
I want to know how I can write a SQL query to get the count of users who participated in each month survey
The table structures are like below
SurveyTable
SurveyId, SurveyName, Datetime
SurveyInfo
Id, SurveyId, First name, Last name DateTime
Sample Data
SurveyTable
1 Survey-ABC 1 Jan 2011
2 Survey-AXC 1 Feb 2011
3 Survey-AEW 1 Mar 2011
4 Survey-AVD 1 Apr 2011
....
SurveyInfo
1 1 Peter James 1 Jan 2011
2 1 John Tom 1 Jan 2011
3 1 Harry Kin 1 Jan 2011
4 2 Amber Wills 1 Feb 2011
5 2 Tom Kin 1 Feb 2011
I want a result like
3 users participated in Survey-ABC,
2 users participated in Survey-AXC
Thanks
SELECT
Count(*) AS "Number of participants",
SurveyTable.surveyName
FROM
surveyinfo
INNER JOIN surveytable
ON surveytable.surveyid = surveyinfo.surveyid
GROUP BY
Year(DateTime),
Month(Date_Time)
SELECT stab.Name, COUNT(si.ID)
FROM SurveyTable as stab INNER JOIN SurveyInfo si ON si.SurveyId = stab.SurveyId
GROUP BY stab.Name
Because you want to see the number or participant for each month, so you need to extract the month and group by this.
SELECT SurveyTable.SurveyId, SurveyTable.SurveyName, Month(SurveyInfo.DateTime) As M, COUNT(SurveyInfo.*) As Cnt
FROM SurveyTable
LEFT JOIN SurveyInfo ON SurveyInfo.SurveyId = SurveyTable.SurveyId
GROUP By SurveyTable.SurveyId, SurveyTable.SurveyName, Month(SurveyInfo.DateTime)
The following select statement
SELECT st.Name, COUNT(si.ID)
FROM SurveyTable.st INNER JOIN SurveyInfo si ON si.SurveyId = st.SurveyId
GROUP BY st.Name
should provide you with the users who participated in the surveys.
Name ExactDate Presents
bob 2011 1
bob 2008 2
bob 2012 3
mary 1986 4
mary 2001 5
mary 2012 6
kate 2011 7
kate 2012 8
kate 2013 9
celia 2011 10
celia 1986 11
celia 1972 12
celia 2012 13
celia 1991 14
So the goal is we subtract the amount of presents kate got from celia on the same day and out put should be something like the following:
Name ExactDate Presents
celiaminuskate 2011 3
celiaminuskate 2012 5
Thank you so much I am a first time user of access and SQL . and have a data management task required of me at work. so this has really go me stuck
This is done in ms access 2003 SQL
HEY SO I GOT THE FIRST PART THERE AND NOW IT GETS HARDER ASSUMING THE DATA SET IS NOW LIKE THIS
Name ExactDate Presents Location
bob 2011 1 home
bob 2008 2 school
bob 2012 3 school
mary 1986 4 school
mary 2001 5 home
mary 2012 6 homw
kate 2011 7 home
kate 2012 8 home
kate 2011 9 school
celia 2011 10 school
celia 1986 11 school
celia 1972 12 home
celia 2012 14 home
celia 2012 13 school
So the goal is we subtract the amount of presents kate got from celia on the same year ( but since there are a few different present values for the same year we choose to have priority of home > school....for example celia and kate both recieves presents in 2012 but celia gets both home presents and school presents in 2012 in which case we choose her home present value to do the calculation) and out put should be something like the following:
Name ExactDate Presents
celiaminuskate 2011 3
celiaminuskate 2012 6
This should do it:
SELECT
'celiaminuskate' AS [NAME],
T1.[date] AS [EXACT DATE],
T1.presents - T2.presents AS [PRESENTS DIFF]
FROM
Some_Table T1
INNER JOIN Some_Table T2 ON
T2.[name] = 'kate' AND
T2.[date] = T1.[date]
WHERE
T1.[name] = 'celia'
ORDER BY
T1.[date]
A couple of suggestions since you're new to SQL:
Try to avoid using keywords, like "date" for column names
Your "date" column looks like a year, not a date. It should be named appropriately and you should make sure that it's the correct data type.
Since it sounds like the version of Access that you're using doesn't support the now standard JOIN syntax, here is another query which should be equivalent:
SELECT
'celiaminuskate' AS [NAME],
T1.[date] AS [EXACT DATE],
T1.presents - T2.presents AS [PRESENTS DIFF]
FROM
Some_Table T1, Some_Table T2
WHERE
T1.[name] = 'celia' AND
T2.[name] = 'kate' AND
T2.[date] = T1.[date]
ORDER BY
T1.[date]
T1 and T2 in this query are simply aliases for the tables in the FROM clause so that you can distinguish them.
If you're trying to put the results into another table then you will need this to be part of an INSERT statement. Or, with Access you might be able to use a query to generate a table as part of some wizard. I'm afraid that I don't have a copy handy to give more specifics. In any case, here is what the INSERT statement would look like:
INSERT INTO Some_New_Table (name, exactdate, presentsdiff)
SELECT ...
(The ellipsis just means to use the query as I have it up above)
You will need to do a self-join, because you are comparing rows from the same table.
Below we join table1 to itself using simular dates, but different names.
A more realistic answer would use unique row_ids instead of names.
Next we tell the database that we want only celia's rows in part1 and only kate's rows in part2.
SELECT 'celiaminuskate' AS useless_filler
, a.[date] AS whendidthishappen
, (celia.presents - kate.presents) AS outcome
FROM table1 AS kate
INNER JOIN table1 AS celia ON (a.[date] = b.[date] and a.name <> b.name)
WHERE celia.name = 'celia' and kate.name = 'kate'
Note that date is a reserved word and you will need to enclose it in square brackets []
The key to answering this is a self-join:
SELECT 'Celia - Kate' AS tag, C.ExactDate, C.Presents - K.Presents
FROM (SELECT ExactDate, Presents FROM AnonymousTable WHERE Name = 'celia') AS C
JOIN (SELECT ExactDate, Presents FROM AnonymousTable WHERE Name = 'kate') AS K
ON C.ExactDate = K.ExactDate
ORDER BY C.Date;
SELECT 'celiaminuskate', p1.exactdate, p1.presents - p2.presents
FROM presents p1 JOIN presents p2 ON p1.exactdate = p2.exactdate
WHERE p1.name = 'celia' AND p2.name = 'kate'