how to add two millisecond column in mysql - mysql

I have table as shown below
gid code time qid
1 123 08:108 15
1 145 11:012 15
1 145 11:216 16
1 123 12:102 16
Now U want to group the 'gid' and add the two time with same code column (ex: i am taking 123, calculating the time (08:108+12:102)/2. Divided by '2' because code 123 appears two time,if it appears three time then divided by 3 this should be dynamic.
I want the result should be
gid code time
1 123 10:105
1 145 11:114
i tried using this query
SELECT sum(time) FROM results group by code; // result in integer values
and SELECT timestamp(sum(time)) FROM results group by code; // result is null

Your time field does not look like it is of the type TIME. A TIME field is in the format HH:MM:SS and doesn't allow to store milliseconds. The MySQL Documentation states that trailing fractions of seconds are allowed in date and time values, but are discarded and not stored.
Your time field looks like it is a varchar and while you can use functions like SUM() or AVG() on that, your notation seconds:milliseconds is wrong.
You can use the following query:
SELECT code,AVG(REPLACE(time,':','.')) FROM results group by code
This replaces the : in your value with ., creating a float number AVG() can handle correctly.
The result:
code AVG(REPLACE(time,':','.'))
123 10.105
145 11.114
Of course this will create more operations on the SQL server. The best way would be to change your column definition to FLOAT and store your seconds and milliseconds as a float:
code time
123 8.108
145 11.012
145 11.216
123 12.102
The result of SELECT code,AVG(time) FROM results GROUP BY code:
code AVG(time)
123 10.1050000190735
145 11.1139998435974

You can use the avg aggregate function on a time column - you'd just need to convert it back to time when you're done, and use time_format if the default format doesn't suit you:
SELECT gid, code, TIME_FORMAT(TIME(AVG(`time`)), '%H-%i.%f')
FROM mytable
GROUP BY gid, code

Related

Recursively running a MySQL function

I have a function in MySQL that needs to be run about 50 times (not a set value) in a query. the inputs are currently stored in an array such as
[1,2,3,4,5,6,7,8,9,10]
when executing the MySQL query individually it's working fine, please see below
column_name denotes the column it's getting the data for, in this case, it's a DOUBLE in the database
The second value in the MOD() function is the input I'm supplying MySQL from the aforementioned array
SELECT id, MOD(column_name, 4) AS mod_output
FROM table
HAVING mod_output > 10
To achieve the output I require* the following code works
SELECT id, MOD(column_name, 4) AS mod_output1, MOD(column_name, 5) AS mod_output2, MOD(column_name, 6) AS mod_output3
FROM table
HAVING mod_output1 > 10 AND mod_output2 > 10 AND mod_output3 > 10
However this obviously is extremely dirty, and when having not 3 inputs, but over 50, this will become highly inefficient.
Appart from calling over 50 individual querys, is there a better way to acchieve the same sort (see below) of output?
In escennce i need to supply MySQL with a list of values and have it run MOD() over all of them on a specified column.
The only data I need returned is the id's of the rows that match the MOD() functions output with the specified input (see value 2 of the MOD() function) where the output is less than 10
Please note, MOD() has been used as an example function, however, the final function required *should* be a drop in replacement
example table layout
id | column_name
1 | 0.234977
2 | 0.957739
3 | 2.499387
4 | 48.395777
5 | 9.943782
6 | -39.234894
7 | 23.49859
.....
(The title may be worded wrong, I'm not quite sure how else you'd explain what I'm trying to do here)
Use a join and derived table or temporary table:
SELECT n.n, t.id, MOD(t.column_name, n.n) AS mod_output
FROM table t CROSS JOIN
(SELECT 4 as n UNION ALL SELECT 5 UNION ALL SELECT 6 . . .
) n
WHERE MOD(t.column_name, n.n) > 10;
If you want the results as columns, you can use conditional aggregation afterwards.

If more than 10% of results are over X in mysql

I have a database table with lists of temperature readings from many locations in a number of buildings. I need a query that will give me a true or false if more than 10% of the readings in a building, taken on a date, are greater than X
I am not looking for a average. If there are 100 measurements taken in a building on a date, and 10 of them are over X (say 80 degrees) then create a flag.
The table is laid out as
Building # location # date temperature
| 123 | 555 |2016-04-08 | 68.5 |
| 123 | 556 |2016-04-08 | 70.2 |
| 123 | 557 |2016-04-08 | 65.4 |
| 888 | 999 |2013-03 22 | 80.4 |
Typically a building would have over 100 readings. There are many hundreds of building/date entries in the table
Can this be done with a single mysql query and can you share that query with me?
I obviously haven't made my question clear.
The result I am looking for is a single True or False.
If more than 10% of the results for a building/date combination were over X (say 80%) then show true, or some flag equal to true.
The known fields will be building and date. The location is not relevant, and can be ignored. So given the input of building (123) and date (2016-04-08) are more than 10% of the entries in the table that have that building number and date greater than X (e.g. 80). The only data to be tested are those for that building and date. So the query would end in:
where building_id=`123` AND date =`2016-04-08`
I am NOT looking for an average or a median. I am NOT looking to see a list of the data for that 10%. I am just looking for true or false.
You can use conditional aggregation, something like this:
select building, date,
(case when avg(temperature > x) > 0.1 then 'Y' else 'N' end) as flag
from t
group by building, date;
To return building and date, and "create a flag" for rows where more than 10% of the readings for that building on that date are over a given value X ...
SELECT r.building
, DATE(r.date)
, ( SUM(r.reading > X ) > SUM(.10) ) AS _flag
FROM myreadings r
GROUP BY r.building, DATE(r.date)
Absent more specification about the actual resultset you want to return, we're just guessing at what result set you want to return.
FOLLOWUP
Based on the update to the question... to return a row for a single building and a single date, add the WHERE clause as shown in the question. And remove expressions from the SELECT list.
SELECT ( SUM(r.reading > X ) > SUM(.10) ) AS _flag
FROM myreadings r
WHERE r.building = '123'
AND r.date >= '2016-04-08'
AND r.date < '2016-04-08' + INTERVAL 1 DAY
If there are no rows for the given building and given date, the query will return zero rows. If there is at least one row, and the number of rows that have a reading greater than X is more than 10% of the total number of rows, the query will return a single row, with _flag having a value of 1 (TRUE). Otherwise, the query will return a single row with _flag having a value of 0 (FALSE).
If you want the query to return a row even when there are no matching rows in the table, that can be accomplished with a more complex SQL statement.
If you want the query to return string values 'TRUE' or 'FALSE', that can be accomplished as well.
Again, absent an example of the resultset you are expecting to have returned, (without an actual specification which we can compare a resultset to), we're just guessing.

Add blank row to MySQL query results based upon change in column value

If I have a typical MySQL query such as:
SELECT CommentNo, CreatedDate, CreatedTime, Text FROM Comment
The results will be displayed in a simple table. But is it possible to have MySQL format the output so that a blank line can be inserted into the results based upon a change in a column?
So if my query above returns the following by default:
CommentNo CreatedDate CreatedTime Text
1 2012-08-02 15:33:27 This.
2 2012-08-02 15:34:40 That.
3 2013-06-30 19:45:48 Something else.
4 2013-06-30 21:26:01 Nothing.
5 2013-06-30 21:26:43 Was.
6 2013-07-01 13:40:32 Hello.
7 2013-07-01 14:08:25 Goodbye.
Is it possible to insert a blank row upon change of column value for CreatedDate, so I get:
CommentNo CreatedDate CreatedTime Text
1 2012-08-02 15:33:27 This.
2 2012-08-02 15:34:40 That.
3 2013-06-30 19:45:48 Something else.
4 2013-06-30 21:26:01 Nothing.
5 2013-06-30 21:26:43 Was.
6 2013-07-01 13:40:32 Hello.
7 2013-07-01 14:08:25 Goodbye.
The above is not how data would be stored in the Comment table, it is an issue of formatting query output.
Or would it be better to use a BufferedWriter in Java?
First, let me say that this type of formatting should definitely be done at the application layer and not as a query.
That said, this seems like an amusing exercise, and it isn't so hard:
select CommentNo, CreatedDate, CreatedTime, Text
from (select c.*, CreatedDate as key1, 0 as ordering
from comment c
union all
select c2.*, c.CreatedDate, 1
from (select distinct CreatedDate from comment c) c left join
comment c2
on 1 = 0
) c
order by key1, ordering, id;
Note the use of the left join in the second subquery to bring in all the columns, so it matches the select * in the first subquery. However, getting rid of the last two columns still requires listing all of them.
I have adapted the code found at: enter link description here which can easily be extended to include a line when parsing the data to write the .csv file.
// Iinitialise FileWriter object.
fileWriter = new FileWriter(fileName);
// Iinitialise CSVPrinter object/ t.
csvFilePrinter = new CSVPrinter(fileWriter, csvFileFormat);
// Create .csv file header.
csvFilePrinter.printRecord(FILE_HEADER);
// Write a new student object list to the .csv file.
for (Student student : students) {
ArrayList studentDataRecord = new ArrayList();
studentDataRecord.add(String.valueOf(student.getId()));
studentDataRecord.add(student.getFirstName());
studentDataRecord.add(student.getLastName());
studentDataRecord.add(student.getGender());
studentDataRecord.add(String.valueOf(student.getAge()));
csvFilePrinter.printRecord(studentDataRecord);
csvFilePrinter.println();
}
Although I have yet to add code to determine the change in the column value required but that is straightforward.

MySQL - get all column averages also with a 'total' average

I have a MySQL table which looks like this:
id load_transit load_standby
1 40 20
2 30 15
3 50 10
I need to do the following calculations:
load_transit_mean = (40+30+50)/3 = 40
load_standby_mean = (20+15+10)/3 = 15
total_mean = (40+15)/2 = 27.5
Is it possible to do this in a single query? What would the best design be?
I need my answer to be scalable (the real design has more rows and columns), and able to handle some rows containing NULL.
I believe this would do it:
SELECT AVG(Load_transit)
, AVG(load_standby)
, (AVG(Load_transit) + AVG(load_standby))/2.0
FROM table
The AVG() function handles NULL's in that it ignores them, if you want the NULL row to be counted in your denominator you can replace AVG() with SUM() over COUNT(*), ie:
SUM(load_transit)/COUNT(*)
Regarding scalability, manually listing them out like above is probably the simplest solution.

SQL Server 2008 Optimize FULL JOIN with ISNULL statements

HI All
I was hoping someone could help me improve a query I have to run periodically. At the moment it takes more than 40 minutes to execute. It uses the full allocated memory during this time, but CPU usage mostly meanders at 2% - 5%, every now and then jumping to 40% for a few seconds.
I have this table (simplified example):
CREATE TABLE [dbo].[dataTable]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[dteEffectiveDate] [date] NULL,
[dtePrevious] [date] NULL,
[dteNext] [date] NULL,
[Age] [int] NULL,
[Count] [int] NULL
) ON [PRIMARY]
GO
Here are some input values:
INSERT INTO [YourDB].[dbo].[dataTable]
([dteEffectiveDate]
,[dtePrevious]
,[dteNext]
,[Age]
,[Count])
VALUES
('2009-01-01',NULL,'2010-01-01',40,300),
('2010-01-01','2009-01-01', NULL,40,200),
('2009-01-01',NULL, '2010-01-01',20,100),
('2010-01-01','2009-01-01', NULL,20,50),
('2009-01-01',NULL,'2010-01-01',30,10)
GO
Each entry has a dteEffectiveDate field. In addition, each has a dtePrevious and dteNext, which reflects the dates of the nearest previous/next effective date. Now what I want is a query that will calculate the mid-value on the Count fields between successive periods, within a specific age.
So for example, in the data above, for age 40 we have 300 at 2009/01/01 and 200 at 2010/01/01 so the query should produce 250.
Note that age 30 has only one entry, 10. This is at 2009/01/01. There is no entry at 2010/01/01, but we know that data was captured at this point, so the fact that there is nothing means that 30 is 0 at this date. Hence the query should produce 5.
In order to achieve this I use a FULL JOIN of the table on itself, and use ISNULL to select values. Here is my code:
SELECT
ISNULL(T1.dteEffectiveDate,T2.dtePrevious) as [Start Date]
,ISNULL(T1.dteNext,T2.dteEffectiveDate) as [End Date]
,ISNULL(T1.Age,T2.Age) as Age
,ISNULL(T1.[Count],0) as [Count Start]
,ISNULL(T2.[Count],0) as [Count End]
,(ISNULL(T1.[Count],0)+ISNULL(T2.[Count],0))/2 as [Mid Count]
FROM
[ExpDBClient].[dbo].[dataTable] as T1
FULL JOIN [ExpDBClient].[dbo].[dataTable] as T2
ON
T2.dteEffectiveDate = T1.dteNext
AND T2.Age = T1.Age
WHERE ISNULL(T1.dteEffectiveDate,T2.dtePrevious) is not null
AND ISNULL(T1.dteNext,T2.dteEffectiveDate) is not null
GO
which outputs:
Start Date End Date Age Count Start Count End Mid Lives
2009-01-01 2010-01-01 40 300 200 250
2009-01-01 2010-01-01 20 100 50 75
2009-01-01 2010-01-01 30 10 0 5
It works perfectly, but when I run this on the actual data, which is about 7m records, it takes painfully long to execute.
Does anyone have any suggestions?
Thanks
Karl
It's hard to make a lot of recommendations.
One thing I'd would definitely recommend is indices on those columns that you use as foreign keys in your JOIN conditions, e.g.
Age
dteEffectiveDate
dteNext
Create a NONCLUSTERED index on each of those columns separately and measure again. With just a few data rows, there's no improvement measurable - but with millions of rows, it might make a difference.