T-SQL multiple datetime diff on one line - sql

Using SQL Server (T-SQL), and I have two tables:
tblTrial:
TrialID (PK) int
TrialDate
...
tblLaps:
LapID (PK) int
TrialID (FK) int
LapNumber int
LapStart smalldatetime
...
For TrialID = 1, there are four Lap rows:
LapID TrialID LapNumber LapStart
1 1 1 t1 (some smalldatetime value)
2 1 2 t2
3 1 3 t3
4 1 4 t4
I want to display the SQL so that for each Trial, only one row is displayed, and it has the time differences.
For example, a row for TrialID = 1 might look like:
Trial# 1stLap 2ndLap 3rdLap
---------------------------------
1 3min 4min 5min
where 1stLap is time difference t2-t1, 2ndLap is t3-t2, 3rdLap is t4-t3.
How do I make everything go on one line in a SQL statement?
Thanks

select
t.TrialID
, datediff(minute, max(case when LapNumber = 1 then LapStart end) , max(case when LapNumber = 2 then LapStart end) ) lap1_2
, datediff(minute, max(case when LapNumber = 2 then LapStart end) , max(case when LapNumber = 3 then LapStart end) ) lap2_3
, datediff(minute, max(case when LapNumber = 3 then LapStart end) , max(case when LapNumber = 4 then LapStart end) ) lap3_4
from tblTrial as t
inner join tblLaps as l on t.TrialID = l.TrialID
group by
t.TrialID
see this sqlfiddle demo

For time difference in minutes, you could do like this:
with laps as -- First CTE table to join every lap with the next lap to get end time
(
select TrialID,t1.LapID, datediff(mi,t1.LapStart, t2.LapStart ) as Lap
from tblLaps t1
join tblLaps t2
on t1.LapID = t2.LapID - 1 and t1.TrialID = t2.TrialID
)
select TrialID,
max(case t1.LapID when 1 then Lap else null end) as [1stLap],
max(case t1.LapID when 2 then Lap else null end) as [2ndLap],
max(case t1.LapID when 3 then Lap else null end) as [3rdLap],
from laps
group by TrialID
If you want differ by seconds, use datediff(ss,startdate , enddate ), here's the datediff document.

Related

How could I adapt this query to work over multiple years?

This query pulls data from a VistaDB and produces info on the number of courses started in each month of the year from people in different countries.
Select c.CountryName As Country,
Count (case When Month( ch.CourseStarted ) = 1 Then 1 End) As Jan19,
Count (case when Month(ch.CourseStarted ) = 2 Then 1 End) as Feb19,
Count (case When Month(ch.CourseStarted ) = 3 Then 1 End) as Mar19,
Count (case When Month(ch.CourseStarted ) = 4 Then 1 End) as Apr19,
Count (case When Month(ch.CourseStarted ) = 5 Then 1 End) as May19,
Count (case When Month(ch.CourseStarted ) = 6 Then 1 End) as Jun19,
Count (case When Month(ch.CourseStarted ) = 7 Then 1 End) as Jul19,
Count (case When Month(ch.CourseStarted ) = 8 Then 1 End) as Aug19,
Count (case When Month(ch.CourseStarted ) = 9 Then 1 End) as Sep19,
Count (case When Month(ch.CourseStarted ) = 10 Then 1 End) as Oct19,
Count (case When Month(ch.CourseStarted ) = 11 Then 1 End)as Nov19,
Count (case When Month(ch.CourseStarted ) = 12 Then 1 End) as Dec19
From Country As c
Inner Join CourseHistory As ch On c.Oid = ch.Country
Where (ch.CourseStarted >= '2019-01-01' And
ch.CourseStarted <= '2019-12-31')
Group By c.CountryName
Order by c.CountryName;
My question is would it be possible to make this semi-dynamic so that if I were to make the final date in the where clause '2022-12-31' I could get a rafft of colums for each month of each year?

TSQL Pivot table rows to columns

I've got some data in a table that looks like the following. I'm trying to run a query that will get my data on a single row per requestId. I don't need the dates or the denial reason just the eprojman and apvStatus for each groupId
requestId - projMan1 - apvStatus1 - projMan2 - apvStatus2 - projMan3 - apvStatus3 etc.. for all 5 groupIds
requestId
groupId
entryDate
approvalDate
apvStatus
projMan
denialReason
1
1
2020-11-02
2019-07-25
APPROVED
rx1942
NULL
1
2
2020-11-02
2019-07-25
APPROVED
ma2674
NULL
1
3
2020-11-02
2019-07-25
APPROVED
cb9097
NULL
1
4
2020-11-02
2019-07-25
APPROVED
bj1763
NULL
1
5
2020-11-02
2019-07-25
APPROVED
tr5972
NULL
2
1
2020-11-02
NULL
NOT APPROVED
NULL
6
2
2
2020-11-02
NULL
PENDING
ma2674
NULL
2
3
2020-11-02
NULL
PENDING
cb9097
NULL
2
4
2020-11-02
NULL
PENDING
bj1763
NULL
2
5
2020-11-02
NULL
PENDING
tr5972
NULL
I've been trying to use a PIVOT table but all the examples I find involves summing data or something. I just pretty much want to take the 5 rows and turn it into 1 for each requestID
The only thing I've been able to come up with is to select from the same table 5 times for each groupID and union it but that's slower than heck. Got to be a better way
Thanks.
Current query:
select group1.requestId
, group1.apvStatus as apvStatus1
, group1.projMan as projMan1
, group2.apvStatus as apvStatus2
, group2.projMan as projMan2
, group3.apvStatus as apvStatus3
, group3.projMan as projMan3
,group4.apvStatus as apvStatus4
, group4.projMan as projMan4
,group5.apvStatus as apvStatus5
, group5.projMan as projMan5
,group1.denialReason
INTO #TEMPBAOrganized
from (
select requestId, apvStatus, projMan, denialReason from #TEMPBULKAPPROVAL where groupId = 1) group1
INNER JOIN
(select requestId, apvStatus, projMan, denialReason from #TEMPBULKAPPROVAL where groupId = 2) group2
on group1.requestId = group2.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 3) group3
on group1.requestId = group3.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 4) group4
on group1.requestId = group4.requestId
INNER JOIN
(select requestId, apvStatus, projMan from #TEMPBULKAPPROVAL where groupId = 5) group5
on group1.requestId = group5.requestId
For pivoting of multiple column, it is easier to use CASE expression with aggregate.
select t.requestId,
projMan1 = max(case when t.groupId = 1 then t.projMan end),
apvStatus1 = max(case when t.groupId = 1 then t.apvStatus end),
projMan2 = max(case when t.groupId = 2 then t.projMan end),
apvStatus2 = max(case when t.groupId = 2 then t.apvStatus end),
projMan3 = max(case when t.groupId = 3 then t.projMan end),
apvStatus3 = max(case when t.groupId = 3 then t.apvStatus end),
projMan4 = max(case when t.groupId = 4 then t.projMan end),
apvStatus4 = max(case when t.groupId = 4 then t.apvStatus end),
projMan5 = max(case when t.groupId = 5 then t.projMan end),
apvStatus5 = max(case when t.groupId = 5 then t.apvStatus end)
from #TEMPBULKAPPROVAL t
group by t.requestId
Note : max is also an aggregate function
You can do it with something like this:
SELECT
requestId,
approvalDate_1 = MAX(approvalDate_1),
approvalDate_2 = MAX(approvalDate_2),
approvalDate_3 = MAX(approvalDate_3),
approvalDate_4 = MAX(approvalDate_4),
approvalDate_5 = MAX(approvalDate_5),
projMan_1 = MAX(projMan_1),
projMan_2 = MAX(projMan_2),
projMan_3 = MAX(projMan_3),
projMan_4 = MAX(projMan_4),
projMan_5 = MAX(projMan_5)
FROM
(
SELECT
requestId,
groupId,
approvalDate,
projMan,
'approvalDate_' + CAST( groupId AS VARCHAR(2)) AS approvalDatePivot,
'projMan_' + CAST( groupId AS VARCHAR(2)) AS projManPivot
FROM
#tbl
) T
PIVOT (
MAX(approvalDate) FOR approvalDatePivot IN ([approvalDate_1],[approvalDate_2],[approvalDate_3],[approvalDate_4],[approvalDate_5])
) pvt_1
PIVOT (
MAX(projMan) FOR projManPivot IN ([projMan_1],[projMan_2],[projMan_3],[projMan_4],[projMan_5])
) pvt_2
GROUP BY requestId

3 Query by column and create a new table

I have students, and these students have their meals, morning and evening. I want to print the number of meals each student eats in the morning and evening.
If the number of dishes that student eats in the morning is more than one, I want to print the number in the table and the ID of the food.
FoodType when 1(morning), and when 2(evening)
StudentId FoodId FoodType
3 1 1
3 2 1
3 3 1
3 4 2
4 3 1
4 1 2
4 2 2
4 4 2
5 4 2
5 1 1
6 1 1
6 2 1
6 3 2
6 4 2
Sample out;
StudentId MorningFoodCountOrId EveningFoodCountOrId
3 3 meals 4
4 3 3 meals
5 4 1
6 2 meals 2 meals
Use conditional aggregation. The logic that decides if we print the number of records or their value is not intuitive, but I would phrase it as follows:
select
studentId,
case when sum(case when foodtype = 1 then 1 else 0 end) = 1
then max(case when foodtype = 1 then foodId end)
else sum(case when foodtype = 1 then 1 else 0 end)
end MorningFoodCountOrId
case when sum(case when foodtype = 2 then 1 else 0 end) = 1
then max(case when foodtype = 2 then foodId end)
else sum(case when foodtype = 2 then 1 else 0 end)
end EveningFoodCountOrId
from mytable
group by studentId
You RDMBS should be able to optimize the query by not computing the conditional sums twice.
Note: you did not specify which RDMBS you are using. If this is MySQL, then it is possible to shorten the conditional sums a little, as follows:
select
studentId,
case when sum(foodtype = 1) = 1
then max(case when foodtype = 1 then foodId end)
else sum(foodtype = 1)
MorningFoodCountOrId
case when sum(foodtype = 2) = 1
then max(case when foodtype = 2 then foodId end)
else sum(foodtype = 2)
EveningFoodCountOrId
from mytable
group by studentId
Here's your query, select sum() and case.. will do this
select t1.studentid
, case when t1.m <= 1 then t2.FoodId else concat(t1.m, ' meals') end MorningFoodCountOrId
, case when t1.e <= 1 then t3.FoodId else concat(t1.e, ' meals') end EveningFoodCountOrId
from(
select studentid
, sum(case when FoodType = 1 then 1 else 0 end) as m
, sum(case when FoodType = 2 then 1 else 0 end) as e
from tableA
group by studentid) t1
left join tableA t2 on t2.studentId = t1.studentId and t1.m = 1 and t2.FoodType = 1
left join tableA t3 on t3.studentId = t1.studentId and t1.e = 1 and t3.FoodType = 2
order by t1.studentid
see dbfiddle
in postgresql
, case when t1.m <= 1 then t2.FoodId::text else concat(t1.m, ' meals') end MorningFoodCountOrId
, case when t1.e <= 1 then t3.FoodId::text else concat(t1.e, ' meals') end EveningFoodCountOrId

Full Join on two queries

I'm trying to do a full join on two SQL queries, below:
1st Query:
SELECT
ID
,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue
,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate
GROUP BY ID
2nd Query:
SELECT
ID
,SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New
,SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend
,SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2
WHERE Date2 >=#StartDate AND Date2<= #EndDate
GROUP BY ID
Result from query1
ID CountAll TValue
-------------------------
id1 24 1020
id2 13 2030
id3 4 120
Result from query 2:
ID New Amend Cancel
--------------------------------
id1 12 4 6
id2 7 6 1
id4 2 1 2
Needed output:
ID TValue CountAll New Amend Cancel Total(countall+new+amend+cancel)
----------------------------------------------------------------------------------------
Id1 1020 24 12 4 6 46
Id2 2030 13 7 6 1 27
id3 120 4 0 0 0 4
Id4 0 0 2 1 2 5
I'll post my current solution if requested, but it is pretty far from working.
I've been doing a bit of research and I think I need to either make a union to join the ID'S, or just do a Full Join. (Second day ever doing sql)
Try this,
SELECT *
FROM
(
SELECT ID ,
SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue,
COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate
GROUP BY ID
) a FULL JOIN
(
SELECT ID ,
SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New ,
SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend ,
SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2
WHERE Date2 >=#StartDate AND Date2<= #EndDate
GROUP BY ID
) b ON a.ID = b.ID
I would write something like below:
select decode (a.id, null, b.id, a.id) as ID, a.TValue, CountAll, b.new, b.Amend, b.cancel
from (SELECT ID ,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END)
AS TValue ,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate GROUP BY ID
) a FULL OUTER JOIN
(SELECT ID , SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END)
AS New ,SUM(CASE WHEN reason = 6
THEN 1 ELSE 0 END) AS Amend ,
SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2 WHERE Date2 >=#StartDate AND Date2<= #EndDate GROUP BY ID
) b
on a.id = b.id
have you tried this...
select isnull (a.id,b.id) as ID, a.TValue, CountAll, b.new, b.Amend, b.cancel
from (SELECT ID ,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue ,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate GROUP BY ID ) a
FULL OUTER JOIN (SELECT ID , SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New ,SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend , SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2 WHERE Date2 >=#StartDate AND Date2<= #EndDate GROUP BY ID ) b on a.id = b.id

Fetch data in a single Row

I have two tables PackageDetail and PackageDuration
PackageDuration have the PackageID as Foreign Key i.e. can have multiple records with respect to PackageID
The Schema of the PackageDetail is:
PackageID INT PK
PackageName Nvarchar(50)
Schema of the PackageDuration Table is:
DurationID INT PK
Price Money
Duration Nvarchar(50)
PackageID INT FPK
PackageDetail tables have follwoing records:
PackageID PackageName
1 TestPackage
2 MySecondPackage
PackageDuration table have following records:
DurationID PackageID Price Duration
1 1 100 6
2 1 200 12
3 1 300 24
4 2 500 6
PackageDuration table can have max 3 records with one PackageID not more than this(if have ignore that)
Now I want to select the Records as in following Way:
PackageId PackageNAme Price1 Price2 Price3 Duration1 Duration2 Duration3
1 TestPackage 100 200 300 6 12 24
2 MySecondPackage 500 null null 6 null null
Please suggest me how can I achive this.
Another approach:
WITH Durations AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY PackageId ORDER BY DurationId) Sequence
FROM PackageDuration
)
SELECT A.PackageId, B.PackageName,
MIN(CASE WHEN Sequence = 1 THEN Price ELSE NULL END) Price1,
MIN(CASE WHEN Sequence = 2 THEN Price ELSE NULL END) Price2,
MIN(CASE WHEN Sequence = 3 THEN Price ELSE NULL END) Price3,
MIN(CASE WHEN Sequence = 1 THEN Duration ELSE NULL END) Duration1,
MIN(CASE WHEN Sequence = 2 THEN Duration ELSE NULL END) Duration2,
MIN(CASE WHEN Sequence = 3 THEN Duration ELSE NULL END) Duration3
FROM Durations A
INNER JOIN PackageDetail B
ON A.PackageId = B.PackageId
GROUP BY A.PackageId, B.PackageName
This should work as long as the durations are unique for a package and they are either 6, 12, or 24.
SELECT
PackageDetail.PackageId, PackageDetail.PackageName,
D1.Price as Price1, D2.Price as Price2, D3.Price as Price3,
D1.Duration as Duration1, D2.Duration as Duration2, D3.Duration as Duration3
FROM PackageDetail
LEFT OUTER JOIN PackageDuration D1
ON D1.PackageId = PackageDetail.PackageId AND D1.Duration = 6
LEFT OUTER JOIN PackageDuration D2
ON D2.PackageId = PackageDetail.PackageId AND D2.Duration = 12
LEFT OUTER JOIN PackageDuration D3
ON D3.PackageId = PackageDetail.PackageId AND D3.Duration = 24
;WITH pvt AS
(
SELECT PackageID,
Price1 = MAX(CASE WHEN Duration = 6 THEN Price END),
Price2 = MAX(CASE WHEN Duration = 12 THEN Price END),
Price3 = MAX(CASE WHEN Duration = 24 THEN Price END),
Duration1 = MAX(CASE WHEN Duration = 6 THEN 6 END),
Duration2 = MAX(CASE WHEN Duration = 12 THEN 12 END),
Duration3 = MAX(CASE WHEN Duration = 24 THEN 24 END)
FROM dbo.PackageDuration
GROUP BY PackageID
)
SELECT
pvt.PackageID,
p.PackageName,
pvt.Price1, pvt.Price2, pvt.Price3,
pvt.Duration1, pvt.Duration2, pvt.Duration3
FROM
dbo.PackageDetail AS p
INNER JOIN
pvt ON p.PackageID = pvt.PackageID
ORDER BY p.PackageID;
Maybe I'm missing something in the requirements, but it seems like Sql Server's PIVOT is what you're looking for.
There are quite a few questions here at SO about PIVOT... Here's a good clean example with references to other questions: How do i transform rows into columns in sql server 2005
The big benefit of a pivot table over the other answers here is that it will scale out with no modifications if you add records to your PackageDuration table in the future.