I want to give number to occurrence in sql result
for example I have table like this:
ID| Name | Login Date
1 |John | 12/1/2013
2 |John | 15/1/2013
3 |Ben | 12/2/2013
Now I want additional column to know what times they log in:
ID| Name | Visit times
1 |John |1
2 |John |2
3 |Ben |1
SELECT t1.ID
, t1.Name
(
SELECT COUNT(t2.ID)
FROM tablename AS t2
WHERE t1.Name = t2.Name
and t2.ID <= t1.ID
) AS 'Visit times'
FROM tablename AS t1;
Take a look at the example below.
create table log(id integer, name varchar(100), time datetime);
insert into log values (1, 'John', '2013-01-12 00:00:00');
insert into log values (2, 'John', '2013-01-15 00:00:00');
insert into log values (3, 'Ben', '2013-02-12 00:00:00');
SELECT #row_num := IF(#prev_value=l.name,#row_num+1,1) AS RowNumber
,#prev_value := l.name
FROM log l,
(SELECT #row_num := 1) x,
(SELECT #prev_value := '') y
ORDER BY l.name, l.time DESC
your problem has been solved in this so post.
adapted to your tables:
CREATE TABLE IF NOT EXIST staging_table (
id
, name
, visit_sequence
)
SELECT id
, name
, (
CASE name
WHEN #curType THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curName := name
END
) + 1 AS visit_sequence
, login_date
FROM your_table
, (SELECT #curRow := 0, #curNamee := '') r
ORDER BY name ASC
, login_date ASC
;
ALTER TABLE your_table ADD visit_sequence INTEGER AFTER login_date;
UPDATE your_table yt
, staging_table st
SET yt.visit_sequence = st.visit_sequence
WHERE st.id = yt.id
;
Related
I have a mysql table capturing state information for a signal every minute in MySQL table as follows:
ID | state | timestamp |
--------------------------------------
'sig1'| 'red' | '2017-07-10 15:30:21'
'sig1'| 'green' | '2017-07-10 15:31:26'
'sig1'| 'green' | '2017-07-10 15:32:24'
'sig1'| 'red' | '2017-07-10 15:33:29'
'sig1'| 'red' | '2017-07-10 15:34:30'
'sig1'| 'red' | '2017-07-10 15:35:15'
I need to come up with a query where it result should be the most recent time 'sig1' was in 'red' state for more than 5 minutes consecutively, the output of the query should be
ID | state| duration | start_time | end_time
So if you guys can help me with the query, that would be great!
cheers!
You can try something like this:
SELECT t.id,t.consecutive,t.state
,COUNT(*) consecutive_count
,MIN(timestamp) start_time
,MAX(timestamp) end_time
,TIMEDIFF(MAX(timestamp), MIN(timestamp)) AS diff /* for ckeck*/
FROM (SELECT a.* ,
#r:= CASE WHEN #g = a.state AND #h=a.id THEN #r ELSE #r + 1 END consecutive,
#g:= a.state g,
#h:= a.id h
FROM yourtable a
CROSS JOIN (SELECT #g:='', #r:=0, #h:='') t1
ORDER BY id
) t
GROUP BY t.id,t.consecutive,t.state
HAVING (UNIX_TIMESTAMP(end_time)-UNIX_TIMESTAMP(start_time))/60>5
;
Sample data:
CREATE TABLE yourtable (
id VARCHAR(10) NOT NULL ,
state VARCHAR(10) NOT NULL,
timestamp datetime
);
INSERT INTO yourtable VALUES ('sig1','red','2017-07-10 15:30:21');
INSERT INTO yourtable VALUES ('sig1','green','2017-07-10 15:31:26');
INSERT INTO yourtable VALUES ('sig1','green','2017-07-10 15:32:24');
INSERT INTO yourtable VALUES ('sig1','red','2017-07-10 15:33:29');
INSERT INTO yourtable VALUES ('sig1','red','2017-07-10 15:34:30');
INSERT INTO yourtable VALUES ('sig1','red','2017-07-10 15:39:15');
INSERT INTO yourtable VALUES ('sig2','red','2017-07-10 15:15:15');
Output:
id consecutive state consecutive_count start_time end_time diff
sig1 3 red 3 10.07.2017 15:33:29 10.07.2017 15:39:15 00:05:46
SELECT TIMESTAMPDIFF(HOUR,MAXTIME ,MINTIME),ID,state FROM
(
SELECT ID,state,MIN(timestamp)MINTIME,MAX(timestamp) MAXTIME FROM TABLE GROUP BY ID,state
)Z
Try above query.
It's possible to create a query that return the x/y number of records?
Eg.
I have table like this
ID | id_user | id_event
23 | 3 | 1
24 | 3 | 1
25 | 3 | 1
26 | 4 | 2
27 | 4 | 2
I will return something that looks like this:
Event
id_user 3 -> **1/3**
id_user 3 -> **2/3**
id_user 3 -> **3/3**
id_user 4 -> **1/2**
id_user 4 -> **2/2**
Any suggestion is appreciated!
Try this
SET #id_event := 0;
SELECT CONCAT('id_user ', id_user ,'->','**', (#id_event := #id_event + 1) ,'/', id_user ,** ) from table
This is probably a duplicate to this question.
SELECT CONCAT('id_user ',id_user,' -> **',rank,'/',group_total,'**') FROM (
SELECT id,
group_total,
CASE id_user
WHEN #id_user THEN
CASE id_event
WHEN #id_event THEN #rowno := #rowno + 1
ELSE #rowno := 1
END
ELSE #rowno :=1
END AS rank,
#id_user := id_user AS id_user,
#id_event := id_event AS id_event
FROM event_table
JOIN (SELECT id_user, id_event, COUNT(*) group_total FROM event_table GROUP BY id_user, id_event) t USING (id_user, id_event)
JOIN (SELECT #rowno := 0, #id_user := 0, #id_event := 0) r
ORDER BY id_user, id_event
) c;
Assuming you want output like this:
id_user < id_user > ** serial number of event related to this user / total events related to this user **
You can accomplish such result by the following query:
SELECT
CONCAT('id_user ',UE.id_user,' -> **',IF(#userID = UE.id_user, #eventNumber := #eventNumber + 1, #eventNumber := 1),'/',t.totalEvents,'**') AS output,
#userID := UE.id_user
FROM (SELECT #userID := -1, #eventNumber := 1) var,user_events UE
INNER JOIN
(
SELECT
id_user,
COUNT(id_event) totalEvents
FROM user_events
GROUP BY id_user
) AS t
ON UE.id_user = t.id_user
ORDER BY UE.id_user;
SQL FIDDLE DEMO
More:
SQL FIDDLE DEMO 2
This particular fiddle returns only the desired output column whereas the first fiddle contains one extra column
I played a little bit and that would be my solution:
SELECT id, id_user, id_event, if(#n = a.id_event, #c:=#c+1, if(#n:=a.id_event, #c:=1, #c:=1)) as count, (SELECT count(*) from TABLE b WHERE a.id_user = b.id_user) as total, from TABLE a join (SELECT #n:= "", #c:=1) c
It just have two if conditions for counting a #c up if #n and id_user matches if not #n become id_user and #c is 1 again. The join is for initialize the var in the same query.
Thx to that question, i found the answer to a questions that i asked 4 days ago.
I want to select data from table
suppose we have a table
table Temp
sequence_number | breakdown_number | physical_account | logical_account | debit_amount | credit_amount
----------------+------------------+------------------+-----------------+--------------+---------------
1 | 1 | 10001 | 10 | 0
2 | 1 | 0011 | 10 | 0
Now I have to select physical_account from 1st row and logical account from second row and insert it into another table in single row based on the breakdown number.
How can I do this ?
I am going to assume that sequence_number actually provides the ordering of the rows and you want to do this for each breakdown_number. The most accurate method is probably to use variables:
INSERT INTO second_table(physical_account, logical_account)
SELECT MAX(CASE WHEN seqnum = 1 THEN t.physical_account END),
MAX(CASE WHEN seqnum = 2 THEN t.logical_account END)
FROM (SELECT t.*,
(#rn := if(#b = t.breakdown_number, #rn + 1,
if(#b := t.breakdown_number, 1, 1)
)
) as seqnum
FROM Temp t CROSS JOIN
(SELECT #rn := 0, #b := -1) params
ORDER BY t.breakdown_number, t.sequence_number
) t
WHERE rn IN (1, 2)
GROUP BY t.breakdown_number;
If the sequence_number restarts at 1 for each breakdown_number, then the subquery and variables are not needed:
INSERT INTO second_table(physical_account, logical_account)
SELECT MAX(CASE WHEN t.sequence_number = 1 THEN t.physical_account END),
MAX(CASE WHEN t.sequence_number = 2 THEN t.logical_account END)
FROM Temp t
WHERE t.sequence_number IN (1, 2)
GROUP BY t.breakdown_number;
Finally, in some cases, you can just use a hack:
INSERT INTO second_table(physical_account, logical_account)
SELECT SUBSTRING_INDEX(GROUP_CONCAT(t.physical_account), ',', 1),
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(t.logical_account), ',', 2), ',', -1)
FROM Temp t
WHERE t.sequence_number IN (1, 2)
GROUP BY t.breakdown_number;
Notes about this approach:
It converts the accounts to strings, if they are of some other time.
group_concat() has a (configurable) maximum length, so if there are many records for a given breakdown_number, then you can get a run-time error.
You can use a sub query in the select with LIMIT OFFSET:
INSERT INTO second_table (physical_account, logical_account)
SELECT t.physical_account,
(SELECT s.logical_account FROM temp s
ORDER BY s.breakdown_number
LIMIT 1,1)
FROM Temp t
ORDER BY t.breakdown_number
LIMIT 1
This will select the first and second values based on breakdown_number on ACSENDING order.
DECLARE #physical_account varchar(30); /*Data Type as required*/
DECLARE #logical_account varchar(30);
SELECT #physical_account=physical_account FROM Temp WHERE logical_account=NULL AND physical_account='10001'
SELECT #logical_account=logical_account FROM Temp WHERE logical_account='0011' AND physical_account=NULL
INSERT INTO Table_New(physical_account, logical_account) VALUES(#physical_account, #logical_account);
I've the following table structure:
id |name |date
1 a 2012-01-01
2 a 2011-01-01
3 a 2010-01-01
4 a 2014-01-01
5 a 2011-01-01
I'd like to perform a select order by date (desc), and after select the first 3 rows from the results by a condition which would be where id = 1. So the second part of the query would be "give me the first 3 rows starting from the row whose id equals to 1"
EDIT:
After the first "part" the result would be:
SELECT id, name, date FROM table ORDER BY date DESC
id |name |date
4 a 2014-01-01
1 a 2012-01-01
2 a 2011-01-01
5 a 2011-01-01
3 a 2010-01-01
After the second part it should look like this (so the first 3 after the row whose id is 1):
id |name |date
2 a 2011-01-01
5 a 2011-01-01
3 a 2010-01-01
I have no any idea how could I solve it, please help me.
EDIT:
This is the concrete code I'd like to re-write:
SELECT `id`, `questions`.`userid`, `categories`.`name`, `user`.`username`, `title`,
`details`, `date` FROM `questions`
LEFT JOIN `user`
ON `questions`.`userid` = `user`.`userid`
LEFT JOIN `categories`
ON `questions`.`categoryid` = `categories`.`categoryid`
ORDER BY `date` DESC LIMIT 10
SELECT *
FROM table
WHERE date < (SELECT date FROM table WHERE id = 1)
ORDER BY date DESC
LIMIT 3
This isn't pretty because MySQL doesn't support row_number() or common table expressions, but it should work. Basically, get the row number ordered by the date, then select those whose row number is greater than an arbitrary value (in this case 1). Finally use limit to select the number of records you want.
SELECT id, name, mydate
FROM (
SELECT id, name, mydate, #rn:=#rn+1 rn
FROM mytable, (select #rn:=0) t
ORDER BY mydate DESC
) t2
WHERE rn > (
select rn
from (
SELECT id, name, mydate, #rn:=#rn+1 rn
FROM mytable, (select #rn:=0) t
ORDER BY mydate DESC
) t2
where id = 1
)
LIMIT 3
SQL Fiddle Demo
This is what you want to do... if finds the first id thats equal to 4 and then selects those out. then limit the offset to go to the next row and pull out 3
SELECT id, name, m_date from(
SELECT id, name, m_date, #a := id, if(#a = 4, #b := 1, #b) AS join_id
FROM test
join(SELECT #a := 0, #b := 0) t
ORDER BY m_date DESC
) AS tt
WHERE join_id = 1
LIMIT 1,3
SELECT temp.`id`, temp.`userid`, `categories`.`name`, `user`.`username`, temp.`title`,
temp.`details`, temp.`date` FROM (
SELECT `id`, `categoryid`, `details`, `title`, `userid`, `date`, #a := id, if(#a = 11, #b := 1, #b) AS join_id
FROM `questions`
join(SELECT #a := 0, #b := 0) t
ORDER BY `date` DESC
) as temp
LEFT JOIN `user`
ON temp.`userid` = `user`.`userid`
LEFT JOIN `categories`
ON temp.`categoryid` = `categories`.`categoryid`
WHERE join_id = 1
LIMIT 1,10;
SEE FIDDLE for clarification
Is there any way I can get the actual row number from a query?
I want to be able to order a table called league_girl by a field called score; and return the username and the actual row position of that username.
I'm wanting to rank the users so i can tell where a particular user is, ie. Joe is position 100 out of 200, i.e.
User Score Row
Joe 100 1
Bob 50 2
Bill 10 3
I've seen a few solutions on here but I've tried most of them and none of them actually return the row number.
I have tried this:
SELECT position, username, score
FROM (SELECT #row := #row + 1 AS position, username, score
FROM league_girl GROUP BY username ORDER BY score DESC)
As derived
...but it doesn't seem to return the row position.
Any ideas?
You may want to try the following:
SELECT l.position,
l.username,
l.score,
#curRow := #curRow + 1 AS row_number
FROM league_girl l
JOIN (SELECT #curRow := 0) r;
The JOIN (SELECT #curRow := 0) part allows the variable initialization without requiring a separate SET command.
Test case:
CREATE TABLE league_girl (position int, username varchar(10), score int);
INSERT INTO league_girl VALUES (1, 'a', 10);
INSERT INTO league_girl VALUES (2, 'b', 25);
INSERT INTO league_girl VALUES (3, 'c', 75);
INSERT INTO league_girl VALUES (4, 'd', 25);
INSERT INTO league_girl VALUES (5, 'e', 55);
INSERT INTO league_girl VALUES (6, 'f', 80);
INSERT INTO league_girl VALUES (7, 'g', 15);
Test query:
SELECT l.position,
l.username,
l.score,
#curRow := #curRow + 1 AS row_number
FROM league_girl l
JOIN (SELECT #curRow := 0) r
WHERE l.score > 50;
Result:
+----------+----------+-------+------------+
| position | username | score | row_number |
+----------+----------+-------+------------+
| 3 | c | 75 | 1 |
| 5 | e | 55 | 2 |
| 6 | f | 80 | 3 |
+----------+----------+-------+------------+
3 rows in set (0.00 sec)
SELECT #i:=#i+1 AS iterator, t.*
FROM tablename t,(SELECT #i:=0) foo
Here comes the structure of template I used:
select
/*this is a row number counter*/
( select #rownum := #rownum + 1 from ( select #rownum := 0 ) d2 )
as rownumber,
d3.*
from
( select d1.* from table_name d1 ) d3
And here is my working code:
select
( select #rownum := #rownum + 1 from ( select #rownum := 0 ) d2 )
as rownumber,
d3.*
from
( select year( d1.date ), month( d1.date ), count( d1.id )
from maindatabase d1
where ( ( d1.date >= '2013-01-01' ) and ( d1.date <= '2014-12-31' ) )
group by YEAR( d1.date ), MONTH( d1.date ) ) d3
You can also use
SELECT #curRow := ifnull(#curRow,0) + 1 Row, ...
to initialise the counter variable.
Assuming MySQL supports it, you can easily do this with a standard SQL subquery:
select
(count(*) from league_girl l1 where l2.score > l1.score and l1.id <> l2.id) as position,
username,
score
from league_girl l2
order by score;
For large amounts of displayed results, this will be a bit slow and you will want to switch to a self join instead.
If you just want to know the position of one specific user after order by field score, you can simply select all row from your table where field score is higher than the current user score. And use row number returned + 1 to know which position of this current user.
Assuming that your table is league_girl and your primary field is id, you can use this:
SELECT count(id) + 1 as rank from league_girl where score > <your_user_score>
I found the original answer incredibly helpful but I also wanted to grab a certain set of rows based on the row numbers I was inserting. As such, I wrapped the entire original answer in a subquery so that I could reference the row number I was inserting.
SELECT * FROM
(
SELECT *, #curRow := #curRow + 1 AS "row_number"
FROM db.tableName, (SELECT #curRow := 0) r
) as temp
WHERE temp.row_number BETWEEN 1 and 10;
Having a subquery in a subquery is not very efficient, so it would be worth testing whether you get a better result by having your SQL server handle this query, or fetching the entire table and having the application/web server manipulate the rows after the fact.
Personally my SQL server isn't overly busy, so having it handle the nested subqueries was preferable.
I know the OP is asking for a mysql answer but since I found the other answers not working for me,
Most of them fail with order by
Or they are simply very inefficient and make your query very slow for a fat table
So to save time for others like me, just index the row after retrieving them from database
example in PHP:
$users = UserRepository::loadAllUsersAndSortByScore();
foreach($users as $index=>&$user){
$user['rank'] = $index+1;
}
example in PHP using offset and limit for paging:
$limit = 20; //page size
$offset = 3; //page number
$users = UserRepository::loadAllUsersAndSortByScore();
foreach($users as $index=>&$user){
$user['rank'] = $index+1+($limit*($offset-1));
}