How can I select one, most recent NID, per every 7 days, per UID, starting count back from today.
If today is July 11, the following table
+-----+------------+-----+
| NID | timestamp | UID |
+-----+------------+-----+
| 1 | 1341719851 | 8 | //July 7
| 2 | 1341115051 | 8 | //July 1
| 3 | 1341547051 | 8 | //July 6
| 4 | 1341719851 | 8 | //July 8
| 5 | 1341979051 | 8 | //July 11
| 6 | 1341806251 | 9 | //July 9
| 7 | 1341460651 | 9 | //July 5
| 8 | 1341892651 | 9 | //July 10
+-----+------------+-----+
Will output this:
+-----+------------+-----+
| NID | timestamp | UID |
+-----+------------+-----+
| 2 | 1341115051 | 8 | //July 1
| 5 | 1341979051 | 8 | //July 11
| 8 | 1341892651 | 9 | //July 10
+-----+------------+-----+
In the last 7 days, most recent NID for each user is '5' and '8', in the prior 7 days, most recent NID is '2', and so on...
I'm assuming, Group By will do the trick; but I don't have a clue where to start.
UPDATE
This is the query that worked, based on the top answer:
SELECT nid, timestamp, uid, weeks_ago
FROM (
SELECT nid, timestamp, uid, FLOOR(
(UNIX_TIMESTAMP()- timestamp)/604800
) weeks_ago
FROM `table`
ORDER BY timestamp DESC
) x
GROUP BY uid, weeks_ago
select nid, max(timestamp), uid, weeks_ago
from (select nid, timestamp, uid, floor(datediff(now(), from_unixtime(timestamp))/7) weeks_ago
from mytable) x
group by nid, uid, weeks_ago
select * from (
(
select nid, uid,timestamp,floor(datediff(now(), from_unixtime(timestamp))/7) as weeks,from_unixtime(timestamp) as Dt from test t1 order by dt desc limit 0,2)
Union
(
select nid, uid,timestamp,
floor(datediff(now(), from_unixtime(timestamp))/7) as weeks,from_unixtime(timestamp) as Dt
from test t2 group by weeks having weeks>0 order by dt asc limit 0,1
)
) t4
May be this resolve your issue. Try this
Related
This question already has answers here:
Using LIMIT within GROUP BY to get N results per group?
(14 answers)
Closed 6 years ago.
I have 2 tables devices and locations. I need result like latest 5 records by deviceid where admin id. as json format
tables as below:
devices table:
+-----------------------+
| id |device_id|adminid |
+-----------------------+
| 1 |eefdcdfs | admin1 |
| 2 |ffsdxwe2 | admin1 |
| 3 |aaqw35ds | admin2 |
| 4 |grfdr23f | admin2 |
| 5 |etdhy79e | admin3 |
+-----------------------+
locations table:
+-----------------------------------------------------------------+
| lid|lat |long |time |deviceid |adminid |
+----+----------+----------+-------------------+---------+--------+
| 1 |17.4425358|78.3922061|2016-06-08 12:23:24|eefdcdfs | admin1 |
| 2 |17.4425358|78.3922061|2016-06-08 12:45:24|eefdcdfs | admin1 |
| 3 |17.4425358|78.3922061|2016-06-08 11:56:24|eefdcdfs | admin1 |
| 4 |17.4425358|78.3922061|2016-06-08 12:53:24|eefdcdfs | admin1 |
| 5 |17.4425500|78.3922342|2016-06-08 12:53:34|ffsdxwe2 | admin1 |
| 6 |17.4425342|78.3922546|2016-06-08 11:55:34|ffsdxwe2 | admin1 |
| 7 |17.4425562|78.3922657|2016-06-08 12:23:34|ffsdxwe2 | admin1 |
| 8 |17.4425223|78.3922675|2016-06-08 12:12:34|ffsdxwe2 | admin1 |
| 9 |17.4424856|78.3922307|2016-06-08 12:56:48|aaqw35ds | admin2 |
| 10 |17.4425453|78.3922087|2016-06-08 13:08:30|grfdr23f | admin2 |
| 11 |17.4425472|78.3922294|2016-06-08 13:15:54|etdhy79e | admin3 |
+----+----------+----------+-------------------+---------+--------+
expected result:
+-----------------------------------------------------------------+
| lid|lat |long |time |deviceid |adminid |
+----+----------+----------+-------------------+---------+--------+
| 4 |17.4425358|78.3922061|2016-06-08 12:53:24|eefdcdfs | admin1 |
| 2 |17.4425358|78.3922061|2016-06-08 12:45:24|eefdcdfs | admin1 |
| 1 |17.4425358|78.3922061|2016-06-08 12:23:24|eefdcdfs | admin1 |
| 3 |17.4425358|78.3922061|2016-06-08 11:56:24|eefdcdfs | admin1 |
| 5 |17.4425500|78.3922342|2016-06-08 12:53:34|ffsdxwe2 | admin1 |
| 7 |17.4425562|78.3922657|2016-06-08 12:23:34|ffsdxwe2 | admin1 |
| 8 |17.4425223|78.3922675|2016-06-08 12:12:34|ffsdxwe2 | admin1 |
| 6 |17.4425342|78.3922546|2016-06-08 11:55:34|ffsdxwe2 | admin1 |
+----+----------+----------+-------------------+---------+--------+
I tried like:
select deviceid,CONCAT('[',CAST(lat AS CHAR ),',',CAST(long AS
CHAR ),'],') json from locations WHERE
admin_id='admin1'
AND `time` > DATE_SUB(NOW(),
INTERVAL 3 HOUR) ORDER BY time DESC limit 10;
This is a mysql query used in my program.Use jointo concat two table
Distinct will avoid replication.
qry=Select distinct * from tbl_1 join tbl_2 on tbl_1 .Som_Id =tbl_2 .S_Id order by DATE1";
This is an example only for reference. By making some changes and you can achieve your result.
One way to do this is using variables. Do a sub query to get the possible rows you are interested in, ordered by the time descending. Add 1 to the counter for each one where the device id is the same. The use those results as a sub query and return those where the counter is 5 or less.
Something like this:-
SELECT lid,
lat,
`long`,
`time`,
deviceid,
adminid
FROM
(
SELECT lid,
lat,
`long`,
`time`,
deviceid,
adminid,
#cnt:=IF(#deviceid = deviceid, #cnt + 1, 1) AS cnt,
#deviceid := deviceid
FROM
(
SELECT locations.lid,
locations.lat,
locations.`long`,
locations.`time`,
locations.deviceid,
locations.adminid
FROM devices
INNER JOIN locations
ON devices.device_id = locations.deviceid
WHERE locations.adminid = 'admin1'
AND locations.`time` > DATE_SUB(NOW(), INTERVAL 3 HOUR)
ORDER BY locations.deviceid, locations.time DESC
) sub0
CROSS JOIN
(
SELECT #cnt:=0, #deviceid:=''
) sub1
) sub2
WHERE cnt <= 5
EDIT
An alternative is to use and abuse the GROUP_CONCAT statement to get all the times for each device, then SUBSTRING_INDEX to get the 5th time (if less than 5 times it will get the last one which is fine in this case), and then join that against your tables to get the records with the appropriate times:-
SELECT locations.lid,
locations.lat,
locations.`long`,
locations.`time`,
locations.deviceid,
locations.adminid
FROM devices
INNER JOIN locations
ON devices.device_id = locations.deviceid
INNER JOIN
(
SELECT locations.deviceid,
GROUP_CONCAT(locations.`time` ORDER BY locations.time DESC) AS times
FROM devices
INNER JOIN locations
ON devices.device_id = locations.deviceid
WHERE locations.adminid = 'admin1'
AND locations.`time` > DATE_SUB(NOW(), INTERVAL 3 HOUR)
GROUP BY locations.deviceid
) sub0
ON locations.deviceid = sub0.deviceid
AND locations.`time` >= SUBSTRING_INDEX(SUBSTRING_INDEX(sub0.times, ',', 6), ',', -1)
WHERE locations.adminid = 'admin1'
ID Timestamp Value
1 11:59.54 10
1 12.04.00 20
1 12.12.00 31
1 12.16.00 10
1 12.48.00 05
I want the result set as
ID Timestamp Value
1 11:59.54 10
1 12:00:00 10
1 12.04.00 20
1 12.12.00 31
1 12:15:00 31
1 12:16.00 10
1 12:30:00 10
1 12:45:00 10
1 12.48.00 05
More coffee will probably lead to a simpler solution, but consider the the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,timestamp TIMESTAMP
,value INT NOT NULL
);
INSERT INTO my_table VALUES
(1 ,'11:59:54',10),
(2 ,'12:04:00',20),
(3 ,'12:12:00',31),
(4 ,'12:16:00',10),
(5 ,'12:48:00',05);
... in addition, I have a table of integers, that looks like this:
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
So...
SELECT a.timestamp
, b.value
FROM
( SELECT x.*
, MIN(y.timestamp) min_timestamp
FROM
( SELECT timestamp
FROM my_table
UNION
SELECT SEC_TO_TIME((i2.i*10+i1.i)*900)
FROM ints i1
, ints i2
WHERE SEC_TO_TIME((i2.i*10+i1.i)*900)
BETWEEN (SELECT MIN(timestamp) FROM my_table)
AND (SELECT MAX(timestamp) FROM my_table)
ORDER
BY timestamp
) x
LEFT
JOIN my_table y
ON y.timestamp >= x.timestamp
GROUP
BY x.timestamp
) a
JOIN my_table b
ON b.timestamp = min_timestamp;
+-----------+-------+
| timestamp | value |
+-----------+-------+
| 11:59:54 | 10 |
| 12:00:00 | 20 |
| 12:04:00 | 20 |
| 12:12:00 | 31 |
| 12:15:00 | 10 |
| 12:16:00 | 10 |
| 12:30:00 | 5 |
| 12:45:00 | 5 |
| 12:48:00 | 5 |
+-----------+-------+
The idea is as follows. Use SERIES_GENERATE() to generate the missing time stamps with the 15 minute intervals and and union it with the existing data your table T. Now you would want to use LAST_VALUE with IGNORE NULLS. IGNORE NULLS is not implemented in HANA, therefore you have to do a bit of a workaround. I use COUNT() as a window function to count the non null values. I do the same on the original data and then join both on the count. This way I repeat the last non-null value.
select X.ID, X.TIME, Y.VALUE from (
select ID, TIME, value,
count(VALUE) over (order by TIME rows between unbounded preceding and current row) as CNT
from (
--add the missing 15 minute interval timestamps
select 1 as ID, GENERATED_PERIOD_START as TIME, NULL as VALUE
from SERIES_GENERATE_TIME('INTERVAL 15 MINUTE', '12:00:00', '13:00:00')
union all
select ID, TIME, VALUE from T
)
) as X join (
select ID, TIME, value,
count(value) over (order by TIME rows between unbounded preceding and current row) as CNT
from T
) as Y on X.CNT = Y.CNT
I have a select result like this:
ID | DATE
----------------
10 | 2014-07-23
7 | 2014-07-24
8 | 2014-07-24
9 | 2014-07-24
1 | 2014-07-25
2 | 2014-07-25
6 | 2014-07-25
3 | 2014-07-26
4 | 2014-07-27
5 | 2014-07-28
The result above is ordered by date. Now, I want to select the one previous row before:
2 | 2014-07-25
Which is:
1 | 2014-07-25
In case I don't know the exact ID and the conditional code must be compatible with if I want to select a previous row of:
3 | 2014-07-26
Which is:
6 | 2014-07-25
What condition should I use?
UPDATE
Tried this:
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, t1.*
FROM table t1
Then I got this:
RANK | ID | DATE
----------------
1 | 10 | 2014-07-23
2 | 7 | 2014-07-24
3 | 8 | 2014-07-24
4 | 9 | 2014-07-24
5 | 1 | 2014-07-25
6 | 2 | 2014-07-25
7 | 6 | 2014-07-25
8 | 3 | 2014-07-26
9 | 4 | 2014-07-27
10 | 5 | 2014-07-28
Then I tried this:
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, t1.*
FROM table t1
WHERE rank < 3;
I got this error: Unknown column 'rank' in 'where clause'.
Here's one way...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(ID INT NOT NULL PRIMARY KEY
,DATE DATE NOT NULL
);
INSERT INTO my_table VALUES
(10 ,'2014-07-23'),
(7 ,'2014-07-24'),
(8 ,'2014-07-24'),
(9 ,'2014-07-24'),
(1 ,'2014-07-25'),
(2 ,'2014-07-25'),
(6 ,'2014-07-25'),
(3 ,'2014-07-26'),
(4 ,'2014-07-27'),
(5 ,'2014-07-28');
SELECT a.id
, a.date
, b.id b_id
, b.date b_date
FROM
( SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON (y.date < x.date)
OR (y.date = x.date AND y.id <= x.id)
GROUP
BY x.date
, x.id
) a
LEFT
JOIN
( SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON (y.date < x.date)
OR (y.date = x.date AND y.id <= x.id)
GROUP
BY x.date
, x.id
) b
ON b.rank = a.rank - 1;
+----+------------+------+------------+
| id | date | b_id | b_date |
+----+------------+------+------------+
| 10 | 2014-07-23 | NULL | NULL |
| 7 | 2014-07-24 | 10 | 2014-07-23 |
| 8 | 2014-07-24 | 7 | 2014-07-24 |
| 9 | 2014-07-24 | 8 | 2014-07-24 |
| 1 | 2014-07-25 | 9 | 2014-07-24 |
| 2 | 2014-07-25 | 1 | 2014-07-25 |
| 6 | 2014-07-25 | 2 | 2014-07-25 |
| 3 | 2014-07-26 | 6 | 2014-07-25 |
| 4 | 2014-07-27 | 3 | 2014-07-26 |
| 5 | 2014-07-28 | 4 | 2014-07-27 |
+----+------------+------+------------+
... but you can also do this (quicker) with variables.
You can add a row id to the select like this
SELECT #rowid:=#rowid+1 as rowid,
t1.* FROM yourdatabase.tablename t1, (SELECT #rowid:=0) as rowids;
Then you can run a simple query to get the lower rowid from the input.
This uses a sub query that joins the table against itself, where on one side it is the date you are checking and matching against smaller dates. It uses MAX to get the highest smaller date.
This is then joined against another sub query that gets the highest ID for each date, which also joins against the table itself to get the other details from that row.
SELECT table.*
FROM table
INNER JOIN
(
SELECT MAX(a.date) AS latest_prev_date
FROM table1 a
INNER JOIN table1 b
ON a.date > b.date
WHERE a.date = '2014-07-26'
) sub0
ON table.date = sub0.latest_prev_date
INNER JOIN
(
SELECT date, MAX(ID) AS latest_prev_id
FROM table1
GROUP BY date
) sub1
ON table.ID = sub1.latest_prev_id
AND sub1.date = sub0.latest_prev_date
if you want to use a user_defined_variable this is a way to do it.
SELECT
tab.id, temp.id, temp.date
FROM
(
SELECT
#A:=#A + 1 AS rank_col, t.date, t.id
FROM
myTable t
CROSS JOIN (SELECT #A:=0) join_table
) AS tab
LEFT JOIN
(
SELECT
#B:=#B + 1 AS rank_col, t2 . *
FROM myTable t2
CROSS JOIN (SELECT #B:=0) join_table1
) temp ON temp.rank_col = tab.rank_col - 1;
DEMO
I have a table like this:
| id | date | user_id |
----------------------------------------------------
| 1 | 2008-01-01 | 10 |
| 2 | 2009-03-20 | 15 |
| 3 | 2008-06-11 | 10 |
| 4 | 2009-01-21 | 15 |
| 5 | 2010-01-01 | 10 |
| 6 | 2011-06-01 | 10 |
| 7 | 2012-01-01 | 10 |
| 8 | 2008-05-01 | 15 |
I’m looking for a solution how to select user_id where the difference between MIN and MAX dates is more than 3 yrs. For the above data I should get:
| user_id |
-----------------------
| 10 |
Anyone can help?
SELECT user_id
FROM mytable
GROUP BY user_id
HAVING MAX(`date`) > (MIN(`date`) + INTERVAL '3' YEAR);
Tested here: http://sqlize.com/MC0618Yg58
Similar to bernie's approach, I'd keep date formats native. I'd also probably list the MAX first as to avoid an ABS call (secure a positive number is always returned).
SELECT user_id
FROM my_table
WHERE DATEDIFF(MAX(date),MIN(date)) > 365
DATEDIFF just returns delta (in days) between two given date fields.
SELECT user_id
FROM (SELECT user_id, MIN(date) m0, MAX(date) m1
FROM table
GROUP by user_id)
HAVING EXTRACT(YEAR FROM m1) - EXTRACT(YEAR FROM m0) > 3
SELECT A.USER_ID FROM TABLE AS A
JOIN TABLE AS B
ON A.USER_ID = B.USER_ID
WHERE DATEDIFF(A.DATE,B.DATE) > 365
I've got MySQL table
CREATE TABLE cms_webstat (
ID int NOT NULL auto_increment PRIMARY KEY,
TIMESTAMP_X timestamp DEFAULT CURRENT_TIMESTAMP,
# ... some other fields ...
)
which contains statistics about site visitors.
For getting visits per hour I use
SELECT
hour(TIMESTAMP_X) as HOUR
, count(*) AS HOUR_STAT
FROM cms_webstat
GROUP BY HOUR
ORDER BY HOUR DESC
which gives me
| HOUR | HOUR_STAT |
| 24 | 15 |
| 23 | 12 |
| 22 | 9 |
| 20 | 3 |
| 18 | 2 |
| 15 | 1 |
| 12 | 3 |
| 9 | 1 |
| 3 | 5 |
| 2 | 7 |
| 1 | 9 |
| 0 | 12 |
And I'd like to get following:
| HOUR | HOUR_STAT |
| 24 | 15 |
| 23 | 12 |
| 22 | 9 |
| 21 | 0 |
| 20 | 3 |
| 19 | 0 |
| 18 | 2 |
| 17 | 0 |
| 16 | 0 |
| 15 | 1 |
| 14 | 0 |
| 13 | 0 |
| 12 | 3 |
| 11 | 0 |
| 10 | 0 |
| 9 | 1 |
| 8 | 0 |
| 7 | 0 |
| 6 | 0 |
| 5 | 0 |
| 4 | 0 |
| 3 | 5 |
| 2 | 7 |
| 1 | 9 |
| 0 | 12 |
How should I modify the query to get such result (with one mysql query, without creating temporary tables)?
Is it possible to get such result with one MySQL query?
Create another table with a single column,
CREATE TABLE hours_list (
hour int NOT NULL PRIMARY KEY
)
Fill it with all 24 hours.
Then do a join on that table to fill in the zeroes.
SELECT
hs.hour as HOUR, COUNT(ws.ID) AS HOUR_STAT
FROM hours_list hs
LEFT JOIN cms_webstat ws ON hs.hour = hour(ws.TIMESTAMP_X)
GROUP BY hs.hour
ORDER BY hs.hour DESC
This is just the 'why it is not returning` part. Marcus' answer covers the 'how to' part.
The SQL
SELECT
hour(TIMESTAMP_X) as HOUR
, count(*) AS HOUR_STAT
FROM cms_webstat
GROUP BY HOUR
ORDER BY HOUR DESC
gets the count of the records per hour, for the timestamps present in the table
It does not give the details of what is not present in the table. Since there is no recors for the timestamp corresponding to the hour 8 (from your example) the SQL does not return any records.
I've finaly found the answer.
Maybe I'm insane, but this works.
SELECT HOUR, max(HOUR_STAT) as HOUR_STAT FROM (
(
SELECT HOUR(TIMESTAMP_X) as HOUR, count(*) as HOUR_STAT
FROM cms_webstat
WHERE date(TIMESTAMP_X) = date(now())
)
UNION (SELECT 0 as HOUR, 0)
UNION (SELECT 1 as HOUR, 0)
UNION (SELECT 2 as HOUR, 0)
UNION (SELECT 3 as HOUR, 0)
UNION (SELECT 4 as HOUR, 0)
UNION (SELECT 5 as HOUR, 0)
UNION (SELECT 6 as HOUR, 0)
UNION (SELECT 7 as HOUR, 0)
UNION (SELECT 8 as HOUR, 0)
UNION (SELECT 9 as HOUR, 0)
UNION (SELECT 10 as HOUR, 0)
UNION (SELECT 11 as HOUR, 0)
UNION (SELECT 12 as HOUR, 0)
UNION (SELECT 13 as HOUR, 0)
UNION (SELECT 14 as HOUR, 0)
UNION (SELECT 15 as HOUR, 0)
UNION (SELECT 16 as HOUR, 0)
UNION (SELECT 17 as HOUR, 0)
UNION (SELECT 18 as HOUR, 0)
UNION (SELECT 19 as HOUR, 0)
UNION (SELECT 20 as HOUR, 0)
UNION (SELECT 21 as HOUR, 0)
UNION (SELECT 22 as HOUR, 0)
UNION (SELECT 23 as HOUR, 0)
)
AS `combined_table`
GROUP BY HOUR
ORDER BY HOUR DESC
One MySQL query as desired.
$sql = 'SELECT g, MAX(v) AS v, MAX(c) AS c FROM (';
$sql .= '(SELECT DATE_FORMAT(viewed, \'%d.%m.%Y\') AS g, COUNT(1) AS v, 0 AS c FROM '.$this->prefix.'view WHERE campaignid IN ('.join(', ',$ids).') GROUP BY g)';
$sql .= ' UNION (SELECT DATE_FORMAT(clicked, \'%d.%m.%Y\') AS g, 0 AS v, COUNT(1) AS c FROM '.$this->prefix.'clicks WHERE campaignid IN ('.join(', ',$ids).') GROUP BY g)';
$today = strtotime("00:00:00");
for ($i=$today; $i>=time()-30*86400; $i-=86400) {
$sql .= ' UNION (SELECT \''.date('d.m.Y',$i).'\' AS g, 0 AS v, 0 AS c)';
}
$sql .= ') AS tmp GROUP BY g ORDER BY g DESC';
$chart = DB::getAll($sql);
p($chart);
Thanks! Made it! From 2 tables, clicks and views, joined.. works. ajaxel.com