Wrong COUNT() from MySQL in some but not all of the groupings - mysql

I'm trying to count the number of values in a group of matching values. The first query does the count, the second lists the output of the data itself. Please note the count doesn't always match the output of the second query. I'm not sure why this error is occurring or how to fix it. I suspect the problem is with the GROUP BY but after trying many versions of it I don't have a solution.
SELECT CONCAT(date_format(timestamp,'%H'),':',LPAD(MINUTE(
FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(timestamp)/300)*300)),2,0)) AS hrmn,
COUNT(CONCAT(date_format(timestamp,'%H'),':',LPAD(MINUTE(
FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(timestamp)/300)*300)),2,0))) AS hrmncount
FROM TimeLog
WHERE netID = 3646
GROUP BY CONCAT(date_format(timestamp,'%H'),':',LPAD(MINUTE(
FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(timestamp)/300)*300)),2,0))
;
SELECT CONCAT(date_format(timestamp,'%H'),':',LPAD(MINUTE(
FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(timestamp)/300)*300)),2,0)) AS hrmn
FROM TimeLog
WHERE netID = 3646
GROUP BY timestamp
ORDER BY hrmn ASC
;
This is the output of the first query:
hrmn hrmncount
00:50 2
01:00 10
01:05 9
01:10 12
01:15 5
01:20 7
01:25 6
01:30 3
01:35 1
But this is the output from the second, which shows that the first query is counting wrong in the 00:50, 01:10 and 01:20 values.
hrmn
00:50
01:00
01:00
01:00
01:00
01:00
01:00
01:00
01:00
01:00
01:00
01:05
01:05
01:05
01:05
01:05
01:05
01:05
01:05
01:05
01:10
01:10
01:10
01:10
01:10
01:10
01:10
01:10
01:10
01:10
01:10
01:15
01:15
01:15
01:15
01:15
01:20
01:20
01:20
01:20
01:20
01:20
01:20
01:25
01:25
01:25
01:25
01:25
01:25
01:30
01:30
01:30
01:35

I was able to track the root of the problem down to the GROUP BY in the SQL code. It wasn't needed, and by removing it all the records including the duplicate by time were exposed.
I should have seen this earlier but sometimes you get blind to the obvious.

Related

I need MYSQL query to represent check-in check-out log

Being a fresher in query writing, I need help at this MySQL scenario.My table has below records:
event_time status
10:01 ON
10:02 ON
10:03 OFF
10:04 ON
10:07 OFF
10:08 ON
10:09 ON
10:11 ON
10:15 OFF
I want to print record as
log_in log_out onCount
10:01 10:03 2
10:04 10:07 1
10:08 10:15 3
Any Suggestion.... ?? or code to get this result set

SQL uncondensing data / generating more rows containing similar data

I have some data which is very variable in it's update frequency (doesn't change for ages, then changes very often).
I sample it according to a schedule I create and end up with the table below:
periodStart
periodEnd
variable
01/10/2019 00:06
01/10/2019 01:00
0.61
01/10/2019 01:00
01/10/2019 02:00
0.61
01/10/2019 02:00
01/10/2019 03:00
0.61
01/10/2019 03:00
01/10/2019 04:00
0.61
01/10/2019 04:00
01/10/2019 05:00
0.61
01/10/2019 05:00
01/10/2019 06:00
0.61
01/10/2019 06:00
01/10/2019 07:00
0.61
01/10/2019 07:00
01/10/2019 08:00
0.61
01/10/2019 08:00
01/10/2019 09:00
0.59
01/10/2019 09:00
01/10/2019 10:00
0.59
01/10/2019 10:00
01/10/2019 11:00
0.59
01/10/2019 11:00
01/10/2019 12:00
0.58
I am trying to condense the database so stored it the alternative form below. I also need to retain access to the original sampling timestamps (periodStart and periodEnd), so created 'samplingInterval'. Using this you can determine all original periodStart and periodEnd timestamps from the new periodStart and periodEnd.
periodStart
periodEnd
samplingInterval(mins)
variable
01/10/2019 00:06
01/10/2019 01:00
54
0.61
01/10/2019 01:00
01/10/2019 08:00
60
0.61
01/10/2019 08:00
01/10/2019 11:00
60
0.59
01/10/2019 11:00
01/10/2019 12:00
60
0.58
The issue I am having is writing a query to SELECT the table in the original form from it's condensed form. I'm comfortable writing a query using a SQL variable, thinking I could use a loop (in a stored procedure, which is less than ideal) but I just don't know how to output the row without advancing to the next row. Is it possible? Should I be approaching it differently?
You may use a Recursive Query as the following:
With recursive cte as
(
select periodStart st,
adddate(periodStart , interval samplingInterval minute) en
,periodEnd,variable,samplingInterval si
From condensed
Union All
select adddate(st, interval si minute),
adddate(en, interval si minute),
periodEnd,variable,si from cte
where adddate(st , interval si minute)<periodEnd
)
select st as periodStart,en as periodEnd,variable from cte order by st;
See a deom from db-fiddle.

Next value by previous data

I am trying to create a query that will query the next result by looking into data as how it previous was.
So my data is like
Train - Station - Time
158 Station1 11:10
158 Station1 11:11
158 Station1 11:12
158 Station1 11:13
158 Station1 11:14
158 Station2 11:25
158 Station2 11:26
158 Station2 11:27
158 Station3 11:41
158 Station3 11:42
158 Station3 11:43
158 Station3 11:44
158 Station3 11:45
158 Station4 11:50
158 Station4 11:51
158 Station4 11:52
158 Station4 11:53
So lets say im at "Station3"
I am using the following query to find out what previous Station is:
SELECT * FROM Train
WHERE Train = '158' AND Station NOT LIKE 'Station3'
ORDER BY Time DESC
LIMIT 1
I want to query what the next Station is, but i cant really figure out to make that query. Any tips?
Edit:
Ok, lets say that my table have more information, about previous train runs, and it is from that i want to check what the next station will be.
Train - Station - Time
158 Station1 10:10
158 Station1 10:11
158 Station1 10:12
158 Station1 10:13
158 Station1 10:14
158 Station2 10:25
158 Station2 10:26
158 Station2 10:27
158 Station3 10:41
158 Station3 10:42
158 Station3 10:43
158 Station3 10:44
158 Station3 10:45
158 Station4 10:50
158 Station4 10:51
158 Station4 10:52
158 Station4 10:53
158 Station5 10:55
158 Station5 10:56
158 Station6 10:57
158 Station6 10:58
158 Station1 11:10
158 Station1 11:11
158 Station1 11:12
158 Station1 11:13
158 Station1 11:14
158 Station2 11:25
158 Station2 11:26
158 Station2 11:27
158 Station3 11:41
158 Station3 11:42
158 Station3 11:43
158 Station3 11:44
158 Station3 11:45
158 Station4 11:50
158 Station4 11:51
158 Station4 11:52
158 Station4 11:53
159 Station1 11:10
159 Station1 11:11
159 Station1 11:12
159 Station1 11:13
159 Station1 11:14
159 Station2 11:25
159 Station2 11:26
159 Station2 11:27
159 Station3 11:41
159 Station3 11:42
159 Station3 11:43
159 Station3 11:44
159 Station3 11:45
159 Station4 11:50
158 Station4 11:51
159 Station4 11:52
159 Station4 11:53
So lets say im at the Train 158 Station4 Time 11:53, and i dont know what the next Station is, and i want to query it is Station5. How would i be doing that?
if you only need to know the next station you could use a subquery for max time for station3 and train 158
select * from
my_table
where time > (
select max(time) max_time
from my_table
where Train = '158' AND Station = 'Station3' )
and train = '158'
order by time limit 1
Other approach with out first finding the MAX(time) for station.
MySQL 8.0+ makes it really eazy with LEAD to get the next records data.
Query
SELECT
Table1.Train
, Table1.next_station AS station
, Table1.next_station_time AS time
FROM (
SELECT
*
, LEAD(Station) OVER (PARTITION BY Train ORDER BY Time) AS next_station
, LEAD(Time) OVER (PARTITION BY Train ORDER BY Time) AS next_station_time
FROM
Table1
WHERE
Train = 158
)
AS Table1
WHERE
Table1.station = 'station3'
AND
Table1.station <> Table1.next_station
see demo https://www.db-fiddle.com/f/9Ld7XznMEuzNdRC3dmysUt/1
In the older MySQL versions we need to be more creative because LEAD isn't supported.
Best way to simulate LEAD is by using MySQL's user variables, and a shifting self LEFT JOIN
Keep in mind MySQL user variables work in MySQL 5.1+
Query
SELECT
current_train AS train
, next_station AS station
, next_time AS time
FROM (
SELECT
t1.Train AS current_train
, t1.Station AS current_station
, t1.Time AS `current_time`
, t2.Train AS next_train
, t2.Station AS next_station
, t2.Time AS next_time
FROM (
SELECT
*
, #rownum1 := #rownum1 + 1 AS rownum
FROM
Table1
CROSS JOIN ( SELECT #rownum1 := 0 ) AS i
WHERE
Train = 158
ORDER BY
Time ASC
) AS t1
LEFT JOIN (
SELECT
*
, #rownum2 := #rownum2 + 1 AS rownum
FROM
Table1
CROSS JOIN ( SELECT #rownum2 := 0 ) AS i
WHERE
Train = 158
ORDER BY
Time ASC
) AS t2
ON
t1.rownum + 1 = t2.rownum
)
AS records
WHERE
records.current_station = 'station3'
AND
records.current_station <> records.next_station
See demo http://sqlfiddle.com/#!9/ff23fc/38
On a simpler way, using self join could help you achieve it.
Try this:
select t1.* from train t1
right join train t2 on t1.train=t2.train
where t1.station != 'Station3' and t1.time>(t2.time)
and t2.station='Station3' limit 1 ;
Try changing the station value on both table t1 and t2 to your desired value which is Station3 in this case.
Live SQL Fiddle demo here.

Cumulative Adding in SQL SERVER 2008

This is what i have got in my SQL SERVER database table, where I am trying to calculate Balance Leave of an employee.
My actual data is:
EmpId EmpName EvalDate OpeningEL EnjoyedEL BalanceEL
12 CHANDRA 2014-04-01 18:30:00.000 0.95 0.00 0.95
12 CHANDRA 2014-05-01 18:30:00.000 1.30 0.00 1.30
12 CHANDRA 2014-06-01 18:30:00.000 1.20 1.00 1.20
12 CHANDRA 2014-07-01 18:30:00.000 1.25 0.00 1.25
12 CHANDRA 2014-08-01 18:30:00.000 1.25 1.00 1.25
But i need the data in below way
EmpId EmpName EvalDate OpeningEL EnjoyedEL BalanceEL
12 CHANDRA 2014-04-01 18:30:00.000 0.95 0.00 0.95
12 CHANDRA 2014-05-01 18:30:00.000 2.25 0.00 2.25
12 CHANDRA 2014-06-01 18:30:00.000 3.45 1.00 2.45
12 CHANDRA 2014-07-01 18:30:00.000 3.70 0.00 3.70
12 CHANDRA 2014-08-01 18:30:00.000 4.95 1.00 3.95
Previous BalanceELs are added with next OpeningELs.
So, how to achieve this....Please suggest something.
You can use CROSS APPLY and GROUP BY to achieve this
OpeningEL and BalanceEL from CROSS APPLY will get the sum of current and previous records for an employee.
SELECT
EL1.EmpId,
EL1.EmpName,
EL1.EvalDate,
Temp.OpeningEL,
EL1.EnjoyedEL,
Temp.BalanceEL
FROM EmployeeLeave EL1
CROSS APPLY
(
SELECT
SUM(OpeningEL) as OpeningEL,
SUM(BalanceEL) - SUM(EnjoyedEL) as BalanceEL
FROM EmployeeLeave EL2
WHERE EL2.EmpId = EL1.EmpId
AND EL2.EvalDate <= EL1.EvalDate
)Temp;

Calculation to be made while updating a field

Origin Dest Date Amount 50% Due
92509 0021 2013-07-30 00:00:00.000 5.37 0.00
92509 0021 2013-07-30 00:00:00.000 5.37 0.00
92509 0021 2013-07-30 00:00:00.000 5.37 0.00
92509 0021 2013-07-31 00:00:00.000 5.37 2.69
92509 0021 2013-07-31 00:00:00.000 5.37 2.69
92509 0021 2013-07-31 00:00:00.000 5.37 2.69
92509 0021 2013-08-01 00:00:00.000 5.37 2.69
92509 0021 2013-08-01 00:00:00.000 5.37 2.69
42101 0029 2013-03-06 00:00:00.000 6.06 0.00
42101 0029 2013-03-06 00:00:00.000 6.06 0.00
42101 0029 2013-03-07 00:00:00.000 6.06 3.03
42101 0029 2013-03-07 00:00:00.000 6.06 3.03
42101 0030 2013-03-06 00:00:00.000 6.06 0.00
42101 0030 2013-03-06 00:00:00.000 6.06 0.00
42101 0030 2013-03-07 00:00:00.000 6.06 3.03
42101 0030 2013-03-07 00:00:00.000 6.06 3.03
So I have a table something similar to what i shown above. Right now, the 50% Due field is empty. I need to fill that field with values as shown above.
The 50% due field should populate values that are half of what is present in the Amount field. But, it should fill zero for the initial date (2013-07-30 00:00:00.000) and for the consecutive days it should fill half of what is present in the Amount field.
I have a lot of rows like these that needs to get updated. Also there are rows with different Origin and Destination.
I am dealing with some freight parcels. The data describes the parcels that were sent to the same destination from same origin on consecutive days. The parcels that were sent on consecutive days could have been sent together on the initial date itself. So I am trying to generate a claim for those parcels that were sent on consecutive days to the same destination from the same origin. And the 50% Due would be the claim!
I am fairly new to SQL! This seems to be very complicated for me. Please help.
If you want to update all origin/dest combinations that had an entry on the prior day then you can do this:
update t
set [50% due]=coalesce(cast(p.amount/2.0 as smallmoney),0.00)
from [table] t
left join [table] p
on t.origin=p.origin and t.dest=p.dest
and dateadd(D,-1,cast(t.[date] as date))=cast(p.[date] as date)
or use a cte and LAG in SQL 2012 and later versions:
;with previous as
(
select origin,dest,[date]
,LAG(cast([date] as date),1,cast([date] as date)) OVER (PARTITION BY origin,dest ORDER BY cast([date] as date)) as previous
from (select distinct origin, dest, [date]
from [table]) a
)
update t
set [50% Due]=case when dateadd(D,-1,cast(t.[date] as date))<>cte.previous then 0.00 else cast(t.[amount]/2.0 as smallmoney) end
from [table] t
join previous cte
on cte.origin=t.origin and cte.dest=t.dest and cte.[date]=t.[date]
If you want to update all your records based on the earliest day that the origin/dest combination occurred, then this will work in SQL Server:
;with earliest as
(
select origin,dest,min(cast([date] as date)) earliest
from [table]
group by origin,dest
)
update t
set [50% Due]=case when cast(t.[date] as date)=cte.earliest then 0.00 else cast(t.[amount]/2.0 as smallmoney) end
from [table] t
join earliest cte
on cte.origin=t.origin and cte.dest=t.dest
If you want to update all your records only based on the earliest day in the table and you don't care about the orgin/dest combination then you don't need the cte to group the earliest dates and you can simply do the compare in the update.
UPDATE t
set [50% Due]=case when cast(t.[date] as date)=(select min(cast(t.date as date))) then 0.00 else cast(t.[amount]/2.0 as smallmoney) end