mysql - count positive consecutive value - mysql

records:
ID | NAME | VALUE
---|-------|-------
1 | ALPHA | 5
2 | ALPHA | 7 //comment: [2 times positive numbers]
3 | ALPHA | -4
4 | ALPHA | 3 //comment: [1 times positive numbers]
5 | ALPHA | -2
6 | ALPHA | -3
7 | ALPHA | 9
8 | ALPHA | 3 //comment: [2 times positive numbers]
9 | ALPHA | -2
10 | ALPHA | -6
I need to know how many consecutive time I have a positive number so in this case we have:
2 (consecutive positive number)
1 (consecutive positive number)
2 (consecutive positive number)
the final result that I want is show output with a table that tell me how many time in table we have 1 consecutive number, 2consecutive numbers, 3consecutive numbers, ...
so a table like this:
table structure:
consecutive number
value
data:
1 | 1 (we have 1 times, 1 consecutive numbers)
2 | 2 (we have 2 times, 2 consecutive numbers)
3 | 0 (we have 0 times, 3 consecutive numbers)

Try this:
SELECT x consecutive_numbers,
count(*) how_many_times
FROM (
SELECT y, max( x ) x
FROM (
SELECT
if( value < 0, #x:=0, #x:=#x+1 ) x,
if( value < 0, #y:=#y+1, #y ) y,
value
FROM table1
CROSS JOIN (
select #x:=0, #y:=0
) var
ORDER BY id
)q
WHERE value >= 0
GROUP BY y
) qq
GROUP BY x
;
Demo: --> http://www.sqlfiddle.com/#!2/75fa7/8

SELECT peak as consecutive_number, COUNT(*) AS value
FROM (
SELECT IF(value <= 0 AND #counter > 0, #counter, NULL) AS peak, #counter := IF(value <= 0, 0, #counter+1) AS counter
FROM (SELECT *
FROM (SELECT value
FROM mytable
ORDER BY id) x
UNION ALL
SELECT -1) x -- in case last row is positive
CROSS JOIN (SELECT #counter := 0) var
) x
WHERE peak IS NOT NULL
GROUP BY peak
DEMO

Related

MySQL Order by with two colums mix togather

I have this table
id | TOP | POS
1 | 60 | 1
2 | 50 | 2
3 | 40 | 3
4 | 30 | 4
5 | 20 | 5
6 | 10 | 6
and I need result like this:
id | TOP | POS
1 | 60 | 1
6 | 10 | 6
2 | 50 | 2
5 | 20 | 5
3 | 40 | 3
4 | 30 | 4
Basicaly I need order result by TOP column DESC but between each line put result from order by POS DESC column.
I tried use MOD but result was wrong
SELECT * FROM (
select
#row := #row + 1 as row,
a.*
from table a
order by TOP desc
) t ORDER BY IF(MOD(row, 2) = 1, TOP, POS);
In order to do what you want, you need two sortkeys: the row ordered by TOP and the row ordered by POS. This means tow queries you must join.
Then sort first by the lesser of these two keys (TOP60/POS1 has row #1 for TOP, TOP10/POS6 has row #1 for POS, so these two come first), and then by the TOP key (so TOP60/POS1 has precedence over TOP10/POS6).
select a1.*
from
(
select
#rowtop := #rowtop + 1 as row,
a.*
from mytable a
cross join (select #rowtop := 0) vars
order by top desc
) a1
join
(
select
#rowpos := #rowpos + 1 as row,
a.*
from mytable a
cross join (select #rowpos := 0) vars
order by pos desc
) a2 using(id)
order by least(a1.row, a2.row), a1.row;
REXTESTER DEMO: http://rextester.com/ZKOL97078

Minimum / maximum of "sum" with relative values

I have the following table test:
+----+-------+
| id | value |
+----+-------+
| 1 | -3 |
| 2 | -5 |
| 3 | 10 |
| 4 | -1 |
+----+-------+
For MIN(value) I get -5, for MAX(value) I get 10, and for SUM(value) I get 1. However, I would like to get the minimum and maximum value when progressing through the table step by step.
Example 1: SELECT AWESOME_FUNCTION_SUM_MIN(value) FROM test ORDER BY id ASC
This should return -8 (first row is -3, plus the second row -5 results in the lowest value over the course of all values).
Example 2: SELECT AWESOME_FUNCTION_SUM_MAX(value) FROM test ORDER BY id ASC
This should return 2 (first row -3, second -5, and third row +10 leads to the highest value over the course of all values).
Obviously, ORDER BY does not really make sense, since it is used for ordering the results of a query, but I used it here anyways for demonstration purposes. To me, this is such a basic functionality, so I was surprised to find nothing about it. I potentially am using the wrong keywords. Can somebody help me out? Or do I have to extract all values and do the analysis externally (=not with MySQL)?
Create table/insert data.
CREATE TABLE test
(`id` INT, `value` INT)
;
INSERT INTO test
(`id`, `value`)
VALUES
(1, -3),
(2, -5),
(3, 10),
(4, -1)
;
MySQL doesnt have those functions but you can simulate them using a self join.
Query SUM_MIN
SELECT
SUM(test.value)
FROM
test
INNER JOIN (
SELECT
id
FROM
test
WHERE
test.value > 0
ORDER BY
id ASC
LIMIT 1
)
AS
positive_number
ON
test.id < positive_number.id
ORDER BY
test.id
Result
sum(test.value)
-----------------
-8
Query SUM_MAX
SELECT
SUM(test.value)
FROM
test
INNER JOIN (
SELECT
id
FROM
test
WHERE
test.value > 0
ORDER BY
id ASC
LIMIT 1
)
AS
positive_number
ON
test.id <= positive_number.id
ORDER BY
test.id
Result
sum(test.value)
-----------------
2
Here's one way:
SELECT x.*
, #least:=LEAST(#least,value) least
, #greatest:=GREATEST(#greatest,value) greatest
, #i:=#i+value running
FROM my_table x
, (SELECT #least:=1000,#greatest:=-1000,#i:=0) vars
ORDER
BY id;
+----+-------+-------+----------+---------+
| id | value | least | greatest | running |
+----+-------+-------+----------+---------+
| 1 | -3 | -3 | -3 | -3 |
| 2 | -5 | -5 | -3 | -8 |
| 3 | 10 | -5 | 10 | 2 |
| 4 | -1 | -5 | 10 | 1 |
+----+-------+-------+----------+---------+
To get a cumulative sum, you can join a table to itself.
select min(val)
from (select sum(a.value) as val from test a join test b
on a.id<=b.id group by b.id) t1;
/* answer: -8 */
select max(val)
from (select sum(a.value) as val from test a join test b
on a.id<=b.id group by b.id) t1;
/* answer: 2 */

Select Min value from a group without first row

Hello I want one query with one objective...
Select the min value from a field for example:
id | row | value
-----------------------
8 | 1 | 0.9
7 | 1 | 0.8
6 | 1 | 0.7
5 | 1 | 0.6
4 | 2 | 0.5
3 | 2 | 0.4
2 | 3 | 0.3
1 | 1 | 0.2
I need to select the min value from this table where row = '1' in this case is id = 1 right? BUT I don't want the id = 1, I want only the min value from the first continuos row, in this case is the id = 5 because row = '1' have the id 8, 7, 6, 5, and the min value is 0.6, the id = 5.
What query I need?
I think you're after this, but it's very hard to tell...
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.id = x.id + 1
AND y.row = x.row
WHERE x.row = 1
ORDER
BY x.value
LIMIT 1;
Try something like:
SELECT min(value), id
WHERE value > (SELECT min(value)
FROM MyTable
WHERE row = 1)
AND row = 1
GROUP BY id;
Inner query will find min value for row as 1 while outer query will find next min value that is greater than min value we got in inner query.

MySQL Change SUM value

I'm trying to create a query whereby I SUM a column but, if a column contains a certain value, the SUM value has to be reset at that point to take on this value.
ie:
SUM(i.units * op.add_or_subtract) // This would translate to: '50 * -1' or '50 * 1'
The idea is that if op.op_code = 9, the SUM value should be reset to the current value of i.units as a manual adjustment has taken place.
op_code | units | add_or_subtract | SUM value |
--------|-------|-----------------|------------
1 | 50 | 1 | 50
1 | 50 | 1 | 100
4 | 30 | -1 | 70
9 | 225 | 0 | 225
etc etc.
Can anyone help with how I could (if I can) achieve this in MySQL?
try
select *, case when op_code = 9
then #s := units
else (#s := #s + (units * add_or_subtract))
end as sum
from your_table, (select #s := 0) st
try this:
SELECT SUM(IF(op_code = 9, (#var_sum := units),
((#var_sum := #var_sum + units) * add_or_subtract)
) as sum_value
FROM table_name, (SELECT #var_sum := 0) a;

Select a row and rows around it

Ok, let's say I have a table with photos.
What I want to do is on a page display the photo based on the id in the URI. Bellow the photo I want to have 10 thumbnails of nearby photos and the current photo should be in the middle of the thumbnails.
Here's my query so far (this is just an example, I used 7 as id):
SELECT
A.*
FROM
(SELECT
*
FROM media
WHERE id < 7
ORDER BY id DESC
LIMIT 0, 4
UNION
SELECT
*
FROM media
WHERE id >= 7
ORDER BY id ASC
LIMIT 0, 6
) as A
ORDER BY A.id
But I get this error:
#1221 - Incorrect usage of UNION and ORDER BY
Only one ORDER BY clause can be defined for a UNION'd query. It doesn't matter if you use UNION or UNION ALL. MySQL does support the LIMIT clause on portions of a UNION'd query, but it's relatively useless without the ability to define the order.
MySQL also lacks ranking functions, which you need to deal with gaps in the data (missing due to entries being deleted). The only alternative is to use an incrementing variable in the SELECT statement:
SELECT t.id,
#rownum := #rownum+1 as rownum
FROM MEDIA t, (SELECT #rownum := 0) r
Now we can get a consecutively numbered list of the rows, so we can use:
WHERE rownum BETWEEN #midpoint - ROUND(#midpoint/2)
AND #midpoint - ROUND(#midpoint/2) +#upperlimit
Using 7 as the value for #midpoint, #midpoint - ROUND(#midpoint/2) returns a value of 4. To get 10 rows in total, set the #upperlimit value to 10. Here's the full query:
SELECT x.*
FROM (SELECT t.id,
#rownum := #rownum+1 as rownum
FROM MEDIA t,
(SELECT #rownum := 0) r) x
WHERE x.rownum BETWEEN #midpoint - ROUND(#midpoint/2) AND #midpoint - ROUND(#midpoint/2) + #upperlimit
But if you still want to use LIMIT, you can use:
SELECT x.*
FROM (SELECT t.id,
#rownum := #rownum+1 as rownum
FROM MEDIA t,
(SELECT #rownum := 0) r) x
WHERE x.rownum >= #midpoint - ROUND(#midpoint/2)
ORDER BY x.id ASC
LIMIT 10
I resolve this by using the below code:
SELECT A.* FROM (
(
SELECT * FROM gossips
WHERE id < 7
ORDER BY id DESC
LIMIT 2
)
UNION
(
SELECT * FROM gossips
WHERE id > 7
ORDER BY id ASC
LIMIT 2
)
) as A
ORDER BY A.id
I don't believe that you can have an "order by" in different sections of a UNION. Could you just do something like this:
SELECT * FROM media where id >= 7 - 4 and id <= 7 + 4 ORDER BY id
I'm agree with the answer suggested by malonso(+1), but if you try it with id= 1, you will get only 5 thumbnails. I don't know if you want this behaviour. If you want always 10 thumbs, you can try:
select top 10 * from media where id > 7 - 4
The problem is that select top is database dependent (in this case is a sql server clause). Other database has similar clauses:
Oracle:
SELECT * media
FROM media
WHERE ROWNUM < 10
AND id > 7 - 4
MySQL:
SELECT *
FROM media
WHERE id > 7 - 4
LIMIT 10
So maybe you can use the last one.
If we do it, we will have the same problem if you want the last 10 thumbs. By example, If we have 90 thumbs and we give an id=88 ... You can solve it adding an OR condition. In MySQL will be something like:
SELECT *
FROM media
WHERE id > 7 - 4
OR (Id+5) > (select COUNT(1) from media)
LIMIT 10
If you're happy to use temp tables, your original query could be broken down to use them.
SELECT
*
FROM media
WHERE id < 7
ORDER BY id DESC
LIMIT 0, 4
INTO TEMP t1;
INSERT INTO t1
SELECT
*
FROM media
WHERE id >= 7
ORDER BY id ASC
LIMIT 0, 6;
select * from t1 order by id;
drop table t1;
Try union all instead. Union requires the server to ensure that the results are unique and this conflicts with your ordering.
I had to solve a similar problem, but needed to account situations where we always got the same number of rows, even if the desired row was near the top or bottom of the result set (i.e. not exactly in the middle).
This solution is a tweak from OMG Ponies' response, but where the rownum maxes out at the desired row:
set #id = 7;
SELECT natSorted.id
FROM (
SELECT gravitySorted.* FROM (
SELECT Media.id, IF(id <= #id, #gravity := #gravity + 1, #gravity := #gravity - 1) AS gravity
FROM Media, (SELECT #gravity := 0) g
) AS gravitySorted ORDER BY gravity DESC LIMIT 10
) natSorted ORDER BY id;
Here's a break down of what's happening:
NOTE: In the example below I made a table with 20 rows and removed ids 6 and 9 to ensure a gap in ids do not affect the results
First we assign every row a gravity value that's centered around the particular row you're looking for (in this case where id is 7). The closer the row is to the desired row, the higher the value will be:
SET #id = 7;
SELECT Media.id, IF(id <= #id, #gravity := #gravity + 1, #gravity := #gravity - 1) AS gravity
FROM Media, (SELECT #gravity := 0) g
returns:
+----+---------+
| id | gravity |
+----+---------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 7 | 6 |
| 8 | 5 |
| 10 | 4 |
| 11 | 3 |
| 12 | 2 |
| 13 | 1 |
| 14 | 0 |
| 15 | -1 |
| 16 | -2 |
| 17 | -3 |
| 18 | -4 |
| 19 | -5 |
| 20 | -6 |
| 21 | -7 |
+----+---------+
Next we order all the results by the gravity value and limit on the desired number of rows:
SET #id = 7;
SELECT gravitySorted.* FROM (
SELECT Media.id, IF(id <= #id, #gravity := #gravity + 1, #gravity := #gravity - 1) AS gravity
FROM Media, (SELECT #gravity := 0) g
) AS gravitySorted ORDER BY gravity DESC LIMIT 10
returns:
+----+---------+
| id | gravity |
+----+---------+
| 7 | 6 |
| 5 | 5 |
| 8 | 5 |
| 4 | 4 |
| 10 | 4 |
| 3 | 3 |
| 11 | 3 |
| 2 | 2 |
| 12 | 2 |
| 1 | 1 |
+----+---------+
At this point we have all the desired ids, we just need to sort them back to their original order:
set #id = 7;
SELECT natSorted.id
FROM (
SELECT gravitySorted.* FROM (
SELECT Media.id, IF(id <= #id, #gravity := #gravity + 1, #gravity := #gravity - 1) AS gravity
FROM Media, (SELECT #gravity := 0) g
) AS gravitySorted ORDER BY gravity DESC LIMIT 10
) natSorted ORDER BY id;
returns:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 7 |
| 8 |
| 10 |
| 11 |
| 12 |
+----+