SQL Server : multiple join query optimization - sql-server

I've just started with Microsoft SQL Server and I'm facing a problem, which I believe it is an sql optimization issue. Could you please take a look (see below) and give me your feedback.
I have two tables defined as follows:
floatTable (DateAndTime datetime2(7),
TagIndex smallint,
Val float)
stringTable (DateAndTime datetime2(7),
TagIndex smallint,
Val float)
The SQL query which I have used to get the RESULT is (don't laugh):
DECLARE #startDate DATETIME, #endDate DATETIME
SET #startDate = '20130312 9:00:00'
SET #endDate = '20130313 9:00:00'
USE TensionDB
SELECT t1.DateAndTime, t1.Val AS Winch_1,t2.Val AS Winch_2, t3.Val AS Winch_3, t4.Val AS Winch_4, t5.Val AS Winch_5,
t6.Val AS Winch_6, t7.Val AS Winch_7, t8.Val AS Winch_8, t9.Val AS Latitude, t10.Val AS Longitude
FROM
((SELECT DISTINCT DateAndTime ,Val FROM dbo.FloatTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 0)t1
INNER JOIN (SELECT DISTINCT DateAndTime,Val FROM dbo.FloatTable
WHERE ( DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 1)t2
ON t2.DateAndTime = t1.DateAndTime
INNER JOIN (SELECT DISTINCT DateAndTime,Val FROM dbo.FloatTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 2)t3
ON t3.DateAndTime = t1.DateAndTime
INNER JOIN (SELECT DISTINCT DateAndTime,Val FROM dbo.FloatTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 3)t4
ON t4.DateAndTime = t1.DateAndTime
INNER JOIN (SELECT DISTINCT DateAndTime,Val FROM dbo.FloatTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 4)t5
ON t5.DateAndTime = t1.DateAndTime
INNER JOIN (SELECT DISTINCT DateAndTime,Val FROM dbo.FloatTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 5)t6
ON t6.DateAndTime = t1.DateAndTime
INNER JOIN (SELECT DISTINCT DateAndTime,Val FROM dbo.FloatTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 6)t7
ON t7.DateAndTime = t1.DateAndTime
INNER JOIN (SELECT DISTINCT DateAndTime,Val FROM dbo.FloatTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 7)t8
ON t8.DateAndTime = t1.DateAndTime
INNER JOIN (SELECT DISTINCT DateAndTime, Val FROM dbo.StringTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 8)t9
ON t9.DateAndTime = t1.DateAndTime
INNER JOIN (SELECT DISTINCT DateAndTime, Val FROM dbo.StringTable
WHERE (DateAndTime BETWEEN #startDate AND #endDate) AND TagIndex = 9)t10
ON t10.DateAndTime = t1.DateAndTime)
PROBLEM: The big problem is that even if I get the correct result, the query gets very slow for large amount of data. I'm pretty sure that there is another way to write the query, but for the moment I don't have any other idea.
Could you give me a hint please? Appreciate any help from your side.
Thank you in advance
#Kiril and #Patrick,
Using your hints and ideas I have re-wrote my original query using pivot table.
Unfortunately, I still have to use INNER JOIN, as the values(Val) in the stringTable are strings and values(Val) in floatTable are floats. To be honest, I have to perform more tests with both queries, as I can't see a real improvement (time wise), using pivot table; apart from the length of the query. One last thing, I have embedded the query in a stored procedure. Please find below the "final" code:
-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters
-- command (Ctrl-Shift-M) to fill in the parameter
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: xxxx
-- Create date: xxxx
-- Description: retrieves tension data
-- =============================================
CREATE PROCEDURE getTension
-- Add the parameters for the stored procedure here
#startDate datetime = NULL,
#endDate datetime = NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT distinct pvt.DateAndTime
, pvt.[0] AS Winch_1
, pvt.[1] AS Winch_2
, pvt.[2] AS Winch_3
, pvt.[3] AS Winch_4
, pvt.[4] AS Winch_5
, pvt.[5] AS Winch_6
, pvt.[6] AS Winch_7
, pvt.[7] AS Winch_8
, st.Val AS Longitude
, st1.Val AS Latitude
FROM FloatTable
PIVOT
(MAX(Val)
FOR TagIndex in ([0],[1],[2],[3],[4],[5],[6],[7])
) as pvt
inner join StringTable st on st.DateAndTime = pvt.DateAndTime and st.TagIndex = 8
inner join StringTable st1 on st1.DateAndTime = pvt.DateAndTime and st1.TagIndex = 9
Where (pvt.DateAndTime between #startDate and #endDate)
ORDER BY DateAndTime
END
GO
Thanks again for your guidance

I suppose that using pivot table is going to be more efficient:
DECLARE #startDate DATETIME, #endDate DATETIME
SET #startDate = '20130312 9:00:00'
SET #endDate = '20130313 9:00:00'
SELECT st.DateAndTime
, pvt.0 AS Winch_1
, pvt.1 AS Winch_2
, pvt.2 AS Winch_3
, pvt.3 AS Winch_4
, pvt.4 AS Winch_5
, pvt.5 AS Winch_6
, pvt.6 AS Winch_7
, pvt.7 AS Winch_8
, pvt.8 AS Latitude
, pvt.9 AS Longitude
FROM StringTable st
INNER JOIN FloatTable ft on st.DateandTime=ft.DateandTime
pivot
( MAX(Val) for [TagIndex] in ([0], [1], [2], [3], [4], [5], [6], [7], [8], [9])
order by [st.DateAndTime]
)pvt
I haven't tested it so you may have to tweak it a bit, in order to make it work.

Start putting DateAndTime and TagIndex in an index. That would make a difference.
On the other hand, your query could get a lot faster if you wouldn't need the repeating inner joins.
Can you replace the distinct by a single group by and use min or max?
Another option is using pivot tables.
Like this:
select *
from FloatTable
pivot (min(DateAndTime) x, min(Val) y in ([1],[2],[3], ...))

Related

How to optimize below my SQL query shown here

This query is written for those users who did not log-in to the system between 1st July to 31 July.
However when we run the query in query analyzer then it's taking more than 2 minutes. But in application side giving error as 'Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding'.
Below query takes start date as 1st July 2022 and get all the users and add those users into temp table called '#TABLE_TEMP' and increases to next date.
Again while loop runs and fetch users for 2nd July and so on until it reaches to 31st July.
Can anyone help on this to optimize the query using CTE or any other mechanism?
H
ow can we avoid While loop for better performance?
DECLARE #TABLE_TEMP TABLE
(
Row int IDENTITY(1,1),
[UserId] int,
[UserName] nvarchar(100),
[StartDate] nvarchar(20),
[FirstLogin] nvarchar(20),
[LastLogout] nvarchar(20)
)
DECLARE #START_DATE datetime = '2022-07-01';
DECLARE #END_DATE datetime = '2022-07-31';
DECLARE #USER_ID nvarchar(max) = '1,2,3,4,5,6,7,8,9';
DECLARE #QUERY nvarchar(max) = '';
WHILE(#START_DATE < #END_DATE OR #START_DATE = #END_DATE)
BEGIN
SET #QUERY = 'SELECT
s.userid AS [UserId],
s.username AS [UserName],
''' + CAST(#START_DATE as nvarchar) + ''' AS [StartDate],
MAX(h.START_TIME) as [FirstLogin],
MAX(ISNULL(h.END_TIME, s.LAST_SEEN_TIME)) as [LastLogout]
FROM USER s
LEFT JOIN USER_LOGIN_HISTORY h ON h.userid = s.userid
LEFT JOIN TEMP_USER_INACTIVATION TUI ON TUI.userid = s.userid AND ('''+ CAST(#START_DATE as nvarchar) +''' BETWEEN ACTIVATED_DATE AND DEACTIVATD_DATE)
WHERE s.userid IN (' + #USER_ID + ')
AND h.userid NOT IN (SELECT userid FROM USER_LOGIN_HISTORY WHERE CAST(START_TIME AS DATE) = '''+ CONVERT(nvarchar,(CAST(#START_DATE AS DATE))) +''') AND ACTIVATED_DATE IS NOT NULL
GROUP BY s.userid, h.userid, s.username, s.last_seen_time
HAVING CAST(MAX(ISNULL(h.END_TIME, s.LAST_SEEN_TIME)) AS DATE) <> '''+ CONVERT(nvarchar,(CAST(#START_DATE AS DATE))) + '''
ORDER BY [User Name]'
INSERT INTO #TABLE_TEMP
EXEC(#QUERY)
SET #START_DATE = DATEADD(DD, 1, #START_DATE)
END
Without the query plan, it's hard to say for sure.
But there are some clear efficiencies to be had.
Firstly, there is no need for a WHILE loop. Create a Dates table which has every single date in it. Then you can simply join it.
Furthermore, do not inject the #USER_ID values. Instead, pass them thorugh as a Table Valued Parameter. At the least, split what you have now into a temp table or table variable.
Do not cast values you want to join on. For example, to check if START_TIME falls on a certain date, you can do WHERE START_TIME >= BeginningOfDate AND START_TIME < BeginningOfNextDate.
The LEFT JOINs are suspicious, especially given you are filtering on those tables in the WHERE.
Use NOT EXISTS instead of NOT IN or you could get incorrect results
DECLARE #START_DATE date = '2022-07-01';
DECLARE #END_DATE date = '2022-07-31';
DECLARE #USER_ID nvarchar(max) = '1,2,3,4,5,6,7,8,9';
DECLARE #userIds TABLE (userId int PRIMARY KEY);
INSERT #userIds (userId)
SELECT CAST(value AS int)
FROM STRING_SPLIT(#USER_ID, ',');
SELECT
s.userid as [UserId],
s.username as [UserName],
d.Date as [StartDate],
MAX(h.START_TIME) as [FirstLogin],
MAX(ISNULL(h.END_TIME, s.LAST_SEEN_TIME)) as [LastLogout]
FROM Dates d
JOIN USER s
LEFT JOIN USER_LOGIN_HISTORY h ON h.userid = s.userid
LEFT JOIN TEMP_USER_INACTIVATION TUI
ON TUI.userid = s.userid
ON d.Date BETWEEN ACTIVATED_DATE AND DEACTIVATD_DATE -- specify table alias (don't know which?)
WHERE s.userid in (SELECT u.userId FROM #userIds u)
AND NOT EXISTS (SELECT 1
FROM USER_LOGIN_HISTORY ulh
WHERE ulh.START_TIME >= CAST(d.date AS datetime)
AND ulh.START_TIME < CAST(DATEADD(day, 1, d.date) AS datetime)
AND ulh.userid = h.userid
)
AND ACTIVATED_DATE IS NOT NULL
AND d.Date BETWEEN #START_DATE AND #END_DATE
GROUP BY
d.Date,
s.userid,
s.username,
s.last_seen_time
HAVING CAST(MAX(ISNULL(h.END_TIME, s.LAST_SEEN_TIME)) AS DATE) <> d.date
ORDER BY -- do you need this? remove if possible.
s.username;
Better to collect dates in a table rather than running query in a loop. Use following query to collect dates between given date range:
DECLARE #day INT= 1
DECLARE #dates TABLE(datDate DATE)
--creates dates table first and then create dates for the given month.
WHILE ISDATE('2022-8-' + CAST(#day AS VARCHAR)) = 1
BEGIN
INSERT INTO #dates
VALUES (DATEFROMPARTS(2022, 8, #day))
SET #day = #day + 1
END
Then to get all dates where user did not login, you have to use Cartesian join and left join as illustrated below
SELECT allDates.userID,
allDates.userName,
allDates.datDate notLoggedOn
FROM
(
--This will reutrun all users for all dates in a month i.e. 31 rows for august for every user
SELECT *
FROM Users,
#dates
) allDates
LEFT JOIN
(
--now get last login date for every user between given date range
SELECT userID,
MAX(login_date) last_Login_date
FROM USER_LOGIN_HISTORY
WHERE login_date BETWEEN '2022-08-01' AND '2022-08-31'
GROUP BY userID
) loggedDates ON loggedDates.last_Login_date = allDates.datDate
WHERE loggedDates.last_Login_date IS NULL --filter out only those users who have not logged in
ORDER BY allDates.userID,
allDates.datDate
From this query you will get every day of month when a user did not logged in.
If there is no need to list every single date when user did not log in, then Cartesian join can be omitted. This will further improve the performance.
I hope this will help.

Can I "left join" days between 2 dates in sql server?

There is a table in SQL Server where data is entered day by day. In this table, data is not filled in some days.
Therefore, there are no records in the table.
Sample: dataTable
I need to generate a report like the one below from this table.
Create a table with all the days of the year. I know that I can output a report by "joining" the "dataTable" table.
But this solution seems a bit strange to me.
Is there another way?
the code i use for temp date table
CREATE TABLE tempDate (
calendarDate date,
PRIMARY KEY (calendarDate)
)
DECLARE
#start DATE= '2021-01-01',
#dateCount INT= 730,
#rowNumber INT=1
WHILE (#rowNumber < #dateCount)
BEGIN
INSERT INTO tempDate values (DATEADD(DAY, #rowNumber, #start))
set #rowNumber=#rowNumber+1
END
GO
select * from tempDate
This is how I join using this table
SELECT
*
FROM
tempDate td WITH (NOLOCK)
LEFT JOIN dataTable dt WITH (NOLOCK) ON dt.reportDate = td.calendarDate
WHERE
td.calendarDate BETWEEN '2021-09-05' AND '2021-09-15'
Create a table with all the days of the year. I know that I can output a report by "joining" the "dataTable" table.
This is the way. You can generate that "table" on the fly if you really want to, but normally the best way is to simply have a calendar table.
You can use common expression tables for dates. The code you need:
IF(OBJECT_ID('tempdb..#t') IS NOT NULL)
BEGIN
DROP TABLE #t
END
CREATE TABLE #t
(
id int,
dt date,
dsc varchar(100),
)
INSERT INTO #t
VALUES
(1, '2021.09.08', 'a'),
(1, '2021.09.09', 'b'),
(1, '2021.09.12', 'c')
DECLARE #minDate AS DATE
SET #minDate = (SELECT MIN(dt) FROM #t)
DECLARE #maxDate AS DATE
SET #maxDate = (SELECT MAX(dt) FROM #t)
;WITH cte
AS
(
SELECT #minDate AS [dt]
UNION ALL
SELECT DATEADD(DAY, 1, [dt])
FROM cte
WHERE DATEADD(DAY, 1, [dt])<=#maxDate
)
SELECT
ISNULL(CAST(t.id AS VARCHAR(10)), '') AS [id],
cte.dt AS [dt],
ISNULL(t.dsc, 'No record has been entered in the table.') AS [dsc]
FROM
cte
LEFT JOIN #t t on t.dt=cte.dt
The fastest method is to use a numbers table, you can get a date list between 2 dates with that:
DECLARE #Date1 DATE, #Date2 DATE
SET #Date1 = '20200528'
SET #Date2 = '20200625'
SELECT DATEADD(DAY,number+1,#Date1) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2
If you go go in LEFT JOIN this select, whit your table, you have the result that you want.
SELECT *
FROM (SELECT DATEADD(DAY,number+1,#Date1) [Date]
FROM master..spt_values WITH (NOLOCK)
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2 ) as a
LEFT JOIN yourTable dt WITH (NOLOCK) ON a.date = dt.reportDate
WHERE td.[Date] BETWEEN '2021-09-05' AND '2021-09-15'

Using date parameter in SQL Server CTE

I'm trying to use a start and end date parameter in a T-SQL common table expression. I'm very new to SQL Server development and I'm unsure of what I'm missing in the query.
I can specify values for #startdate & #enddate and get correct results.
However, I'm trying to figure out how to make the two parameters open so a user can specify start and end date values. The query will be used in an SSRS report.
DECLARE #startdate Datetime,
#enddate Datetime;
SET #startdate = '2017-02-09';
SET #enddate = '2017-02-10';
WITH ManHours AS
(
SELECT DISTINCT
a.plant_name AS Plant, SUM(tc.total_hr) AS TotalHours
FROM
area AS a
INNER JOIN
tf_department AS dep ON a.plant_id = dep.plant_id
INNER JOIN
tf_timecard AS tc ON dep.department_id = tc.department_id
WHERE
tc.timecard_dt BETWEEN #startdate AND #enddate
AND tc.department_id IN (266, 453, ...endlessly long list of IDs......)
AND tc.hourtype_id = 1
GROUP BY
a.plant_name),
Tonnage AS
(
SELECT DISTINCT
a.plant_name AS Plant, SUM(tglt.postqty) AS TotalTonnage
FROM
area AS a
INNER JOIN
plantgl AS pgl ON a.plant_id = pgl.plant_id
INNER JOIN
tgltransaction AS tglt ON pgl.glacckey = tglt.glacctkey
WHERE
tglt.postdate BETWEEN #startdate AND #enddate
GROUP BY
a.plant_name
)
SELECT DISTINCT
ManHours.Plant,
SUM(TotalTonnage) as 'Production Tons' ,
SUM(TotalHours) as 'Man Hours',
TotalTonnage / TotalHours AS TonsPerManHour
FROM
ManHours
LEFT OUTER JOIN
Tonnage ON ManHours.Plant = tonnage.Plant
GROUP BY
ManHours.Plant, ManHours.TotalHours, Tonnage.TotalTonnage
Below is an example of a stored procedure you could use. In addition, I provided two alternatives to the "endlessly long list of IDs" that is specified in the CTE. In my opinion, it is optimal to pull this logic out of the query and place it at the beginning of the stored procedure. This will enable you, or others, to easily go back and modify this list if / when it changes. Even better, I provided a TABLE VARIABLE (#ListOfDeptIdsFromTable) that you can use to actually retrieve this data as opposed to hard-coding a string.
CREATE PROCEDURE Report
#startdate DATETIME,
#enddate DATETIME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ListOfDeptIds VARCHAR(MAX) = '266, 453, ...endlessly long list of IDs......';
DECLARE #ListOfDeptIdsFromTable TABLE
(
department_id INT
)
INSERT INTO #ListOfDeptIdsFromTable (department_id)
SELECT department_id
FROM -- Table here
WHERE -- Where credentials to retrieve the long list
WITH ManHours AS
(
SELECT DISTINCT
a.plant_name AS Plant, SUM(tc.total_hr) AS TotalHours
FROM
area AS a
INNER JOIN
tf_department AS dep ON a.plant_id = dep.plant_id
INNER JOIN
tf_timecard AS tc ON dep.department_id = tc.department_id
WHERE
tc.timecard_dt BETWEEN #startdate AND #enddate
AND tc.department_id IN (#ListOfDeptIds) -- or ... IN (SELECT department_id FROM #ListOfDeptIdsFromTable)
AND tc.hourtype_id = 1
GROUP BY
a.plant_name),
Tonnage AS
(
SELECT DISTINCT
a.plant_name AS Plant, SUM(tglt.postqty) AS TotalTonnage
FROM
area AS a
INNER JOIN
plantgl AS pgl ON a.plant_id = pgl.plant_id
INNER JOIN
tgltransaction AS tglt ON pgl.glacckey = tglt.glacctkey
WHERE
tglt.postdate BETWEEN #startdate AND #enddate
GROUP BY
a.plant_name
)
SELECT DISTINCT
ManHours.Plant,
SUM(TotalTonnage) as 'Production Tons' ,
SUM(TotalHours) as 'Man Hours',
TotalTonnage / TotalHours AS TonsPerManHour
FROM
ManHours
LEFT OUTER JOIN
Tonnage ON ManHours.Plant = tonnage.Plant
GROUP BY
ManHours.Plant, ManHours.TotalHours, Tonnage.TotalTonnage
END
GO

Searching date between two given Dates

I have two specific dates.
lets say from 2014-04-04 To 2015-10-04.
I have a plan that says classes of Blah course will be on mon, wed and Fri.
Now I want to get the dates of each mon, wed and Fri from 2014-04-04 to 2015-10-04.
With a calendar table it is simple:
SELECT t.*
FROM dbo.TableName t
INNER JOIN CalendarTable c
ON t.DateColumn = c.Date
WHERE c.Date between '2014-04-04' AND '2015-10-04'
AND DATEPART(dw, c.Date) IN (1,3,5)
How to generate a calendar table (look for "Calendar table").
The standard reply to this by any seasoned SQL Server veteran, would be to create a calendar table. But all too often this is scoffed. So here's a slow and out-of-the-box method:
DECLARE #startDate DATE = '2014-04-04', #endDate DATE = '2015-10-04';
WITH CTE(N) AS (SELECT 1 FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))a(N)),
CTE2(N) AS (SELECT 1 FROM CTE x CROSS JOIN CTE y),
CTE3(N) AS (SELECT 1 FROM CTE2 x CROSS JOIN CTE2 y),
CTE4(N) AS (SELECT 1 FROM CTE3 x CROSS JOIN CTE3 y),
CTE5(N) AS (SELECT 1 FROM CTE4 x CROSS JOIN CTE4 y),
CTE6(N) AS (SELECT 0 UNION ALL
SELECT TOP (DATEDIFF(day,#startDate,#endDate))
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM CTE5),
TALLY(N) AS (SELECT DATEADD(day, N, #startDate)
FROM CTE6
WHERE DATENAME(weekday,DATEADD(day, N, #startDate)) IN ('Monday','Tuesday','Wednesday'))
SELECT N
FROM TALLY
ORDER BY N;
You can use a Recursive CTE if you do not have numbers table. Something like this. Note that the DATEPART(weekday,Dates) IN(1,3,5) is based on your setting of SELECT ##DATEFIRST.
For example If ##DATEFIRST is 1 then use DATEPART(weekday,Dates) IN(1,3,5)
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2014-04-04'
SET #EndDate = '2015-10-04'
;WITH CTE AS
(
SELECT #StartDate Dates
UNION ALL SELECT DATEADD(d,1,Dates) FROM CTE WHERE DATEADD(d,1,Dates) <=#EndDate
)
SELECT Dates,DATENAME(weekday,Dates) FROM CTE
WHERE DATEPART(weekday,Dates) IN(1,3,5)
OPTION (MAXRECURSION 0);
GO
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2014-04-04'
SET #EndDate = '2015-10-04'
;WITH CTE AS
(
SELECT #StartDate Dates
UNION ALL SELECT DATEADD(d,1,Dates) FROM CTE WHERE DATEADD(d,1,Dates) <=#EndDate
)
SELECT Dates,DATENAME(weekday,Dates) FROM CTE
WHERE DATEPART(weekday,Dates) IN(1,3,5)
OPTION (MAXRECURSION 0);
GO
--- thanks to #ughai
I've comeup with another way to do this.
this query will not only Show the dates in between but also it will compare the dates u want to compare with.
Q.Dates Comparison
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2014-12-22'
SET #EndDate = '2015-02-13'
drop table #TabCourseDetailID
SELECT ROW_NUMBER() OVER(ORDER BY CourseDetailID asc) AS Row,
CourseDetailID
into #TabCourseDetailID
from coursedetail
where UnitType='T' and courseID =1
Declare #counter as int
Declare #CourseDetailID varchar(500)
set #counter = 0
Declare #TempTable Table (FirstValue datetime,LastValue int)
while #StartDate <= #EndDate
begin
If DATEPART(WeekDay,#StartDate) IN (2,4,6)
begin
Set #Counter = #counter+1
Select #CoursedetailId=CoursedetailId
from #TabCourseDetailID
where row=#counter
insert into #TempTable (FirstValue,LastValue)
values (#StartDate,#CoursedetailId)
end
set #StartDate =DATEADD(d,1,#StartDate)
end
select t.LastValue,
t.FirstValue,
bp.conductedDate
from batchprogress bp
Join #TempTable t on (t.LastValue=bp.CoursedetailID)
where CourseID =1 and batchID =5
order by t.lastvalue

How to Combine a query with a AVG query when using datetime SQL 2008 R2

I am trying to combine two query's together but I'm having trouble as it is a datetime column. right now I have a query that finds all the users + there slowest and fastest "Segment" time but I also need to find the avg segment time below are the two query's I am using right now which work but I need it to be in one query for a report I am making.
Thanks
Thomas James
DECLARE
#vnuID int = 1212,
#StartDate DateTime = '30/10/2013',
#EndDate DateTime = '26/11/2013'
SELECT DISTINCT usrFullName, MIN(CAST(tblTrace.trFinish - tblTrace.trStart AS TIME)) AS FastestUserSegmentTime,
MAX(CAST(tblTrace.trFinish - tblTrace.trStart AS TIME)) AS SlowestUserSegmentTime
FROM tblTrace
INNER JOIN tblUsers ON usrID = tr_usrID
WHERE tr_vnuID = #vnuID AND trFinish IS NOT NULL AND tr_usrID IS NOT NULL AND trObjectType LIKE 'Segment%'
AND trStart BETWEEN #StartDate AND #EndDate
GROUP BY usrFullName
SELECT AVG(TotalTime) AS AvgUserSegmentTime
FROM (SELECT DateDiff(SECOND, tblTrace.trStart , tblTrace.trFinish) as 'TotalTime'
FROM tblTrace
WHERE tblTrace.trFinish IS NOT NULL AND tblTrace.trObjectType LIKE 'Segment%' AND tblTrace.tr_vnuID = #vnuID
AND tblTrace.trStart BETWEEN #StartDate AND #EndDate ) as SubQuery
Functionally (i.e. without optimization) you could use something like this:
DECLARE
#vnuID int = 1212,
#StartDate DateTime = '30/10/2013',
#EndDate DateTime = '26/11/2013'
SELECT usrFullName, FastestUserSegmentTime, SlowestUserSegmentTime,
(SELECT AVG(DateDiff(SECOND, tblTrace.trStart , tblTrace.trFinish))
FROM tblTrace
WHERE tblTrace.trFinish IS NOT NULL AND tblTrace.trObjectType LIKE 'Segment%' AND tblTrace.tr_vnuID = #vnuID
AND tblTrace.trStart BETWEEN #StartDate AND #EndDate) as AvgUserSegmentTime
FROM (
SELECT usrFullName,
MIN(CAST(tblTrace.trFinish - tblTrace.trStart AS TIME)) AS FastestUserSegmentTime,
MAX(CAST(tblTrace.trFinish - tblTrace.trStart AS TIME)) AS SlowestUserSegmentTime
FROM tblTrace
INNER JOIN tblUsers ON usrID = tr_usrID
WHERE tr_vnuID = #vnuID AND trFinish IS NOT NULL AND tr_usrID IS NOT NULL AND trObjectType LIKE 'Segment%'
AND trStart BETWEEN #StartDate AND #EndDate
GROUP BY usrFullName
) coreData
However, I would assume that you would want tr_usrID not to be null in all cases? In which case you could just use this:
SELECT usrFullName,
CONVERT(TIME, DATEADD(s, MIN(SegmentTime))) as FastestUserSegmentTime,
CONVERT(TIME, DATEADD(s, MAX(SegmentTime))) as SlowestUserSegmentTime,
AVG(SegmentTime) as AvgUserSegmentTime
FROM (
SELECT usrFullName,
DateDiff(SECOND, tblTrace.trStart, tblTrace.trFinish) as SegmentTime
FROM tblTrace INNER JOIN
tblUsers
ON usrID = tr_usrID
WHERE tr_vnuID = #vnuID
AND trFinish IS NOT NULL
AND tr_usrID IS NOT NULL
AND trObjectType LIKE 'Segment%'
AND trStart BETWEEN #StartDate AND #EndDate
) coreData

Resources