Query previous row value and merge to current row - mysql

Is there a way how can I query the previous row value and merge to current row, here is my sample table scenario:
+-------------------------------------+
| ColA | ColB | ColValue | Date |
|------|------|----------|------------|
| AAA | 111 | 5 | 2017-04-23 |
| AAA | 111 | 4 | 2017-04-22 |
| AAA | 111 | 3 | 2017-04-21 |
| BBB | 222 | 5 | 2017-04-30 |
| BBB | 222 | 4 | 2017-04-29 |
+-------------------------------------+
And my expected result should be this, just want to get the previous and current value and group it by selected columns and date.
+--------------------------------------------------+
| ColA | ColB | PreValue | CurValue | Date |
|------|------|----------|-------------------------|
| AAA | 111 | 4 | 5 | 2017-04-23 |
| AAA | 111 | 3 | 4 | 2017-04-22 |
| AAA | 111 | N/A | 3 | 2017-04-21 |
| BBB | 222 | 4 | 5 | 2017-04-30 |
| BBB | 222 | N/A | 4 | 2017-04-29 |
+--------------------------------------------------+
any suggestions or solution, thanks in advance
Here is my actual query from my actual data as reference:
SELECT ai.APName, tbap.Value , tbap.DateTime, tbap.Comment, tbap.ModifiedBy, tbap.ToolName, d.Name as Strategy FROM (SELECT dt.*,ins.Value as ToolName FROM (SELECT av.*,ai.DocumentID,ai.IndexID FROM ControlAutomation.appartitionindexes ai
JOIN (SELECT * FROM ControlAutomation.appartitionvalues
where DateTime > '2017-04-22 23:17:13' and DateTime < '2017-04-26 23:18:28') av
ON ai.APPartitionID = av.APPartitionID) dt INNER JOIN factory.indexes ins ON ins.ID = dt.IndexID
where dt.comment like '%updateAdjustableParameter%'
group by dt.ID) tbap
INNER JOIN ControlAutomation.documents d ON d.ID = tbap.DocumentID
INNER JOIN appartitionindexes ai ON ai.APPartitionID = tbap.APPartitionID
GROUP BY tbap.ID
ORDER BY tbap.ToolName DESC, d.Name, tbap.DateTime DESC
LIMIT 100

Hope I correctly got your question: http://sqlfiddle.com/#!9/d815c/6
select
prev_query.cola as ColA,
prev_query.colb as ColB,
rhs.colvalue as PreValue,
prev_query.colvalue as CurValue,
prev_query.coldate as ColDate
from
prev_query left join prev_query as rhs
on prev_query.cola = rhs.cola
and prev_query.colb = rhs.colb
and prev_query.colvalue = rhs.colvalue + 1
order by
prev_query.cola, prev_query.colb, prev_query.coldate desc;

Related

Find All but last duplicate records from MYSQL database

I have a mysql table with following records
-------------------------------
| ID | Name | Age | XXX | YYY |
-------------------------------
| 1 | aa | 12 | qqq | rr |
-------------------------------
| 2 | aa | 12 | ttt | pp |
-------------------------------
| 3 | bb | 13 | qhq | rr |
-------------------------------
| 4 | bb | 13 | pqq | tr |
-------------------------------
| 5 | bb | 13 | ql | jjn |
-------------------------------
My requirement is to retrieve all duplicate records with respect to first column apart from the last entry. Currently I tried to retrieve duplicates and that is working fine
SELECT Name, Age, XXX, YYY FROM list
INNER JOIN (SELECT Name
FROM list
GROUP BY Name
HAVING COUNT(Name) > 1) dup
ON list.Name = dup.Name;
Output is like
-------------------------------
| ID | Name | Age | XXX | YYY |
-------------------------------
| 1 | aa | 12 | qqq | rr |
-------------------------------
| 2 | aa | 12 | ttt | pp |
-------------------------------
| 3 | bb | 13 | qhq | rr |
-------------------------------
| 4 | bb | 13 | pqq | tr |
-------------------------------
| 5 | bb | 13 | ql | jjn |
-------------------------------
But I want to remove the last record from the output like
-------------------------------
| ID | Name | Age | XXX | YYY |
-------------------------------
| 1 | aa | 12 | qqq | rr |
-------------------------------
| 3 | bb | 13 | qhq | rr |
-------------------------------
| 4 | bb | 13 | pqq | tr |
-------------------------------
How can I achieve this?
I would create two new columns in COUNT and Row_number the result because
Get COUNT total number by name column to get the MAX rownumber.
use Row_number by name to get rownumber.
To write a condition exclude the last rownumber by name from total count
If your mysql version support window function you can try this code.
make Row_number and COUNT then remove greater Row_number by Name.
SELECT *
FROM (
select *,
Row_number() over(partition by Name order by ID) rn,
COUNT(*) over(partition by Name) totle
from `list`
) t1
where rn <> totle
sqlfiddle
EDIT
if you have ID auto_increment column and your mysql didn't support window function you can try this query.
SELECT id,Name,Age,XXX,YYY
FROM (
SELECT *,
(SELECT COUNT(*) FROM `list` t1 WHERE t.ID >= t1.ID AND t.Name = t1.Name) rn,
(SELECT COUNT(*) FROM `list` t1 WHERE t.Name = t1.Name) totle
FROM `list` t
) t1
where rn <> totle
sqlfiddle
[Results]:
| id | Name | Age | XXX | YYY |
|----|------|-----|-----|-----|
| 1 | aa | 12 | qqq | rr |
| 3 | bb | 13 | qhq | rr |
| 4 | bb | 13 | pqq | tr |

Select most recent MAX() and MIN() - WebSQL

i'm build an exercises web app and i'm working with two tables like this:
Table 1: weekly_stats
| id | code | type | date | time |
|----|--------------|--------------------|------------|----------|
| 1 | CC | 1 | 2015-02-04 | 19:15:00 |
| 2 | CC | 2 | 2015-01-28 | 19:15:00 |
| 3 | CPC | 1 | 2015-01-26 | 19:15:00 |
| 4 | CPC | 1 | 2015-01-25 | 19:15:00 |
| 5 | CP | 1 | 2015-01-24 | 19:15:00 |
| 6 | CC | 1 | 2015-01-23 | 19:15:00 |
| .. | ... | ... | ... | ... |
Table 2: global_stats
| id | exercise_number |correct | wrong |
|----|-----------------|--------|-----------|
| 1 | 138 | 1 | 0 |
| 2 | 246 | 1 | 0 |
| 3 | 988 | 1 | 10 |
| 4 | 13 | 5 | 0 |
| 5 | 5 | 4 | 7 |
| 6 | 5 | 4 | 7 |
| .. | ... | ... | ... |
What i would like is to get MAX(correct-wrong) and MIN(correct-wrong) and now i'm working with this query:
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
UNION
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats);
This query is working good, except for one thing: when "WHERE correct - wrong = (SELECT MIN(correct - wrong)[...]" selects more than one row, the row selected is the first but i would like to have returned the most recent (in other words: ordered by datetime(date, time)). Is it possible?
Thanks!
I think you can solve it like this:
SELECT * FROM (
SELECT
1 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1 ) as a
UNION
SELECT * FROM (
SELECT
2 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1) as b
ORDER BY sort_column;
Here is the documentation about how UNION works.

MySQL: SELECT value and a minimum value on certain conditions in the same row

(MySQL) In 'simple' terms I need to add a minimum price column.
That is, the minimum price for each unique combination of PA and DA records.
Example Raw Data
id | PA | DA | price |
---|-----|------------|--------|
1 | SW1 | PO19 | 100 |
1 | W6 | E16 | 5 |
2 | SW1 | PO19 | 90 |
2 | W6 | E16 | 8 |
3 | TW6 | SO14 | 2000 |
3 | W6 | E16 | 9 |
Output from Example
id | PA | DA | price | MIN price|
---|-----|------------|--------|--------- |
1 | SW1 | PO19 | 100 | 90 |
1 | W6 | E16 | 5 | 5 |
2 | SW1 | PO19 | 90 | 90 |
2 | W6 | E16 | 8 | 5 |
3 | TW6 | SO14 | 2000 | 2000 |
3 | W6 | E16 | 9 | 5 |
e.g. above: for PA=SW1, DA=PO19 the MIN price=90 (id=2).
Ideally I would also like to only SELECT a particular id, but it still returns the "global" minimum.
e.g. if I want to select id=2, it returns:
id | PA | DA | price | MIN price|
---|-----|------------|--------|--------- |
2 | SW1 | PO19 | 90 | 90 |
2 | W6 | E16 | 8 | 5 |
I would post some attempts I've made but they've been useless attempts.
Regards,
George
The sub-select with the minimum price can be joined to the original table to get your result.
SELECT p.id, p.pa, p.da, p.price, minp.price min_price
FROM prices p
JOIN (SELECT pa, da, min(price) price from prices group by pa, da) minp
ON minp.pa = p.pa and minp.da = p.da
WHERE p.id = 2
You can use the the subquery like this
check output here sqlFiddle
select p.id,p.pa,p.da,
(select min(s.price)
from sample s
group by s.pa,s.da
having s.pa=p.pa and s.da=p.da)
from sample p
mind the formatting

SQL Query design involving multiple tables

I am working on a MYSQL query design that, in my opinion, is pretty hard. I'm not experienced in SQL, so I found it really dificult. The point is:
I've got the 'ordertable' table which stores the order of some codes (AA, BB, CC..). In another table, 'AllTables' I store the name of a table associated to a code (AA -> tableA). Finally, 'tableA' table stores some data of diferent units (unit1, unit2...).
CASE 1.
ordertable : Order of codes is given like:
+----------------+------+
| split_position | code |
+----------------+------+
| 1 | AA |
| 2 | BB |
| 3 | CC |
| 4 | DD |
+----------------+------+
CASE 2.
ordertable Order of codes is given like:
+-------+------+------+------+------+
| id | pos1 | pos2 | pos3 | pos4 |
+-------+------+------+------+------+
| unit1 | AA | BB | DD | CC |
| unit2 | CC | BB | AA | DD |
| unit3 | BB | DD | CC | AA |
+-------+------+------+------+------+
In Case 2 we can also find special codes like 'var15':
+-------+------+-------+------+-------+
| id | pos1 | pos2 | pos3 | pos4 |
+-------+------+-------+------+-------+
| unit1 | AA | var15 | DD | var37 |
| unit2 | CC | BB | AA | DD |
+-------+------+-------+------+-------+
In case we find something similar to 'var'+ number the associated table is always the same: 'variable', where de 'id' is the number of the code 'var37' -> id = 37.
variable
+-----+------------+------+--------+
| id | name | time | active |
+-----+------------+------+--------+
| 15 | Pedro | 5 | 1 |
| 17 | Maria | 4 | 1 |
+-----+------------+------+--------+
Info of tables:
AllTables
+------+------------+
| code | name |
+------+------------+
| AA | tableA |
| BB | tableB |
| CC | tableC |
| DD | tableD |
+------+------------+
tableA
+-------+------+------+--------+
| id | name | time | active |
+-------+------+------+--------+
| unit1 | Mark | 11 | 1 |
| unit2 | Jame | 20 | 0 |
+-------+------+------+--------+
tableB
+-------+------+------+--------+
| id | name | time | active |
+-------+------+------+--------+
| unit1 | Mari | 44 | 1 |
| unit3 | nam2 | 57 | 1 |
+-------+------+------+--------+
Given an id='unit1', I'm expecting the next:
Result
+----------------+------+-------+-------+--------+
| split_position | code | name | time | active |
+----------------+------+-------+-------+--------+
| 1 | AA | Mark | 11 | 1 |
| 2 | BB | Mari | 44 | 1 |
| 3 | CC | | | 0 |
| 4 | DD | | | 0 |
+----------------+------+-------+-------+--------+
In case that the id (unit1) does not exists in tableC or tableD, 'split_position' and 'code' associated should appear but in the 'active' field should appear a 0.
it's a bit of a steep learning curve, but basically you have to declare a cursor and loop
over the each row in the ordertable and select your data then UNION the result together using dynamic SQL.
check this sqlFiddle
to order by final result by split position ASC just add ORDER BY split_position ASC to the sql variable before executing it like this sqlFiddle
to solve your problem you would need something like the following:
select split_position, code, name, time, active
from
(
select 'tableA' as tablename, id, [name], [time], active
from tableA
union all select 'tableB' as tablename, id, [name], [time], active
from tableB
) as tbls
inner join alltables atbls
on tbls.tablename=atbls.name
inner join ordertable ot
on atbls.code=ot.code
where tbls.id='unit1'

mysql sorting and ranking statement

I need some help in mysql statement
Ive table1 with 7 column and table 2 with 8 column the extra column named ranking , my statement should be like
select all from table 1 then sort it by " number of users " insert it in table 2 and ranking start 1 2 3 etc,
table 1 :
username | email | number of users
jack a#a.com 75
ralf b#b.com 200
anne c#c.com 12
sonny d#d.com 300
===================================
here where i need to INSERT and RANKING based on number of users
table 2
ranking | username | email | number of users
1
2
3
I would avoid to use another table. A single query suffices.
create table mytable (
id int not null auto_increment primary key,
username varchar(50),
email varchar(50),
number int
) engine = myisam;
insert into mytable (username,email,number)
values
('a','aaa',10),
('b','bbb',30),
('c','ccc',50),
('d','ddd',30),
('e','eee',20),
('f','fff',45),
('g','ggg',20);
select #r:=#r+1 as rnk,username,email,number
from mytable,(select #r:=0) as r order by number desc
+------+----------+-------+--------+
| rnk | username | email | number |
+------+----------+-------+--------+
| 1 | c | ccc | 50 |
| 2 | f | fff | 45 |
| 3 | b | bbb | 30 |
| 4 | d | ddd | 30 |
| 5 | e | eee | 20 |
| 6 | g | ggg | 20 |
| 7 | a | aaa | 10 |
+------+----------+-------+--------+
7 rows in set (0.00 sec)
This is a smarter version that considers ties
select #r:=#r + 1 as rn, username,email,
#pos:= if(#previous<>number,#r,#pos) as position,
#previous:=number as num
from mytable,(select #r:=0,#pos:=0,#previuos:=0) as t order by number desc
+------+----------+-------+----------+--------+
| rn | username | email | position | num |
+------+----------+-------+----------+--------+
| 1 | c | ccc | 1 | 50 |
| 2 | f | fff | 2 | 45 |
| 3 | b | bbb | 3 | 30 |
| 4 | d | ddd | 3 | 30 |
| 5 | e | eee | 5 | 20 |
| 6 | g | ggg | 5 | 20 |
| 7 | a | aaa | 7 | 10 |
+------+----------+-------+----------+--------+
7 rows in set (0.00 sec)
INSERT INTO table2
SELECT #rank := #rank + 1, table1.* FROM table1
JOIN( SELECT #rank := 0 ) AS init
ORDER BY number_of_users DESC
You need to do something like this:
SELECT * FROM `table1`
INNER JOIN `table2` USING ([a common filed name here])
ORDER BY table2.[the filed name here]
Good Luck!