Join 2 queries with equal fields created dynamically - sql

I have a table that i wanted to create some charts.
Basically i need to know how many registers i have in interval of 10 minutes and how many of them are already pos-processed. With this 2 queries i have the info i need, but i need they 2 in just 1 result table.
First query
SELECT
(
CAST(DATEPART(HOUR, m.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, m.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Movimentações
FROM
Integracao m
WHERE
m.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND m.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
ORDER BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
Second query
SELECT
(
CAST(DATEPART(HOUR, s.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, s.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Sucesso
FROM
Integracao s
WHERE
s.Veiculo_Modelo <> ''
AND s.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND s.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, s.Ocr_DataHora),
(DATEPART(MINUTE, s.Ocr_DataHora) % 6)
How can i join them if my common field is generated dynamically?

As others have already told you, you could just use the two queries as derived tables (subselects) and join them using each one's Hora column. It doesn't matter if the column is computed in the query, it's perfectly legal to use it in a join condition.
However, I think you can get the same results more efficiently, by calculating both counts in a single query (i.e. without subqueries). Here's how you can do that:
SELECT
(
CAST(DATEPART(HOUR, m.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, m.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Movimentações,
COUNT(CASE WHEN s.Veiculo_Modelo <> '' THEN 1 END) AS Sucesso
FROM
Integracao m
WHERE
m.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND m.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
ORDER BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
As you can see, the query doesn't filter rows on s.Veiculo_Modelo <> ''. However, that condition is used when counting rows for the Sucesso column. The argument of the second COUNT is a CASE expression. It returns a value (arbitrarily chosen to be 1, but could be anything really) if the specified condition is met and NULL otherwise. Since COUNT omits nulls, the result will be the same as with your dedicated query calculating and returning Sucesso.

I am assuming that you want the output to be as follows:
Hora | Movimentações | Sucesso
You can use the two SQL queries as if they are table using the WITH statement as follows:
WITH
firstQuery(Hora, Movimentações) AS (<first query definition with out ORDER BY >),
secondQuery(Hora, Sucesso) AS (<second query definition with out ORDER BY >)
SELECT
f.Hora, f.Movimentações , s.Sucesso
FROM
firstQuery f
JOIN secondQuery s
ON f.Hora = s.Hora

You should be able to do this by putting them in a sub query in a from clause. The way I'm referring to has the following form:
Select A.Hora,A.Movimentações,B.Sucesso
From (*First Query*) as A
inner join (*Second Query*) as B on A.Hora = B.Hora
The end result being:
Select A.Hora,A.Movimentações,B.Sucesso
From (SELECT
(
CAST(DATEPART(HOUR, m.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, m.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Movimentações
FROM
Integracao m
WHERE
m.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND m.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)
ORDER BY
DATEPART(HOUR, m.Ocr_DataHora),
(DATEPART(MINUTE, m.Ocr_DataHora) % 6)) As A
inner join (SELECT
(
CAST(DATEPART(HOUR, s.Ocr_DataHora) AS NVARCHAR) +
':' +
CAST((DATEPART(MINUTE, s.Ocr_DataHora) % 6) AS NVARCHAR) +
'0'
) AS Hora,
COUNT(*) AS Sucesso
FROM
Integracao s
WHERE
s.Veiculo_Modelo <> ''
AND s.Ocr_DataHora >= '2012-09-17 00:00:00.000'
AND s.Ocr_DataHora < '2012-09-18 00:00:00.000'
GROUP BY
DATEPART(HOUR, s.Ocr_DataHora),
(DATEPART(MINUTE, s.Ocr_DataHora) % 6)) As B on B.Hora = A.Hora

Related

Convert SUM of integer to HH:MM format

I have time stored in minutes in integer datatype which has to be displayed in HH:MM format. For example: if total minutes is 80 then it should convert to 01:20.
select SUM(OTTime) from dbo.TableOT where ....
I have tried some queries but didn't get the exact output.
Updated Query:
SELECT SUM(t.OTTime),d.combovalue
FROM dbo.employee e
join dbo.OT t
on e.id = t.employeeid
JOIN dbo.combovalues d
ON e.department = d.id
GROUP By d.combovalue
Try this Single Query:
DECLARE #Duration int
SET #Duration= 4000
SELECT CAST( CAST((#Duration) AS int) / 60 AS varchar) + ':' + right('0' + CAST(CAST((#Duration) AS int) % 60 AS varchar(2)),2)
For Updated Query:
SELECT d.combovalue,CAST(CAST((SUM(t.OTTime)) AS int) / 60 AS varchar) + ':'
+ right('0' + CAST(CAST((SUM(t.OTTime)) AS int) % 60 AS varchar(2)),2)
FROM dbo.employee e join dbo.OT t on e.id = t.employeeid
JOIN dbo.combovalues d ON e.department = d.id
GROUP By d.combovalue
Instead of #minutes variable you can use SUM(OTTime) and a from clause in this below Query
DECLARE #minutes INT=80
SELECT CASE WHEN #minutes >= 60 THEN (SELECT CAST((#minutes / 60) AS VARCHAR(2)) + ':' + CASE WHEN (#minutes % 60) > 0 THEN CAST((#minutes % 60) AS VARCHAR(2))
ELSE '' END)
ELSE CAST((#minutes % 60) AS VARCHAR(2)) END
For Eg:
SELECT CASE WHEN SUM(OTTime) >= 60 THEN (SELECT CAST((SUM(OTTime) / 60) AS VARCHAR(2)) + ':' + CASE WHEN (SUM(OTTime) % 60) > 0 THEN CAST((SUM(OTTime) % 60) AS VARCHAR(2))
ELSE '' END)
ELSE CAST((SUM(OTTime) % 60) AS VARCHAR(2)) END
from dbo.TableOT where ....
Create a SCALAR Function and pass the
select dbo.Minutes_to_HrsMts (SUM(OTTime)) from dbo.TableOT where ....
Function:
CREATE Function dbo.Minutes_to_HrsMts (#minutes INT)
RETURNS nvarchar(30)
AS
BEGIN
declare #hours nvarchar(20)
SET #hours =
CASE WHEN #minutes >= 60 THEN
(SELECT CAST((#minutes / 60) AS VARCHAR(2)) + ':' +
CASE WHEN (#minutes % 60) > 0 THEN
CAST((#minutes % 60) AS VARCHAR(2))
ELSE
''
END)
ELSE
CAST((#minutes % 60) AS VARCHAR(2))
END
return #hours
END
First, I recommend using decimal hours. Much simpler:
SELECT d.combovalue, SUM(t.OTTime) / 60.0
FROM dbo.employee e JOIN
dbo.OT t
ON e.id = t.employeeid JOIN
dbo.combovalues d
ON e.department = d.id
GROUP By d.combovalue;
But that is not your question. If you knew that there are never more than 24 hours, you could use the TIME data type:
CONVERT(VARCHAR(5), DATEADD(minute, SUM(t.OTTime), 0), 8)
If that is too dangerous, then you need to convert to a string:
CONCAT(FORMAT(SUM(t.OTTime) / 60, '00'), ':', FORMAT(SUM(t.OTTime) % 60, '00'))

How i can remove results of my Query, using LAG function?

First of all, srry for my bad english.
I need to calculate the iddle time of an separators group. Actualy i have the following query:
USE mydatabase
SELECT p.iddoc as SaleOrder, p.name as Client, u.username as SysUser,
CONVERT(DATETIME, o.creatdate) as CreateDate,
CONVERT(DATETIME, o.inidate) as IniDate,
CONVERT(DATETIME, o.findate) as FinalDate,
RIGHT('0' + CAST(DATEDIFF(S, (lag(o.findate, 1) OVER (ORDER BY o.findate)), o.inidate) / 3600 AS VARCHAR(2)),2) + ':'
+ RIGHT('0' + CAST(DATEDIFF(S, (lag(o.findate, 1) OVER (ORDER BY o.findate)), o.inidate) % 3600/60 AS VARCHAR(2)),2) + ':'
+ RIGHT('0' + CAST(DATEDIFF(S, (lag(o.findate, 1) OVER (ORDER BY o.findate)), o.inidate) % 60 AS VARCHAR(2)),2) as IdleTime
FROM serviceorder o
inner join user u on o.usercode = u.codusuario
inner join requests p on o.idrequest = p.codpedidocab
WHERE u.username like ('USER NAME')
and o.iduserstart is NOT NULL
and p.sit = '8'
and o.creatdate BETWEEN '2020-06-30 00:00:00' and '2020-06-30 23:59:00'
and o.inidate BETWEEN '2020-06-30 00:00:00' and '2020-06-30 23:59:00'
ORDER BY o.inidate
My problem on this query is when "o.inidate is < than a last o.findate", i need to remove the results than have "o.inidate < (lag(o.findate, 1) OVER (ORDER BY o.findate))", but i can't this because can't use the LAG on the where.
What do you suggest i do?
Window functions can be used in the select and order by clauses only. You can use a subquery instead:
SELECT *
FROM (
SELECT
p.iddoc as SaleOrder,
p.name as Client,
u.username as SysUser,
CONVERT(DATETIME, o.creatdate) as CreateDate,
CONVERT(DATETIME, o.inidate) as IniDate,
CONVERT(DATETIME, o.findate) as FinalDate,
LAG(o.findate) OVER (ORDER BY o.findate) lagFinalDate,
RIGHT('0' + CAST(DATEDIFF(S, (lag(o.findate, 1) OVER (ORDER BY o.findate)), o.inidate) / 3600 AS VARCHAR(2)),2) + ':'
+ RIGHT('0' + CAST(DATEDIFF(S, (lag(o.findate, 1) OVER (ORDER BY o.findate)), o.inidate) % 3600/60 AS VARCHAR(2)),2) + ':'
+ RIGHT('0' + CAST(DATEDIFF(S, (lag(o.findate, 1) OVER (ORDER BY o.findate)), o.inidate) % 60 AS VARCHAR(2)),2) as IdleTime
FROM serviceorder o
INNER JOIN [user] u ON o.usercode = u.codusuario
INNER JOIN requests p ON o.idrequest = p.codpedidocab
WHERE
u.username = 'USER NAME'
AND o.iduserstart is NOT NULL
AND p.sit = '8'
AND o.creatdate >= '2020-06-30'
AND o.creatdate < '2020-07-01'
AND o.inidate >= '2020-06-30'
AND o.inidate < '2020-07-01'
) t
where IniDate < lagFinalDate
Additional changes to the query:
half-open interval filters are more accurate (and easier to understand) than between
u.username like ('USER NAME') is equivalent to u.username = 'USER NAME'
user is a language keyword, hence it needs to be quoted when used as a table name

Operand data type varchar is invalid for avg operator

I keep getting the above error message
"Operand data type varchar is invalid for avg operator"
Can anyone fix it for me? PLEASE
WITH Average -- Calculating Mean
AS (
SELECT avg(convert(VARCHAR(5), DateDiff(s, [ARRIVAL_DATE_TIME], [COMPLETE_DATE_TIME]) / 3600) + ':' + convert(VARCHAR(5), DateDiff(s, [ARRIVAL_DATE_TIME], [COMPLETE_DATE_TIME]) % 3600 / 60) + ':' + convert(VARCHAR(5), (DateDiff(s, [ARRIVAL_DATE_TIME], [COMPLETE_DATE_TIME]) % 60))) AS Average
FROM [CLERKS]
WHERE [ARRIVAL_DATE_TIME] >= DATEADD(dd, - 30, getdate() - 1)
)
,data
AS (
SELECT cast(ARRIVAL_DATE_TIME AS DATE) AS Attendance_Date
,avg(convert(VARCHAR(5), DateDiff(s, [ARRIVAL_DATE_TIME], [COMPLETE_DATE_TIME]) / 3600) + ':' + convert(VARCHAR(5), DateDiff(s, [ARRIVAL_DATE_TIME], [COMPLETE_DATE_TIME]) % 3600 / 60) + ':' + convert(VARCHAR(5), (DateDiff(s, [ARRIVAL_DATE_TIME], [COMPLETE_DATE_TIME]) % 60))) AS Arr_Com
FROM [Clerks]
WHERE [ARRIVAL_DATE_TIME] >= DATEADD(dd, - 30, getdate() - 1)
GROUP BY cast(ARRIVAL_DATE_TIME AS DATE)
)
SELECT a.Attendance_Date
,a.Arr_Com
,c.Average
,abs(a.Arr_Com - b.Arr_Com) AS MR
FROM data a
LEFT JOIN data b ON cast(a.Attendance_Date AS DATETIME) = cast(b.Attendance_Date AS DATETIME) + 1
CROSS JOIN Average c
ORDER BY a.Attendance_Date
GO
Thanks in Advance
Actually, the problem is that the implementation is incorrect. You don't want to average timestamps (5h:3m:20s), but durations.
Hence, you need to calculate the duration in the smallest denominator, in your case seconds, calculate the average in seconds, by using the AVG() function and then formatting that result to look like hh:mm:ss.
Your code should look like:
;WITH Average -- Calculating Mean
AS (
SELECT AVG(DateDiff(s, [ARRIVAL_DATE_TIME], [COMPLETE_DATE_TIME])) as Average
FROM [CLERKS]
WHERE [ARRIVAL_DATE_TIME] >= DATEADD(dd, - 30, getdate() - 1)
)
,data
AS (
SELECT cast(ARRIVAL_DATE_TIME AS DATE) AS Attendance_Date
, AVG(DateDiff(s, [ARRIVAL_DATE_TIME], [COMPLETE_DATE_TIME])) as Arr_Com
FROM [Clerks]
WHERE [ARRIVAL_DATE_TIME] >= DATEADD(dd, - 30, getdate() - 1)
GROUP BY cast(ARRIVAL_DATE_TIME AS DATE)
)
SELECT
Attendance_Date
, Arr_Com
, Average
, avg(convert(VARCHAR(5), MR / 3600) + ':' + convert(VARCHAR(5), MR % 3600 / 60) + ':' + convert(VARCHAR(5), MR % 60))) AS MR
FROM (
SELECT a.Attendance_Date
,a.Arr_Com
,c.Average
, abs(a.Arr_Com - b.Arr_Com) AS MR
FROM data a
LEFT JOIN data b ON cast(a.Attendance_Date AS DATETIME) = cast(b.Attendance_Date AS DATETIME) + 1
CROSS JOIN Average c
) tmp
ORDER BY Attendance_Date

Showing only hours and minutes?

How to display only hours and minutes in a field if I get them separately
[SELECT
D.numOperador,
SUM(DATEDIFF(MINUTE, D.FechaSalida, D.FechaLlegada)) / 60 AS \[Horas\] ,
SUM(DATEDIFF(MINUTE, D.FechaSalida, D.FechaLlegada)) % 60 AS \[Minutos\]
FROM
trkOperadores O
INNER JOIN trfDespacho D ON O.NumOperador = D.numOperador
WHERE
O.cveTipoOperador = 2
AND O.NumOperador = 900200
AND D.FechaSalida>='2017-03-10 00:00:00.000' AND D.FechaLlegada<='2017-03-11 00:00:00.000'
GROUP BY
D.NumOperador;
GO]
Time
If you just want to join hrs and mns together in the result, concatenate both of them :
SELECT
D.numOperador,
( cast ( SUM(DATEDIFF(MINUTE, D.FechaSalida, D.FechaLlegada)) / 60 as varchar)
+
cast ( SUM(DATEDIFF(MINUTE, D.FechaSalida, D.FechaLlegada)) % 60 AS varchar ) ) as time
FROM
trkOperadores O
INNER JOIN trfDespacho D ON O.NumOperador = D.numOperador
WHERE
O.cveTipoOperador = 2
AND O.NumOperador = 900200
AND D.FechaSalida>='2017-03-10 00:00:00.000' AND D.FechaLlegada<='2017-03-11 00:00:00.000'
GROUP BY
D.NumOperador;
GO]
I assume that you want something like this.
DECLARE #D1 DATETIME = '20170401'
DECLARE #D2 DATETIME = '20170402 00:05:00'
SELECT RIGHT( '00' + CAST(DATEDIFF(MINUTE, #D1, #D2) /60 AS VARCHAR(2)) , 2) + ':' + RIGHT( '00' + CAST(DATEDIFF(MINUTE, #D1, #D2) % 60 AS VARCHAR(2)) , 2)
-- 24:05
SELECT
D.numOperador,
SUM(DATEDIFF(MINUTE, D.FechaSalida, D.FechaLlegada)) / 60 AS [Horas] ,
SUM(DATEDIFF(MINUTE, D.FechaSalida, D.FechaLlegada)) % 60 AS [Minutos]
CONVERT(VARCHAR(10),CAST(CAST (D.FechaSalida AS FLOAT) - CAST (D.FechaLlegada AS FLOAT) / 86400. AS DATETIME),108) --86400 equal 1 day
FROM
trkOperadores O INNER JOIN trfDespacho D
ON O.NumOperador = D.numOperador
WHERE
O.cveTipoOperador = 2
AND O.NumOperador = 900200
AND D.FechaSalida>='2017-03-10 00:00:00.000' AND D.FechaLlegada<='2017-03-11 00:00:00.000'
GROUP BY
D.NumOperador;
GO

SQL SUM() function ignoring WHERE clause and CASE statement

SELECT u.FirstName + ' ' + u.LastName as 'User',
COUNT(*) as 'Number of Calls',
CONVERT(varchar(4), SUM(CASE WHEN c.FromTime BETWEEN #FromDate AND #ToDate
THEN DATEDIFF(mi, c.FromTime, c.ToTime) ELSE 0 END) / 60) + ':' +
CASE WHEN SUM(CASE WHEN c.FromTime BETWEEN #FromDate AND #ToDate
THEN DATEDIFF(mi, c.FromTime, c.ToTime) ELSE 0 END) % 60 < 10 THEN '0' ELSE '' END +
CONVERT(varchar(2), SUM(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60) as 'Total Time Spent',
CONVERT(varchar(4), AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) / 60) + ':' +
CASE WHEN AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60 < 10 THEN '0' ELSE '' END +
CONVERT(varchar(2), AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60) as 'Average Call Time'
FROM Calls c
JOIN Users u ON u.UserID = c.TakenBy
WHERE c.FromTime BETWEEN #FromDate AND #ToDate
GROUP BY u.UserID, u.FirstName, u.LastName
ORDER BY u.FirstName + ' ' + u.LastName
The preceding SQL query returns the correct "Number of Calls" but the "Total Time" and "Average Time" are always the same regardless of the # of calls (which is obviously wrong).
I've read and tried to implement using the CASE WHEN __ Then value ELSE 0 inside SUM but it still returns an incorrect value.
The only way I can get this query to return correct results is if I completely strip out all other info, e.g.
SELECT SUM(DATEDIFF(mi, FromTime, ToTime)) FROM Calls WHERE c.FromTime BETWEEN...
How can I still use my JOIN and GROUP BY and get the aggregate functions to give me the results I want?
Thanks for any and all help!
You're probably better off with a subquery, e.g. in the SELECT part, add something like (SELECT SUM(...) FROM ... WHERE ...) AS total_sum.