SQL Error (1111): Invalid use of group function - mysql

I am trying to fetch data between the (max date in the entrydate column) and (max date in the entrydate column-15days)!!
Table1:Totally it has 953rows
Table2: Totally it has 400rows
i got this error ,Please help me !!
Select
o_material_transaction_inward.Mat_Code,
s_material_details.Mat_Spec,
s_material_details.Mat_Make,
o_material_transaction_inward.Sup_Name,
o_material_transaction_inward.Entry_Date,
o_material_transaction_inward.DC_qty,
o_material_transaction_inward.Received_qty,
from
o_material_transaction_inward
join
s_material_details
on
s_material_details.Mat_Code=o_material_transaction_inward.Mat_Code and s_material_details.Mat_Group_Id=o_material_transaction_inward.Mat_Group_id
where
o_material_transaction_inward.Entry_Date between Max(o_material_transaction_inward.Entry_Date) and Max(o_material_transaction_inward.Entry_Date - 15)

Update: Fixed BETWEEN predicate:
The reason it wasn't working is that the lowest value need to be the first value in the BETWEEN predicate, so in order to get those rows from the two tables having the entry_date between the max entry date - 15 and max entry date try this:
SELECT
o.Mat_Code,
s.Mat_Spec,
s.Mat_Make,
o.Sup_Name,
DATE_FORMAT(o.Entry_Date, '%Y-%m-%d') AS Entry_Date,
o.DC_qty,
o.Received_qty
FROM o_material_transaction_inward AS o
INNER JOIN s_material_details AS s ON s.Mat_Code = o.Mat_Code
WHERE o.Entry_Date BETWEEN ((SELECT Max(Entry_Date)
FROM o_material_transaction_inward) - 15)
AND (SELECT Max(Entry_Date)
FROM o_material_transaction_inward) ;
SQL Fiddle Demo

Try:
Select
o_material_transaction_inward.Mat_Code,
s_material_details.Mat_Spec,
s_material_details.Mat_Make,
o_material_transaction_inward.Sup_Name,
o_material_transaction_inward.Entry_Date,
o_material_transaction_inward.DC_qty,
o_material_transaction_inward.Received_qty,
from
o_material_transaction_inward
join
s_material_details
on
s_material_details.Mat_Code=o_material_transaction_inward.Mat_Code and s_material_details.Mat_Group_Id=o_material_transaction_inward.Mat_Group_id
having
o_material_transaction_inward.Entry_Date between Max(o_material_transaction_inward.Entry_Date) and Max(o_material_transaction_inward.Entry_Date - 15)
(see the accepted answer of MySQL: Invalid use of group function for an explanation of where vs having)

This should work:
SELECT
o.Mat_Code,
s.Mat_Spec,
s.Mat_Make,
o.Sup_Name,
o.Entry_Date,
o.DC_qty,
o.Received_qty,
FROM o_material_transaction_inward AS o
INNER JOIN s_material_details AS s ON s.Mat_Code = o.Mat_Code
AND s.Mat_Group_Id = o.Mat_Group_id
WHERE o.Entry_Date BETWEEN (SELECT Max(Entry_Date)
FROM o_material_transaction_inward)
AND (SELECT Max(Entry_Date)
FROM o_material_transaction_inward) - 15);

Related

How to sort by date in mysql

I need to perform the following query in mysql.
SELECT
evaluationpart.id,
evaluationpart.creation,
evaluationpart.evaluationid,
evaluationpart.partid,
evaluation.horimeter,
personcompressorpart.hourcapacity,
evaluation.evaluationdate AS changedate,
evaluation.averageworkload,
#ed := DATEDIFF(curdate(), evaluation.evaluationdate) AS elapseddays,
#uh:= #ed * evaluation.averageworkload AS usedhours,
#htu:= personcompressorpart.hourcapacity - #uh AS hourstouse,
#nc:= curdate() + INTERVAL (#htu/evaluation.averageworkload) DAY AS nextchange
FROM evaluationpart
LEFT JOIN evaluation ON evaluation.id = evaluationpart.evaluationid
LEFT JOIN personcompressorpart ON personcompressorpart.id = evaluationpart.partid
ORDER BY #nc ASC
But the Order By is not working and I'm getting this result
Could anyone tell me why?
It seems that you are not using the column name in the ORDER BY clause.
If you want to order the query result by the column named 'nextchange', the ORDER BY clause should be ORDER BY nextchange ASC.
Here's the MySQL documentation on Sorting Rows: https://dev.mysql.com/doc/refman/8.0/en/sorting-rows.html
I hope this helps.

How to delete data with date value + time

I want to delete data between 'change_slot' and ('change_slot' + 2min) WHERE type = ' crash', 'pilot_death', or 'eject'
DELETE FROM pe_LogEvent
WHERE pe_LogEvent_type = 'crash'
OR pe_LogEvent_type = 'pilot_death'
OR pe_LogEvent_type = 'eject'
AND pe_LogEvent_datetime IN??? pe_LogEvent_type = 'change_slot'
BETWEEN ('change_slot datetime') AND (DATE_ADD('change_slot datetime', INTERVAL 120)))
Visual SQL DB Image with annotation of what I am trying to accomplish.:
I have another issue now, I'm trying to think of a way to solve this one as well. I was trying to use maybe a 'for each' iterative statement to filter only 1 pilotname at a time maybe? How would you go about solving this?
Problem: different pilot change_slot and causes current pilot's death not to count now ... I need to only have same pilot change_slot within 2 min pilot_death, crash, or eject does not count
DELETE FROM pe_LogEvent
WHERE pe_LogEvent_datetime BETWEEN
(SELECT pe_LogEvent_datetime FROM pe_LogEvent WHERE pe_LogEvent_type = 'change_slot' ORDER BY pe_LogEvent_datetime DESC LIMIT 1)
AND (SELECT pe_LogEvent_datetime + INTERVAL 2 MINUTES FROM pe_LogEvent WHERE pe_LogEvent_type = 'change_slot' ORDER BY pe_LogEvent_datetime DESC LIMIT 1)
AND pe_LogEvent_type IN ('crash', 'pilot_death', 'eject')
Somethink like this?
EDIT:
According to your comments below this answer, propably you want something like this, but i am not sure but if not, propably you could change this SQL at your own.
For such non-obvious queries i always use the CTE. I love it because you could use it to 'separate' your logic to simplest 'steps'. Then it's looks simple and easy to see what going on (At least for me) instead of one big query from which it is not so easy to deduce what is happening and is more difficult to edit/read.
First you could use the CTE to get all 'change_slot' type events with dates:
WITH change_slots_dates AS (
SELECT
pe_LogEvent_datetime AS date_start,
pe_LogEvent_datetime + INTERVAL 2 MINUTE AS end_time
FROM pe_LogEvent
WHERE pe_LogEvent_type = 'change_slot'
)
then another CTE to get IDs between this dates (Remember that your's CTE need to be separate by , sign:
rows_to_delete AS (
SELECT
pe.pe_LogEvent_id AS id
FROM pe_LogEvent AS pe
INNER JOIN change_slots_dates AS csd ON (pe.pe_LogEvent_datetime BETWEEN csd.date_start AND csd.end_time)
WHERE pe.pe_LogEvent_type IN ('crash', 'pilot_death', 'eject')
)
after that you can finally delete this entities:
DELETE FROM pe_LogEvent WHERE pe_LogEvent_id IN (SELECT id FROM rows_to_delete);
Notice that this is a one SQL query and you cannot use this as 3 queries. So all seems to be like this:
WITH change_slots_dates AS (
SELECT
pe_LogEvent_datetime AS date_start,
pe_LogEvent_datetime + INTERVAL 2 MINUTE AS end_time
FROM pe_LogEvent
WHERE pe_LogEvent_type = 'change_slot'
),
rows_to_delete AS (
SELECT
pe.pe_LogEvent_id AS id
FROM pe_LogEvent AS pe
INNER JOIN change_slots_dates AS csd ON (pe.pe_LogEvent_datetime BETWEEN csd.date_start AND csd.end_time)
WHERE pe.pe_LogEvent_type IN ('crash', 'pilot_death', 'eject')
)
DELETE FROM pe_LogEvent WHERE pe_LogEvent_id IN (SELECT id FROM rows_to_delete);
I hope it will be finally more helpfull.

Generating complex sql tables

I currently have an employee logging sql table that has 3 columns
fromState: String,
toState: String,
timestamp: DateTime
fromState is either In or Out. In means employee came in and Out means employee went out. Each row can only transition from In to Out or Out to In.
I'd like to generate a temporary table in sql to keep track during a given hour (hour by hour), how many employees are there in the company. Aka, resulting table has columns HourBucket, NumEmployees.
In non-SQL code I can do this by initializing the numEmployees as 0 and go through the table row by row (sorted by timestamp) and add (employee came in) or subtract (went out) to numEmployees (bucketed by timestamp hour).
I'm clueless as how to do this in SQL. Any clues?
Use a COUNT ... GROUP BY query. Can't see what you're using toState from your description though! Also, assuming you have an employeeID field.
E.g.
SELECT fromState AS 'Status', COUNT(*) AS 'Number'
FROM StaffinBuildingTable
INNER JOIN (SELECT employeeID AS 'empID', MAX(timestamp) AS 'latest' FROM StaffinBuildingTable GROUP BY employeeID) AS LastEntry ON StaffinBuildingTable.employeeID = LastEntry.empID
GROUP BY fromState
The LastEntry subquery will produce a list of employeeIDs limited to the last timestamp for each employee.
The INNER JOIN will limit the main table to just the employeeIDs that match both sides.
The outer GROUP BY produces the count.
SELECT HOUR(SBT.timestamp) AS 'Hour', SBT.fromState AS 'Status', COUNT(*) AS 'Number'
FROM StaffinBuildingTable AS SBT
INNER JOIN (
SELECT SBIJ.employeeID AS 'empID', MAX(timestamp) AS 'latest'
FROM StaffinBuildingTable AS SBIJ
WHERE DATE(SBIJ.timestamp) = CURDATE()
GROUP BY SBIJ.employeeID) AS LastEntry ON SBT.employeeID = LastEntry.empID
GROUP BY SBT.fromState, HOUR(SBT.timestamp)
Replace CURDATE() with whatever date you are interested in.
Note this is non-optimal as it calculates the HOUR twice - once for the data and once for the group.
Again you are using the INNER JOIN to limit the number of returned row, this time to the last timestamp on a given day.
To me your description of the FromState and ToState seem the wrong way round, I'd expect to doing this based on the ToState. But assuming I'm wrong on that the following should point you in the right direction:
First, I create a "Numbers" table containing 24 rows one for each hour of the day:
create table tblHours
(Number int);
insert into tblHours values
(0),(1),(2),(3),(4),(5),(6),(7),
(8),(9),(10),(11),(12),(13),(14),(15),
(16),(17),(18),(19),(20),(21),(22),(23);
Then for each date in your employee logging table, I create a row in another new table to contain your counts:
create table tblDailyHours
(
HourBucket datetime,
NumEmployees int
);
insert into tblDailyHours (HourBucket, NumEmployees)
select distinct
date_add(date(t.timeStamp), interval h.Number HOUR) as HourBucket,
0 as NumEmployees
from
tblEmployeeLogging t
CROSS JOIN tblHours h;
Then I update this table to contain all the relevant counts:
update tblDailyHours h
join
(select
h2.HourBucket,
sum(case when el.fromState = 'In' then 1 else -1 end) as cnt
from
tblDailyHours h2
join tblEmployeeLogging el on
h2.HourBucket >= el.timeStamp
group by h2.HourBucket
) cnt ON
h.HourBucket = cnt.HourBucket
set NumEmployees = cnt.cnt;
You can now retrieve the counts with
select *
from tblDailyHours
order by HourBucket;
The counts give the number on site at each of the times displayed, if you want during the hour in question, we'd need to tweak this a little.
There is a working version of this code (using not very realistic data in the logging table) here: rextester.com/DYOR23344
Original Answer (Based on a single over all count)
If you're happy to search over all rows, and want the current "head count" you can use this:
select
sum(case when t.FromState = 'In' then 1 else -1) as Heads
from
MyTable t
But if you know that there will always be no-one there at midnight, you can add a where clause to prevent it looking at more rows than it needs to:
where
date(t.timestamp) = curdate()
Again, on the assumption that the head count reaches zero at midnight, you can generalise that method to get a headcount at any time as follows:
where
date(t.timestamp) = "CENSUS DATE" AND
t.timestamp <= "CENSUS DATETIME"
Obviously you'd need to replace my quoted strings with code which returned the date and datetime of interest. If the headcount doesn't return to zero at midnight, you can achieve the same by removing the first line of the where clause.

how to the min and the max date in sql

Hi i want to substruct to fields between the min and the max date heres my query:
SELECT Max(km) - MIN(km)
from positions
where deviceid = 2
and cast(devicetime as date) between MIN('2017-03-23') and Max('2017-03-23'))
but it gives an error.
PS, devicetime in the database is a datetime type, i'm using mysql
the error is:invalide group function
You do not need MIN and MAX in between:
SELECT Max(km) - MIN(km)
from positions
where deviceid = 2
and cast(devicetime as date) between '2017-03-23 00:00:00' and '2017-03-23 23:59:59'
Try this instead:
SELECT Max(km) - MIN(km)
from positions
where deviceid = 2
and cast(devicetime as date) between '2017-03-23' and '2017-03-23'
You can't use aggregation functions in the where clause, only in select, having or order by clause.
Remove min & max from where....that is not allowed. Try the below sql. Added timestamp so you get the accurate result.
SELECT Max(km) - MIN(km)
from positions
where deviceid = 2
and cast(devicetime as date) between '2017-03-23 00:00:00' and '2017-03-23 23:59:59'
In SQL Server you can use below code,
SELECT Max(km) - MIN(km) KiloMeter FROM positions
where deviceid = 2
Group by deviceid,devicetime
Having cast(devicetime as datetime) between MIN('2017-03-21') and Max('2017-03-26')
Please look in to aggregate and non-aggregate functions..
Thank you..
Your specific problem are the aggregation functions in the WHERE clause. However, you should also note that when using columns in the WHERE, it is a bad idea to use functions or type casting. That prevents the use of indexes.
So, a better version uses inequalities:
select min(km) - max(km)
from positions
where deviceid = 2 and
devicetime >= '2017-03-23' and
devicetime < '2017-03-24';
If performance is an issue, you want an index on positions(deviceid, devicetime, km).

MySql count entries without duplicate datetime

I have a table which logs all "check-ins" into a system. I want to count all "check-ins" from an user for the current day, but there's the problem that sometimes users check-in like two or three times by mistake in one minute. So I just want to count all entries with a gap from at least two minutes
My current command looks like:
"SELECT event_datetime, LEFT(tag_uid,8) AS tag_uid, count(*) as anzahl FROM events WHERE date(event_datetime) = curdate() GROUP BY tag_uid"
So I only want to count it if the gap between event_datetime is at least two minutes grouped by the tag_uid (the user)
Any ideas how to solve that?
first, you need to calculate the gap for each tag_uid and then you can set conditions as you wish. To calculate the gap you need to have a nested select commands which will get the "first checkIn" and "last checkIn" using min and max command. Use the "timediff" to calculate the time gap.
NOTE: i haven't tried the code, also I've added the select commands so you can see the values to find Errors.
the code would look similar to this:
SELECT
tbl_outer_events.event_datetime,
LEFT(tbl_outer_events.tag_uid,8) AS tag_uid,
count(tbl_outer_events.tag_uid) as anzahl,
(select min(event_datetime) from tbl_events as T1 where ( (t1.tag_uid = tbl_outer_events.tag_uid)) and (date(t1.event_datetime) = curdate()) )as `first_checkin`,
(select max(event_datetime) from tbl_events as T1 where ( (t1.tag_uid = tbl_outer_events.tag_uid)) and (date(t1.event_datetime) = curdate()))as `last_checkin`,
(select TIMEDIFF(max(event_datetime), min(event_datetime)) from tbl_events as T1 where ( (t1.tag_uid = tbl_outer_events.tag_uid)) and (date(t1.event_datetime) = curdate())) as `interval`
FROM events as tbl_outer_events
WHERE ((date(event_datetime) = curdate())
AND (select TIMEDIFF(max(event_datetime), min(event_datetime)) from tbl_events as T1 where ( (t1.tag_uid = tbl_outer_events.tag_uid)) and (date(t1.event_datetime) = curdate())) >= '00:20:00')
GROUP BY tag_uid;
PS: please provide us with sample data or desired outcome. it will help us and you to understand the issue.