I have read a few similar questions on counting consecutive rows, but none of them gave me a clear answer. I hope someone could give me some help with my problem. I have the following table data:
ID TEST_VALUES
1 A
2 B
3 C
4 C
5 C
6 C
7 A
8 D
9 D
10 D
11 B
12 C
13 C
14 C
15 C
now I want to find three consecutive rows with the same value within the ID range is 1 to 10,such as when the ID range is 1 to 10,there has the 'C' continuous appear more than three times.
(note: this question has nothing to do with ID,The column ID is only the condition for my query,such as where ID > 1 and ID < 10)
you can try this one,
SELECT TEST_VALUES, MAX(cnt) AS maxCount
FROM (
SELECT TEST_VALUES, ID, COUNT(grp) AS cnt
FROM (
SELECT ID, TEST_VALUES, rn - rnByVal AS grp
FROM (
SELECT ID, TEST_VALUES,
#rn := #rn + 1 AS rn,
#rnByVal := IF (#val = TEST_VALUES,
IF (#val := TEST_VALUES, #rnByVal + 1, #rnByVal + 1),
IF (#val := TEST_VALUES, 1, 1)) AS rnByVal
FROM mytable
CROSS JOIN (SELECT #rn := 0, #rnByVal := 0, #val := '') AS vars
ORDER BY ID) AS t
) AS s
GROUP BY TEST_VALUES, grp ) AS u
GROUP BY TEST_VALUES
It will be return maximum count for occurring consecutive rows with the same value(more then 1).
I also think of a solution,hoping to help others
SELECT
TEST_VALUES
FROM(
SELECT
m.TEST_VALUES AS TEST_VALUES,
IF(#b = m.TEST_VALUES, #a := #a +1, #a := 0) AS countNUM,
#b := m.TEST_VALUES
FROM tableName m
JOIN (
SELECT
#a := 0
) AS t
) AS TEMP
WHERE countNUM >= 2
GROUP BY TEST_VALUES
SELECT
GROUP_CONCAT(`TEST_VALUES` ORDER BY `id` ASC) AS g
FROM
`tbl`
WHERE
`ID` >=1 AND `ID` <= 10
HAVING
g LIKE '%C,C,C,C%'
Ordering the GROUP_CONCAT() by id ensures the results are sequential. The HAVING clause extracts only those rows which contain at least four sequential instances of C.
Convoluted, but tested and working:
SELECT t1.ID AS t1_id, t1.TEST_VALUES AS t1_values,
(SELECT ID FROM test WHERE test.ID > t1.ID LIMIT 1 ) AS t2_id, (SELECT TEST_VALUES FROM test WHERE test.ID > t1.ID LIMIT 1 ) AS t2_values,
(SELECT ID FROM test WHERE test.ID > (SELECT ID FROM test WHERE test.ID > t1.ID LIMIT 1 ) LIMIT 1 ) AS t3_id, (SELECT TEST_VALUES FROM test WHERE test.ID > (SELECT ID FROM test WHERE test.ID > t1.ID LIMIT 1 ) LIMIT 1 ) AS t3_values,
(SELECT ID FROM test WHERE test.ID > (SELECT ID FROM test WHERE test.ID > (SELECT ID FROM test WHERE test.ID > t1.ID LIMIT 1 ) LIMIT 1 ) LIMIT 1 ) AS t4_id, (SELECT TEST_VALUES FROM test WHERE test.ID > (SELECT ID FROM test WHERE test.ID > (SELECT ID FROM test WHERE test.ID > t1.ID LIMIT 1 ) LIMIT 1 ) LIMIT 1 ) AS t4_values
FROM test AS t1
HAVING (
ID BETWEEN 1 AND 10 AND t2_id BETWEEN 1 AND 10 AND t3_id BETWEEN 1 AND 10 AND t4_id BETWEEN 1 AND 10
AND
t1_values = t2_values AND t2_values = t3_values AND t3_values = t4_values
)
Gives these results:
+-------+-----------+-------+-----------+-------+-----------+-------+-----------+
| t1_id | t1_values | t2_id | t2_values | t3_id | t3_values | t4_id | t4_values |
+-------+-----------+-------+-----------+-------+-----------+-------+-----------+
| 3 | C | 4 | C | 5 | C | 6 | C |
+-------+-----------+-------+-----------+-------+-----------+-------+-----------+
1 row in set (0.03 sec)
Related
Table one
===================
id name
-------------------
1 m
2 m
3 a
4 u
5 g
Table two
===================
id name
-------------------
8 m
9 m
10 u
11 a
12 x
15 m
Expected result
===================
1 m 8
2 m 9
3 a 11
4 u 10
I need to find id from table 2 associated with table 1 by name. But ids from table 2 must be different.
If i make join i receive wrong intersections:
select t1.id as i1, t1.name, t2.id as i2 from t1
join t2 on t1.name = t2.name
i1 name i2
--------------------
'1','m','8'
'2','m','8'
'1','m','9'
'2','m','9'
'4','u','10'
'3','a','11'
'1','m','15'
'2','m','15'
I need this for tables synchronization from different systems.
You can use the following query:
SELECT t1.id, t1.name, t2.id
FROM (
SELECT id, name,
#rn1 := IF(#n = name, #rn1 + 1,
IF(#n := name, 1, 1)) AS rn1
FROM Table1
CROSS JOIN (SELECT #rn1 := 0, #n := '') AS vars
ORDER BY name, id) AS t1
INNER JOIN (
SELECT id, name,
#rn2 := IF(#n = name, #rn2 + 1,
IF(#n := name, 1, 1)) AS rn2
FROM Table2
CROSS JOIN (SELECT #rn2 := 0, #n := '') AS vars
ORDER BY name, id
) AS t2 ON t1.name = t2.name AND t1.rn1 = t2.rn2
ORDER BY t1.id
The query uses variables in order to simulate ROW_NUMBER() window function, currently not available in MySQL. Variables #rn1, #rn2 enumerate records that belong to the same name partition with an order determined by id field.
Demo here
id value
---------
1 a
2 b
3 c
4 a
5 t
6 y
7 a
I want to select all rows where the value is 'a' and the row before it
id value
---------
1 a
3 c
4 a
6 y
7 a
I looked into
but I want to get all such rows in one query.
Please help me start
Thank you
I think the easiest way might be to use variables:
select t.*
from (select t.*,
(rn := if(value = 'a', 1, #rn + 1) as rn
from table t cross join
(select #rn := 0) params
order by id desc
) t
where rn in (1, 2)
order by id;
An alternative method uses a correlated subquery to get the previous value and then uses this in the where clause:
select t.*
from (select t.*,
(select t2.value
from table t2
where t2.id < t.id
order by t2.id desc
limit 1
) as prev_value
from table t
) t
where value = 'a' or prev_value = 'a';
With an index on id, this might even be faster than the method using variables.
I have table called "users" and need to select 2 rows before and after specific row, sorted by users.score ASC
users table (structure):
id name score
1 John 2
2 Sara 1
3 san 3
4 test 2
5 jery 5
6 simon 6
7 bob2 7
8 jack 4
9 man 2
for example: need to select 2 rows before and after users.id = 5 order by users.score
result should be like:
id name score
3 san 3
8 jack 4
5 jery 5
6 simon 6
7 bob2 7
thanks,
Using union all and subqueries to limit the records should do it:
select * from users where id = 5
union all (
select * from users
where score < (select score from users where id = 5)
order by score desc limit 2
)
union all (
select * from users
where score > (select score from users where id = 5)
order by score asc limit 2
)
order by score
Sample SQL Fiddle
Edit: I think a better method is to number the rows according to score and then select the rows with number -2 and +2 from the rows of id 5:
select id, name, score
from (select
t.*, #rownum1 := #rownum1 + 1 as rank
from users t, (select #rownum1 := 0) r
order by score
) a,
(select rank from (
select t.*,
#rownum := #rownum + 1 as rank
from users t, (select #rownum := 0) r
order by score
) t
where id = 5
) b
where b.rank between a.rank -2 and a.rank+2
order by score;
Sample SQL Fiddle
Perhaps using union all
(
select * from users where id < 5 order by score limit 2
)
union all
(
select * from users where id > 5 order by score limit 2
)
(SELECT x.* FROM users x JOIN users y ON y.score <= x. score WHERE y.id = 5 ORDER BY score LIMIT 3)
UNION
(SELECT x.* FROM users x JOIN users y ON y.score >= x. score WHERE y.id = 5 ORDER BY score DESc LIMIT 3)
[ORDER BY score] ;
http://www.sqlfiddle.com/#!9/45c22/42
I just write the query, based on "jpw" solution (many thanks to him)
select * from users where id = 5
union all (
select * from users
where id in (select id from users where score < (select score from users u where u.id = 5) order by score ASC)
order by score desc limit 2
)
union all (
select * from users
where id in (select id from users where score > (select score from users u where u.id = 5) order by score ASC)
order by score ASC limit 2
)
order by score
Selecting arbitrarily ordered rows before and after a specific id
SET #j = 0;
SET #i = 0;
SELECT *
FROM (
SELECT id, col1, col2, ..., #j:=#j+1 AS pos
FROM `table`
WHERE col1=... ORDER BY col1 DESC, col2 ASC
) AS zz
WHERE (
SELECT position
FROM (
SELECT id AS id2, #i:=#i+1 AS position
FROM `table`
WHERE col1=... ORDER BY col1 DESC, col2 ASC
) AS zz
WHERE id2=$currId
)
IN (pos-5,pos-4,pos-3,pos-2,pos-1,pos,pos+1,pos+2,pos+3,pos+4,pos+5)
Suppose I have a table like this:
testdata
col1
1
2
3
1
1
2
3
...
how to arrive at a query that give also the the running number / unique id per subgroup ?
col1 | sub_id
1 1
2 1
3 1
1 2
1 3
2 2
3 2
... ...
Assuming you have a column that specifies the ordering, you can use a correlated subquery:
select col1,
(select count(*) from table t2 where t2.col1 = t.col1 and t2.id <= t.id) as sub_id
from table t;
You can also do this with variables:
select t.*,
(#rn := if(#id = id, #rn + 1,
if(#id := id, 1, 1)
)
) as sub_id
from table t cross join
(select #rn := 0, #id := -1) vars
order by col1, id;
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