Insert sequential numbers based on another field - MySQL - mysql

There is a similar question
Insert sequential number in MySQL
I want to insert sequential numbers to the table, but based on another field. I have two columns page_numner and parent, so the rows with same parent should have page_number as consequtive numbers. If parent changes, the page should start from 1 again and increase by one.
I was thinking to use smth like this
SELECT #i:=0;
SELECT #p:=0;
UPDATE my_table AS t SET page_number = CASE
WHEN #p = t.`parent` THEN #i:=#i+1
ELSE 1 -- assign current parent to #p ??
END
but, it cant figure out how to assign the new parent into #p for the else case.
Please note, that I am trying to achieve this with pure mysql (if possible of course)
Thanks

You can do what you want with this code:
set #p := -1;
set #i := 0;
UPDATE my_table t
SET page_number = (CASE WHEN #p = t.`parent` THEN #i := #i+ 1
WHEN (#p := t.parent) = NULL THEN NULL -- never happens
ELSE #i := 1
END)
ORDER BY t.parent;
Unfortunately, MySQL doesn't allow both ORDER BY and JOIN in the same UPDATE query. If it did, you could initialize the variables in the query.
Note the second condition just does the assignment. = NULL never returns TRUE.

Related

My MySQL function doesn't quite do what I want

This came from a question in Python, I thought, I could do this directly in MySQL. The guy said, the .csv file has millions of rows, so I think this came originally from a MySQL table.
I have never made a MySQL function before, so it's all a bit new.
A simple table, 3 columns: id, price and signal_
The task is, look at each price.
The first reference price is the price in row 1 and going down.
Going down, if the price has increased by 1 or more, write 1 in the column signal_
Now the price in that row is the reference price.
Or if the price has decreased by 1 or more, that price is the new reference price.
Always write 1 in column signal_ if the price has increased by 1 or more.
Always write -1 in column signal_ if the price has decreased by 1 or more.
The column signal contains 0 to begin with.
I made this function, but it doesn't get everything right. I think, maybe the scope of the variables is causing the problem. Maybe #start is a local variable in the function and another variable in MySQL
DELIMITER //
CREATE FUNCTION comparePrice(p DECIMAL)
RETURNS INT DETERMINISTIC
BEGIN
DECLARE signalnum INT;
# #start is the first value in row 1 when we start
# if p > #start
IF p - #start >= 1 THEN SET signalnum := 1, #start := p;
# if #start - p >= 1 p is smaller than #start by 1 or more
ELSEIF #start - p >= 1 THEN SET signalnum := -1, #start := p;
# if the change is less than 1, signalnum = 0
ELSE SET signalnum := 0, #start = p;
END IF;
RETURN signalnum;
END; //
DELIMITER ;
I looked at threads for getting the value of the next price but I couldn't make it work.
# initialize #start
SELECT #start := price FROM prices_up_down WHERE id = 1;
UPDATE prices_up_down SET signal_ = comparePrice(price);
Maybe it will never work like this!
I will be very grateful for any advice!
EDIT: the problem is my user variable #start gets truncated, resulting in wrong values in the column signal_
Anyone know why #start is truncated??
You can use a common table expression (cte) to get each row along with the previous value, then just update signal with an expression comparing the two;
WITH cte AS (
-- Get id and price along with the price from the previous row (by id)
SELECT id, price new_price, LAG(price) OVER (ORDER BY id) old_price
FROM prices_up_down
)
-- Join the cte with the original table since cte's aren't updatable in MySQL
UPDATE prices_up_down JOIN cte ON prices_up_down.id = cte.id
-- Set signal to 1 if the diff in price is > 1, or -1 if it's < -1
SET `signal` =
CASE WHEN new_price-old_price > 1 THEN 1
WHEN new_price-old_price < -1 THEN -1
ELSE 0
END;
A DBfiddle to test with
As always, if the data is important, always back up your table and test things before running updates from random people on the Internet.

MySQL Variable Assignment via Procedure Not Working Correctly

In the code below, I'm trying go through the results of endDateTable row by row, comparing the current row's endDate to the previous row's endDate. If there has been any change since the previous, we increment #revisionNum. However, upon populating the new table, all of the #revisionNum entries are 0. What am I doing wrong?
NOTE: I'm using prepared statements in this manner since doing a straightforward SELECT into a variable gives a syntax error due to the LIMIT clause not allowing a variable in our version of MySQL.
BEGIN
DECLARE _currentEndDate DATETIME DEFAULT now();
DECLARE _priorEndDate DATETIME DEFAULT now();
SET #ResultsCount = (SELECT COUNT(*) FROM mainTable);
SET #j = 0;
WHILE #j < #ResultsCount DO
SET #revisionNum = 0;
/*CURRENT END DATE*/
SET #appResultQueryCurrent = CONCAT('
SELECT
end_date
INTO _currentEndDate
FROM endDateTable
LIMIT ', #j, ', 1'
);
PREPARE currentQueryStmt FROM #appResultQueryCurrent;
EXECUTE currentQueryStmt;
/*PREVIOUS END DATE*/
SET #appResultQueryPrior = CONCAT('
SELECT
end_date
INTO _priorAppEndDate
FROM endDateTable
LIMIT ', IF(#j = 0, 0, #j - 1), ', 1'
);
PREPARE priorQueryStmt FROM #appResultQueryPrior;
EXECUTE priorQueryStmt;
SET #revisionNum = IF(
#j = 0 OR (_currentEndDate = _priorEndDate),
#revisionNum,
IF(
_currentEndDate != _priorEndDate,
#revisionNum + 1,
#revisionNum
)
);
INSERT INTO finalTable (RevisionNum)
SELECT
#revisionNum AS RevisionNum
FROM endDateTable;
SET #j = #j +1;
END WHILE;
END $$
You don't need a loop, you can use INSERT INTO ... SELECT ..., incrementing the variable in the select query.
You also need an ORDER BY criteria to specify how to order the rows when comparing one row to the previous row.
INSERT INTO finalTable (RevisionNum, otherColumn)
SELECT revision, otherColumn
FROM (
SELECT IF(end_date = #prev_end_date, #revision, #revision := #revision + 1) AS revision,
#prev_end_date := end_date,
otherColumn
FROM endDateTable
CROSS JOIN (SELECT #prev_end_date := NULL, #revision := -1) AS vars
ORDER BY id) AS x
DEMO
The offset value in the LIMIT clause is tenuous without an ORDER BY.
Without an ORDER BY clause, MySQL is free to return results in any sequence.
There is no guarantee that LIMIT 41,1 will return the row before LIMIT 42,1, or that it won't return the exact same row as LIMIT 13,1 did.
(A table in a relational database represents an unordered set of tuples, there is no guaranteed "order" or rows in a table.)
But just adding ORDER BY to the queries isn't enough to fix the Rube-Goldberg-esque rigmarole.
In the code shown, it looks like each time through the loop, we're inserting a copy of endDateTable into finalTable. If that's 1,000 rows in endDateTable, we're going to get 1,000,000 rows (1,000 x 1,000) inserted into finalTable. Not at all clear why we need so many copies.
Given the code shown, it's not clear what the objective is. Looks like we are conditionally incrementing revisionNum, the end result of which is the highest revision num. Just guessing here.
If there is some kind of requirement to do this in a LOOP construct, within a procedure, I'd think we'd do a cursor loop. And we can use procedure variables vs user-defined variables.
Something along these lines:
BEGIN
DECLARE ld_current_end_date DATETIME;
DECLARE ld_prior_end_date DATETIME;
DECLARE li_done INT;
DECLARE li_revision_num INT;
DECLARE lcsr_end_date CURSOR FOR SELECT t.end_date FROM `endDateTable` t ORDER BY NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET li_done = TRUE;
SET li_done = FALSE;
SET li_revision_num = 0;
OPEN lcsr_end_date;
FETCH lcsr_end_date INTO ld_current_end_date;
SET ld_prior_end_date = ld_current_end_date;
WHILE NOT li_done DO
SET li_revision_num = li_revision_num + IF( ld_current_end_date <=> ld_prior_end_date ,0,1);
SET ld_prior_end_date := ld_current_end_date;
FETCH lcsr_end_date INTO ld_current_end_date;
END WHILE;
CLOSE lcsr_end_date;
INSERT INTO `finalTable` (revisionnum) VALUES (li_revision_num);
END $$
Note the "order by" clause on the SELECT, its not clear what the rows should be ordered on, so we're using a literal as a placeholder.
As the end result, we insert a single row into finalTable.
Again, it's not clear what the code in the question is supposed to achieve, but doing a cursor loop across ordered rows would be much more efficient than a bazillion dynamic SQL executions fetching individual rows.

How to add series number value on my sql columns

I have a table and I need to put a series number(without duplicate) on each value of the columns.
I do it manually by editing each columns using this,|
UPDATE mytable where column = '123'
SET table_column1 = '955';
then next
UPDATE mytable where column = '124'
SET table_column2 = '956';
but this one takes time for me.
There's any solution or fastest way to do this??
I'm thinking of I will add another auto increment but 2 increment is advisable ?
Here's the screenshot of my data below
You can't add another auto-increment. But you can do it with one query if you don't care about a specific order
UPDATE mytable
cross join (select #rank := 0) r
SET table_column2 = 956 + (#rank := #rank + 1)
SQLFiddle demo
If you need a specific order then you can define the #rank variable outside the query and add an order by
set #rank := 0;
UPDATE mytable
SET table_column2 = 956 + (#rank := #rank + 1)
order by id
SQLFiddle demo

Modify column values to that they are in order

I got a very special problem that I'd like to solve in SQL. I need to make sure that optionOrder for the same questionID goes from 0-[any number].
So for example the rows with questionID = 18386, their optionOrder are right now 1,2,3,4. They need to be 0,1,2,3.
Also if the rows are like this 1,2,4, it needs to be 0,1,2
I'm sorry for the incorrect grammars.
In MySQL, you can do this with variables:
set #rn := 0;
set #q := -1;
update table t
set optionorder = (case when #q = questionid then (#rn := #rn + 1)
when (#q := questionid) is not null then (#rn := 0)
else -1 -- should never happen
end)
order by questionid, optionorder;
Because of the order by, you need to set the variables outside the update.

SQL how to extract a value from a variable

I'm looking for a way to extract the value from a SQL variable and save this value in a new variable. For example, if I have the variable #myVar, which is storing the value 25, and I want to save this value to a new variable #myVar1 before updating the value stored in #myVarto 30. I've found that simply setting #myVar1:= #myVar does not work as once I change the value stored in #myVarto 30 the #myVar1 variable will automatically update itself to 30 as well.
I am using these variables in a SELECT statement in order to keep track of and compare values in a previous row to values in the current row.
The relevant code snippet is:
SELECT
CASE
When (#r is not null) then #myVar1:= #myVar
END,
#myVar:= #myVar + 1
As you can see, #myVar is set to a new value every row, while #myVar1 should only be set to the current value of #myVar if the condition is met, and remain storing that value until the condition is met again, regardless of any changes to the value #myVar is storing. So if #myVar = 7 and #r is not null then #myVar1 = 7, but when #myVar will then be set to 8, #myVar1 will also be set to 8 instead of remaining at 7 until the condition is met again.
Maybe you should do something like this:
#x_old := x;
#x := case when #r is not null then #y else #x end;
(If this is in a procedure, you can leave the ; right where they are; if this is inside a query, replace them with ,)
This way you will preserve the value of #x if the condition is not fulfilled