MySQL update Query using Select - mysql

Can someone explain how to write an update query to update a column "newBalance" in table "abcd" with the O/P of balance in the following query ?
I would like to update newBalance column with the balance calculated for each row using the below query.
SELECT id
, inAmt
, outAmt
, #prev := if(id = 1, #prev := #curBalance , #prev + (#prevOut-#prevIn)) as balance
, #prevIn :=inAmt
, #prevOut := outAmt
from (select #prev := 0) as i
, abcd
order
by id
Any help is highly appreciated.

You can use update with a join:
update abcd join
(select id, inAmt, outAmt,
#prev := if(id = 1, #prev := #curBalance , #prev + (#prevOut-#prevIn)) as balance,
#prevIn := inAmt,
#prevOut := outAmt
from (select #prev := 0) as i cross join
abcd
order by id
) x
on abcd.id = x.id
set abcd.newbalance = x.balance;
All that said, your query is definitely not guaranteed to work and can fail at any time. You are using variables in one expression and assigning them in another. MySQL does not guarantee the order of evaluation of expressions.
However, that was not the question that you asked. If you want the query fixed, ask a new question with sample data and desired results.

Related

ranking of data based on multiple columns in mysql with using rank function

i am very new to mysql and tried to rank top records based on two fields
sharing you the current script and output along with the desired output
Current Script :
select u_rank, c_rank,u_name,c_name,
#curRank := #curRank + 1 AS rank
from (
SELECT u_rank, c_rank,u_name,c_name
from abc
) a,
(
select #curRank := 0
) r
order by c_rank,u_rank
Current OutpuT
MY DESIRED OUTPUT IS
Please help
If I am decifering your question correctly, you are wanting to rank the c_ranks ascending or each u_name and then order by that rank then the u_rank. After that you need to get a rank for all records in that order. I'm not sure that the way I just worded that made any sense... but the following should get that result for you:
SET #rank1 = 0;
SET #rank2 = 0;
SET #u_name = '';
SELECT u_rank, c_rank, u_name, c_name, #rank2 := #rank2 + 1 AS rank
FROM (
SELECT u_rank,
c_rank,
c_name,
#rank1 := (#rank1 + 1 - IF(#u_name = u_name, 0, #rank1)) AS rank1,
#u_name := u_name AS u_name
FROM abc
ORDER BY u_name, c_rank
) foo
ORDER BY rank1, u_rank;

It is possible to merge rows chronologically? [duplicate]

The problem is as follows during the day data is put into status_table (this can reach up to 4000 entries a day
During the night i want to put the data in a more organized way in a storage table
(status_table_storage) and remove it from the status_table.
The idea is to group all the entries that have the same status until the status/error changes. from that point on a new group should start an be placed into the table. an example can be found in the fiddle
what is the best way to do this.
an example of the table structure can be found here: http://sqlfiddle.com/#!9/488524/1
Step by step explanation:
First you order the table by name and timestamp and initialize three user-defined variables.
SELECT s.* FROM status_table s
, (SELECT #group_number := 0, #prevName := NULL, #prevStatus := NULL) var_init_subquery
ORDER BY name, timestamp
As you can see, we can use a subquery for that. The ORDER BY is important, because there's no order in a relational database, unless you specify it.
Now, MySQL evaluates the SELECT clause in the specified order, therefore don't change the order here.
SELECT
s.*,
#prevName,
#prevStatus,
#prevName := s.name,
#prevStatus := s.status
FROM status_table s
, (SELECT #group_number := 0, #prevName := NULL, #prevStatus := NULL) var_init_subquery
ORDER BY name, timestamp
When you execute this statement, you can see, that when we simply select the variables they hold the value of the previous row or NULL when it's the first row, that was read. Then the value of the current row is assigned to the variables. So we can compare now the current row with the previous row. If something changed, we simply increment the third variable, which is a number for each "group" that we are building.
SELECT
s.*,
#group_number := IF(#prevName != s.name OR #prevStatus != s.status, #group_number + 1, #group_number) AS group_number,
#prevName := s.name,
#prevStatus := s.status
FROM status_table s
, (SELECT #group_number := 0, #prevName := NULL, #prevStatus := NULL) var_init_subquery
ORDER BY name, timestamp
So we incremented the #group_number when something changed and assigned the variable to itself if not, so that it doesn't change.
Now we can simply use this query as subquery and do a simple grouping.
SELECT
group_number AS id,
name,
status,
MIN(error) AS error,
MIN(timestamp) AS firstEntry,
MAX(timestamp) AS lastEntry,
COUNT(*) AS entries
FROM (
SELECT
s.*,
#group_number := IF(#prevName != s.name OR #prevStatus != s.status, #group_number + 1, #group_number) AS group_number,
#prevName := s.name,
#prevStatus := s.status
FROM status_table s
, (SELECT #group_number := 0, #prevName := NULL, #prevStatus := NULL) var_init_subquery
ORDER BY name, timestamp
) sq
GROUP BY
group_number,
name,
status
see it working in this sqlfiddle

MariaDB/MySQL RANK() implementation

I am working on migration from MS SQL Server to MariaDB 10.0. I have query which use RANK() PARTITION BY. I already created some kind of RANK() implementation on my table but it's not working properly.
The original query was:
RANK() OVER (PARTITION BY visits.id_partner ORDER BY visits.updated_at DESC) AS rank
My implementation for MariaDB/MySQL:
SELECT
...,
(
CASE visits.id_partner
WHEN #currId THEN
#curRow := #curRow + 1
ELSE
#curRow := 1 AND #currId := visits.id_partner
END
) AS rank
FROM
records rec
JOIN visits ON visits.id = rec.id_visit,
(
SELECT
#curRank := 0,
#currId := NULL
) r
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
I want to select row ranked by id_partner order by updated_at field. When id_partner is same as on row before RANK should increase by 1. When is different than before, it should reset to 1.
But my query is not working at all. I have still rank 1 on all rows. Can you help me find mistake?
Thank you for help!
It is tricky to use variables in MySQL/MariaDB. A variable should only be used and assigned in one statement (as you do correctly). However, AND can short-circuit variable assignment.
I use a construct like this for ROW_NUMBER(). RANK() is actually a bit of a pain . . . DENSE_RANK() and ROW_NUMBER() are simpler. However, this seems to be the code that you are aiming for:
SELECT ...,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM records rec JOIN
visits
ON visits.id = rec.id_visit CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC;
EDIT:
In MySQL (and presumably in MariaDB), sometimes variables don't work quite right unless you use a subquery. So, try this:
SELECT . . .,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM (SELECT ...
FROM records rec JOIN
visits
ON visits.id = rec.id_visit
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
) t CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params;

Query to get change in value day-over-day?

I have some historical data in a MySQL database, and I'm making graphs of it. Currently, my graphs look like this:
I'd like, however, to get the change in the value over time - i.e. show the change since the last measurement. Something like this (obviously the data isn't correct):
How can I write a MySQL query that gets the delta of a value when compared with the previous row?
This is my current query, with help from juergen d:
SELECT `timestamp`, total_users, total_users - #prev AS col1, #prev := total_users, FROM stats, (select #prev := 0) p GROUP BY DAY(`timestamp`), floor(HOUR(`timestamp`)/2)
Generally
Use a variable that holds the last records' value to calculate the delta
select some_column,
some_column - #prev as delta,
#prev := some_column
from your_table, (select #prev := 0) p
SQLFiddle demo
The actual query
SELECT DAY(`timestamp`) as day_ts,
floor(HOUR(`timestamp`)/2) as hour_ts
sum(total_users)
sum(total_users) - #prev AS col1,
#prev := sum(total_users)
FROM stats, (select #prev := 0) p
GROUP BY day_ts, hour_ts

Updating Incremental Values in a Column for a Related Column in MySQL

I'm working on fixing our (forum) database as it seems that several posts in certain threads are showing up with the same post number (#2). It seems that they all hold a position of "1" within each respective forum thread.
I've managed to find a query that will set these position values to the proper number via the query below:
select #i := -1; update `xf_post` set position = (select #i := #i + 1) where thread_id=1;
Unfortunately, I've only been doing this update query through the MySQL command line where I'm constantly selecting the Up Arrow key, incrementing the 'thread_id' value by 1, and hitting the Return key. Is there a faster way to do this through a loop or cursor? I'm not very familiar with the other parts of the SQL syntax and I only get by with the basics.
You can use the following query to get the numbers that you want for each thread:
select thread_id,
(case when #prev = thread_id then #rn := 1 else #rn := #rn + 1 end) as rn,
#prev = #threadid
from xf_post p cross join
(select #rn := 0, #thread_id = -1) const
order by thread_id, position
You can test this out to be sure that it produces the right values. Next you can do an update with a join:
update xf_post join
(select thread_id, post_id,
(case when #prev = thread_id then #rn := 1 else #rn := #rn + 1 end) as rn,
#prev = #threadid
from xf_post p cross join
(select #rn := 0, #thread_id = -1) const
order by thread_id, post_id
) toupdate
on xf_post.post_id = toupdate.post_id
set xf_post.position = toupdate.rn;