I have following table for my web application and i want add another column to get time difference between current row and previous row. How can i achieve it?
Currently here is my sql call from php Application
$stmt = $db->prepare("SELECT device,lat,lon, speed, mode, DATE (`currentTime`) ,TIME_FORMAT(`currentTime`, '%H:%i:%s')
FROM myTable
WHERE device=? limit ?");
$stmt ->bind_param('ii', $device_Number ,$limit);
$stmt ->bind_result($device, $lat, $lon, $speed, $mode, $currentDate, $currentTime);
$stmt ->execute();
Here I give sample datas with datetime difference, here you are saving the data in 2 different column
so take date difference as 2 column 'timedifference' and 'daydifference'
testtime table
id date1 time1
1 2017-08-14 01:06:11
2 2017-08-14 01:09:13
3 2017-08-14 01:16:10
4 2017-08-14 01:21:00
5 2017-08-15 01:21:00
6 2017-08-15 02:13:00
Mysql Query is
SELECT A.id, A.time1, TIMESTAMPDIFF(SECOND,A.time1,B.time1) AS timedifference,
TIMESTAMPDIFF(DAY,A.date1,B.date1) AS daydifference
FROM testtime A INNER JOIN testtime B ON B.id = (A.id + 1)
ORDER BY A.id ASC
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(device INT NOT NULL
,lat DECIMAL(10,6) NOT NULL
,lon DECIMAL(10,6) NOT NULL
,speed DECIMAL(5,2)
,mode INT NOT NULL
,dt DATETIME NOT NULL
,PRIMARY KEY(device,dt)
);
INSERT INTO my_table VALUES
(117,1.415738,103.82360,28.8,3,'2017-07-12 22:07:40'),
(117,1.424894,103.82561,31.9,3,'2017-07-12 22:08:41'),
(117,1.429965,103.82674,10.9,3,'2017-07-12 22:09:47'),
(117,1.430308,103.82873, 5.2,3,'2017-07-12 22:10:47'),
(117,1.430542,103.83278,13.9,3,'2017-07-12 22:11:48'),
(117,1.430537,103.83325, 3.2,3,'2017-07-12 22:12:47');
SELECT x.*
, SEC_TO_TIME(TIME_TO_SEC(x.dt)-TIME_TO_SEC(MAX(y.dt))) diff
FROM my_table x
LEFT
JOIN my_table y
ON y.device = x.device
AND y.dt < x.dt
GROUP
BY x.device
, x.dt;
+--------+----------+------------+-------+------+---------------------+----------+
| device | lat | lon | speed | mode | dt | diff |
+--------+----------+------------+-------+------+---------------------+----------+
| 117 | 1.415738 | 103.823600 | 28.80 | 3 | 2017-07-12 22:07:40 | NULL |
| 117 | 1.424894 | 103.825610 | 31.90 | 3 | 2017-07-12 22:08:41 | 00:01:01 |
| 117 | 1.429965 | 103.826740 | 10.90 | 3 | 2017-07-12 22:09:47 | 00:01:06 |
| 117 | 1.430308 | 103.828730 | 5.20 | 3 | 2017-07-12 22:10:47 | 00:01:00 |
| 117 | 1.430542 | 103.832780 | 13.90 | 3 | 2017-07-12 22:11:48 | 00:01:01 |
| 117 | 1.430537 | 103.833250 | 3.20 | 3 | 2017-07-12 22:12:47 | 00:00:59 |
+--------+----------+------------+-------+------+---------------------+----------+
6 rows in set (0.00 sec)
Related
I have 2 tables in my demos database, in which I join them based on 2 keys, here I would like to count distinct of matched result,
my first table
MariaDB [demos]> select * from main_info;
+------+------+-------+-------+----------+
| key1 | key2 | info1 | info2 | date |
+------+------+-------+-------+----------+
| 1 | 1 | 15 | 90 | 20120501 |
| 1 | 2 | 14 | 92 | 20120601 |
| 1 | 3 | 15 | 82 | 20120801 |
| 2 | 1 | 17 | 90 | 20130302 |
| 2 | 2 | 16 | 88 | 20130601 |
+------+------+-------+-------+----------+
5 rows in set (0.00 sec)
And my second table
MariaDB [demos]> select * from product1;
+------+------+--------+--------------+
| key1 | key2 | serial | product_data |
+------+------+--------+--------------+
| 1 | 1 | 0 | NaN |
| 1 | 1 | 1 | NaN |
| 1 | 1 | 2 | NaN |
| 1 | 1 | 3 | NaN |
| 1 | 2 | 0 | 12.556 |
| 1 | 2 | 1 | 13.335 |
| 1 | 3 | 1 | NaN |
| 1 | 3 | 2 | 13.556 |
| 1 | 3 | 3 | 14.556 |
| 2 | 1 | 0 | 12.556 |
| 2 | 1 | 1 | 13.553 |
| 2 | 1 | 2 | NaN |
+------+------+--------+--------------+
12 rows in set (0.00 sec)
So distinct count of field serial is as follows, where serial is not zero
MariaDB [demos]> select count(distinct a.key1,a.key2) as serial_count from main_info a,product1 b where a.key1=b.key1 and a.key2=b.key2 and b.serial !=0;
+--------------+
| serial_count |
+--------------+
| 4 |
+--------------+
1 row in set (0.00 sec)
and distinct count of field product_data is as follows where product_data is not NaN
MariaDB [demos]> select count(distinct a.key1,a.key2) as product_count from main_info a,product1 b where a.key1=b.key1 and a.key2=b.key2 and b.product_data !='NaN';
+---------------+
| product_count |
+---------------+
| 3 |
+---------------+
1 row in set (0.00 sec)
In my original application I have many columns in my table, finally I would like to get result like below using single query, at present I am doing multiple query using PHP which is taking long time because for example if I have 100 columns, I have to execute function 100 times like above, so please someone guide me to speed up my application
+--------------+---------------+
| serial_count |product_count |
+--------------+---------------+
| 4 | 3 |
+--------------+---------------+
Below is structure of tables
DROP TABLE IF EXISTS `main_info`;
CREATE TABLE `main_info` (
`key1` int(11) NOT NULL,
`key2` int(11) NOT NULL,
`info1` int(11) NOT NULL,
`info2` int(11) NOT NULL,
`date` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
LOCK TABLES `main_info` WRITE;
INSERT INTO `main_info` VALUES (1,1,15,90,20120501),(1,2,14,92,20120601),(1,3,15,82,20120801),(2,1,17,90,20130302),(2,2,16,88,20130601);
UNLOCK TABLES;
DROP TABLE IF EXISTS `product1`;
CREATE TABLE `product1` (
`key1` int(11) NOT NULL,
`key2` int(11) NOT NULL,
`serial` int(11) NOT NULL,
`product_data` varchar(1000) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
LOCK TABLES `product1` WRITE;
INSERT INTO `product1` VALUES (1,1,0,'NaN'),(1,1,1,'NaN'),(1,1,2,'NaN'),(1,1,3,'NaN'),(1,2,0,'12.556'),(1,2,1,'13.335'),(1,3,1,'NaN'),(1,3,2,'13.556'),(1,3,3,'14.556'),(2,1,0,'12.556'),(2,1,1,'13.553'),(2,1,2,'NaN');
UNLOCK TABLES;
I didn't understand this why datetype changed, if I run on terminal
$ mysql -u root -p demos -e 'select key1,if(key1 !=0,key1,999.999) as `test1` from main_info'
Enter password:
+------+-------+
| key1 | test1 |
+------+-------+
| 1 | 1.000 |
| 1 | 1.000 |
| 1 | 1.000 |
| 2 | 2.000 |
| 2 | 2.000 |
+------+-------+
I expect it should be integer if IF condition is true
You can do it in a single query using conditional aggregation:
select count(distinct if(b.product_data !='NaN',a.key1, null),
if(b.product_data !='NaN',a.key2, null)) as product_count,
count(distinct if(b.serial !=0,a.key1, null),
if(b.serial !=0,a.key2, null)) as serial_count
from main_info a
inner join product1 b on a.key1=b.key1 and a.key2=b.key2
Output:
product_count serial_count
-----------------------------
3 4
Demo here
Edit: Credit goes to #Paul Spiegel
The query can be simplified using CONCAT:
select count(distinct if(b.product_data !='NaN',
CONCAT(a.key1, ',', a.key2),
null)) as product_count,
count(distinct if(b.serial !=0,
CONCAT(a.key1, ',', a.key2),
null)) as serial_count
from main_info a
inner join product1 b on a.key1=b.key1 and a.key2=b.key2
I'm trying to add bonus points for users every 2 days. But I'm not sure how to join the users table and get the user_bonus value which indicates the last time a user received a bonus. Can this be done in one query?
Insert query:
INSERT INTO points_plus (user_id, points) VALUES (?,?);
This would be the 2 day interval which needs to be checked:
user_bonus <= NOW() - INTERVAL 2 DAY
users table(part of it)
+-------------+--------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+-------------------+-----------------------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| weekly_mail | datetime | YES | | NULL | |
| user_bonus | datetime | YES | | NULL | |
| join_date | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+--------------+------+-----+-------------------+-----------------------------+
points_plus table
+----+---------+--------+---------------------+
| id | user_id | points | date |
+----+---------+--------+---------------------+
| 18 | 13 | 300 | 2015-10-07 12:06:39 |
| 43 | 13 | 99 | 2015-10-18 14:16:50 |
| 45 | 13 | 103 | 2015-10-19 05:49:49 |
+----+---------+--------+---------------------+
You can try this.
INSERT INTO points_plus (user_id, points)
SELECT id, $points
FROM users
WHERE user_bonus <= NOW() - INTERVAL 2 DAY
I have two tables t1, t2 and the following query:
SELECT t2.year,
Count(t1.id) AS count
FROM t1,
t2
WHERE t2.t1id = t1.id
AND t2.year IN ( 1995, 1996, 1997, 1998,
1999, 2000 )
GROUP BY t2.year
ORDER BY t1.year
Which results in:
+----------+--------+
| year | count |
+----------+--------+
| 1995 | 1 |
| 1998 | 3 |
| 1999 | 3 |
| 2000 | 28 |
+----------+--------+
And as you can see some years are missing. Is it possible to rewrite this query such that it results in?
+----------+--------+
| year | count |
+----------+--------+
| 1995 | 1 |
| 1996 | 0 |
| 1997 | 0 |
| 1998 | 3 |
| 1999 | 3 |
| 2000 | 28 |
+----------+--------+
I could use php and check which rows are missing to fill in the missing gaps, but that doesn't seem very efficient.. Any ideas?
edit
t1
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(128) | NO | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
t2
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| t1id | int(11) | NO | | NULL | |
| year | int(11) | NO | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
For example:
t1
+----------+---------+
| id | name |
+----------+---------+
| 1 | john |
| 2 | bob |
| .. | .. |
+----------+---------+
t2
+----------+---------+---------+
| id | t1id | year |
+----------+---------+---------+
| 100 | 1 | 1995 |
| 101 | 2 | 1998 |
| 103 | 3 | 1998 |
| .. | .. | .. |
+----------+---------+---------+
Where after the combination I end up with:
+----------+---------+
| id | year |
+----------+---------+
| 100 | 1995 |
| 101 | 1998 |
| 103 | 1998 |
| .. | .. |
+----------+---------+
SELECT t2.year,
IF(Count(t1.id) > 0, Count(t1.id), 0)
FROM t1,
t2
WHERE t2.t1id = t1.id
AND t2.year IN ( 1995, 1996, 1997, 1998,
1999, 2000 )
GROUP BY t2.year
ORDER BY t1.year
Without a source of all possible years that your query could cover you are going to have to use php to do this. One approach would could look something like this.
function getCountsForRange(\PDO $dbConn, $startYear, $endYear){
$ret = array_fill_keys(range($startYear, $endYear), 0);
$stmt = $dbConn->prepare("SELECT t2.year,Count(t1.id) AS count ".
"FROM t1,t2 ".
"WHERE t2.t1id = t1.id AND t2.year between ? and ? ".
"GROUP BY t2.year ORDER BY t1.year");
$stmt->execute([$startYear, $endYear]);
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)){
$ret[$row["year"]] = $row["count"];
}
return $ret;
}
create table yrCheat
( year int not null
);
create table t1
( -- forgive lack of PK
id int not null,
name varchar(128) not null
);
create table t2
( -- forgive lack of PK
id int not null,
t1id int not null,
year int not null
);
insert t1(id,name) values (100,'john'),(101,'bob'),(102,'sally');
insert t2(id,t1id,year) values (100,1,1995),(101,2,1998),(101,3,1998),(101,4,1998);
insert into yrCheat (year) values (1990),(1991),(1992),(1993),(1994),(1995),(1996),(1997),(1998),(1999),(2000);
-- etc
select yc.year,count(t1.id) as count
from yrCheat yc
left join t2
on t2.year=yc.year -- and yc.year in (1995,1996,1997,1998,1999,2000)
left join t1
on t1.id=t2.id
where yc.year in (1995,1996,1997,1998,1999,2000)
group by yc.year
order by yc.year
+------+-------+
| year | count |
+------+-------+
| 1995 | 1 |
| 1996 | 0 |
| 1997 | 0 |
| 1998 | 3 |
| 1999 | 0 |
| 2000 | 0 |
+------+-------+
6 rows in set (0.00 sec)
You will need to handle the empty rows pragmatically or in the query itself depending on the situation.
See:
MySQL GROUP BY and Fill Empty Rows
or
Populating query results with empty rows
For some ideas.
I have this table that i use to query by grouping via station_id.
+------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------+------+-----+---------+-------+
| id | varchar(50) | NO | PRI | NULL | |
| station_id | tinyint(3) | NO | | NULL | |
| game_type_id | smallint(1) | NO | MUL | NULL | |
| price | decimal(10,2) | YES | | 0.00 | |
| date_created | datetime | YES | MUL | NULL | |
| bet_no1 | tinyint(2) | YES | | 0 | |
| bet_no2 | tinyint(2) | YES | | 0 | |
+------------------+---------------+------+-----+---------+-------+
Here is the query i use to display it on a table using GROUP BY station_id
SELECT station_id,
COUNT(*) as bet_counts,
FORMAT(SUM(price),2) as gross
FROM bets
WHERE bet_void=0
AND date_created >= '2013-02-12 00:00:00'
AND date_created < '2013-02-23 00:00:00'
GROUP BY station_id
The query will give me.
+------------+------------+-------+
| station_id | bet_counts | gross |
+------------+------------+-------+
| 1 | 14 | 16.00 |
| 2 | 5 | 5.00 |
| 7 | 11 | 11.00 |
+------------+------------+-------+
But i also have another query that counts each specific bets( game_type_id ) from each station_id. I usually query this inside the a looping statement.
SELECT COUNT(*) as count
FROM bets
WHERE game_type_id = 1
AND station_id = {station_id from first query}
AND date_created >= '2013-02-12 00:00:00'
AND date_created < '2013-02-23 00:00:00'
My question is, how can i make this in one query and still use the GROUP BY station_id and also get the count of bets on each game_type_id? Something like this result.
+------------+------------+-------+-------------------------+-------------------------+
| station_id | bet_counts | gross | count_of_game_type_id_1 | count_of_game_type_id_2 |
+------------+------------+-------+-------------------------+-------------------------+
| 1 | 14 | 16.00 | 10 | 4 |
| 2 | 5 | 5.00 | 3 | 2 |
| 7 | 11 | 11.00 | 11 | 0 |
+------------+------------+-------+-------------------------+-------------------------+
You can do this by joining the results together. However, the logic in the two queries is very similar, so you can combine them into a single aggregation query:
SELECT station_id,sum(case when bet_void = 0 then 1 else 0 end) as bet_counts,
FORMAT(SUM(case when bet_void = 0 then price else 0 end),2) as gross,
sum(case when game_type_id = 1 then 1 else 0 end) as count
FROM bets b
where date_created >= '2013-02-12 00:00:00' AND date_created < '2013-02-23 00:00:00'
GROUP BY station_id
I am trying to combine these two tables then order it by one column (stamp), and aliasing the second table's id field. I've tried joins, merges, but nothing seems to work. I am also trying to group them by the mm
Table note
id | mm | stamp |
==========================
1 | 5 | 2009-12-11 |
2 | 33 | 2010-09-10 |
3 | 22 | 2011-07-08 |
4 | 1 | 2012-05-06 |
Table note_admin
id | mm | stamp |
==========================
1 | 5 | 2009-08-15 |
2 | 5 | 2011-11-11 |
3 | 5 | 2012-01-08 |
4 | 35 | 2012-02-06 |
Query I thought would work:
(SELECT * FROM note WHERE mm=5)
UNION
(SELECT id AS a_id, mm, stamp FROM note_admin WHERE mm=5)
ORDER BY stamp DESC
Expected Result
id | a_id | mm | stamp |
================================
| 3 | 5 | 2012-01-08 |
| 2 | 5 | 2011-11-11 |
1 | | 5 | 2009-12-11 |
| 1 | 5 | 2009-08-15 |
I don't even know if this is possible. I found a way to sort this in PHP but it would be much easier if it can be done in mySQL. Thanks.
I think you mean this, note the same 4 column names (not sure about the order by though):
(SELECT a_id as NULL, note.* FROM note WHERE mm=5) UNION (SELECT id AS a_id, NULL as id, mm, stamp FROM note_admin WHERE mm=5) ORDER BY stamp DESC