How does this reference with '#' work in mysql? - mysql

I'm new to mysql
I want to change all values in a column once a week. There is no condition.
I have this to do it, but I don't understand how this works.
Could someone please elucidate?
This works:
UPDATE maxscore, (select #row := 0) r SET max_score = (#row := 50);
This also works:
UPDATE maxscore SET max_score = (#row := 60);
Is this the best way to do this?
In my book there was an example which went like:
SELECT COUNT(*) FROM author WHERE
I tried like that, but I couldn't make it work.

The query that you are using works by cross-joining the maxscore table to a derived table that simply initialises #row to 0; then in the SET part of the query #row is set to 50 prior to being assigned to max_score. The second query also works because there's no need to initialise #row since it gets overwritten before being used. However, for what you're doing a simple
UPDATE maxscore
SET max_score = 50
is all you need; there is no need to use variables in the query at all.

Related

MySQL 5.5.56 Temporary table always empty when used in a trigger but works when manually running the query

Can someone help or clear things for me. I've got this SQL code that I need to run on a trigger that doesn't work. But works when manually running the code on an SQL client.
SET #sr_id = NEW.purchase_id; /* SET #sr_id = 123456 when run manually */
SET #ndi = (SELECT COUNT(a.id) FROM purchase_rewards a LEFT JOIN item b ON b.id = a.item_id WHERE a.unit_id IS NOT NULL AND COALESCE(b.is_privileged,0) = 0 AND a.purchase_id = #sr_id);
SET #res = #ndi - CEIL(#ndi/2);
DROP TEMPORARY TABLE IF EXISTS for_removal;
CREATE TEMPORARY TABLE for_removal
SELECT ID FROM (
SELECT a.id, #rownum := #rownum + 1 AS `rank` FROM purchase_rewards a LEFT JOIN item b ON b.id = a.item_id
WHERE a.purchase_id = #sr_id AND COALESCE(b.is_privileged,0) = 0
) ft CROSS JOIN (SELECT #rownum := 0) r WHERE `rank` <= #res;
DELETE ta FROM purchase_rewards ta INNER JOIN for_removal tb ON ta.id = tb.id WHERE ta.purchase_id = #sr_id;
The code queries the purchased items that are not "privileged", putting a rank column on each and removing half of them. You only get rewarded for half of it, that's the point. The software was created by someone else with no source code so this is a piggy back system behind it.
I placed debug codes in between each to see if the connection changes or the results where empty but all is good except for the last part. Before the delete part I added a debug code:
SET #icount = (SELECT COUNT(ID) FROM for_removal);
INSERT INTO debug_log SET `log` = #icount;
and the result is that the table is always empty. I also tried converting the code into a stored procedure but I'm getting the same problem. Only running the code manually where it works.
I'm currently settling on CURSOR and loop-deletes which works, but it is slower when there are hundreds of items.
Sample Data: dbfiddle
Thanks!
Based on the comments above, the answer is to set the #rownum variable before the query.
SET #rownum = 0;
CREATE TEMPORARY TABLE ...
The reason is that you can't depend on the order of table evaluation in the CROSS JOIN. If the subquery is evaluated before the initialization of #rownum, then #rownum will be NULL, and any attempt to increment it with #rownum := #rownum + 1 will also yield NULL. So rank will have NULL on every row, and no rows will satisfy the WHERE clause.
As for why this works in the MySQL client but not in the trigger, I have a theory:
The session variable #rownum will keep its value if you test your query multiple times. So if you set it to some non-NULL value once in a session, then test the ranking query in the same session subsequently, it will increment.
But if you run it as part of a trigger, it will likely be a brand new session each time, and the value of #rownum will be initially NULL.

MySQL limit work around

I need to limit records based on percentage but MYSQL does not allow that. I need 10 percent User Id of (count(User Id)/max(Total_Users_bynow)
My code is as follows:
select * from flavia.TableforThe_top_10percent_of_the_user where `User Id` in (select distinct(`User Id`) from flavia.TableforThe_top_10percent_of_the_user group by `User Id` having count(distinct(`User Id`)) <= round((count(`User Id`)/max(Total_Users_bynow))*0.1)*count(`User Id`));
Kindly help.
Consider splitting your problem in pieces. You can use user variables to get what you need. Quoting from this question's answers:
You don't have to solve every problem in a single query.
So... let's get this done. I'll not put your full query, but some examples:
-- Step 1. Get the total of the rows of your dataset
set #nrows = (select count(*) from (select ...) as a);
-- --------------------------------------^^^^^^^^^^
-- The full original query (or, if possible a simple version of it) goes here
-- Step 2. Calculate how many rows you want to retreive
-- You may use "round()", "ceiling()" or "floor()", whichever fits your needs
set #limrows = round(#nrows * 0.1);
-- Step 3. Run your query:
select ...
limit #limrows;
After checking, I found this post which says that my above approach won't work. There's, however, an alternative:
-- Step 1. Get the total of the rows of your dataset
set #nrows = (select count(*) from (select ...) as a);
-- --------------------------------------^^^^^^^^^^
-- The full original query (or, if possible a simple version of it) goes here
-- Step 2. Calculate how many rows you want to retreive
-- You may use "round()", "ceiling()" or "floor()", whichever fits your needs
set #limrows = round(#nrows * 0.1);
-- Step 3. (UPDATED) Run your query.
-- You'll need to add a "rownumber" column to make this work.
select *
from (select #rownum := #rownum+1 as rownumber
, ... -- The rest of your columns
from (select #rownum := 0) as init
, ... -- The rest of your FROM definition
order by ... -- Be sure to order your data
) as a
where rownumber <= #limrows
Hope this helps (I think it will work without a quirk this time)

MySQL batch queries with limit

I need to change the ownership of several possessions in my MySQL table. The thing that's tripping me up is that there are several identical goods and I don't want to change all of them.
Right now, I'm doing it with queries like this:
UPDATE possessions SET citizen_id=1 WHERE citizen_id=2 AND good_id=8 LIMIT 1
UPDATE possessions SET citizen_id=2 WHERE citizen_id=4 AND good_id=2 LIMIT 1
UPDATE possessions SET citizen_id=4 WHERE citizen_id=3 AND good_id=5 LIMIT 2
There are, some times, a lot of these and they're all so similar that I feel like there should be a way to submit them in a batch. The examples I can find that show batch updates don't allow setting individual limits on each update like I need to.
Do you know of any way I can make this process faster?
The following method relies on the fact that the possessions table has a primary key and citizen_id is not part of it. Here's the idea:
Put all the parameters of the update (citizen_id and good_id to filter on, the new values of citizen_id and the numbers of rows to update) into some storage, a dedicated table, perhaps, or a temporary table.
Assign row numbers to possessions rows partitioning on (citizen_id, good_id), then join the ranked row set to the parameter table to filter the original full set on citizen_id and good_id, as well as the number of rows.
Join possessions and the result of the previous join on the primary key values and update citizen_id with the new values.
In MySQL's SQL, the above might look like this:
UPDATE possessions AS p
INNER JOIN
(
SELECT
#r := #r * (#c = p.citizen_id AND #g = p.good_id) + 1 AS r,
p.possession_id,
#c := p.citizen_id AS citizen_id,
#g := p.good_id AS good_id
FROM
possessions AS p
CROSS JOIN
(SELECT #r := 0, #c := 0, #g := 0) AS x
ORDER BY
p.citizen_id,
p.good_id
) AS f ON p.possession_id = f.possession_id
INNER JOIN
possession_updates AS u ON u.citizen_id = f.citizen_id AND u.good_id = f.good_id
SET
p.citizen_id = u.new_citizen_id
WHERE
f.r <= u.row_count
;
The possessions_update is the table containing the parameter values.
The query uses a known method of row numbering that employs variables, which is implemented in the f subquery.
I don't have MySQL so I can't test this properly from the performance point of view, but at least you can see from this SQL Fiddle demo that the method works. (The UPDATE statement is in the schema script, because SQL Fiddle doesn't allow data modification statements in the right-side script for MySQL. The right side just returns the post-UPDATE contents of possessions.)

Reorder a MYSQL table

I have a MySql table with a 'Order' field but when a record gets deleted a gap appears
how can i update my 'Order' field sequentially ?
If possible in one query 1 1
id.........order
1...........1
5...........2
4...........4
3...........6
5...........8
to
id.........order
1...........1
5...........2
4...........3
3...........4
5...........5
I could do this record by record
Getting a SELECT orderd by Order and row by row changing the Order field
but to be honest i don't like it.
thanks
Extra info :
I also would like to change it this way :
id.........order
1...........1
5...........2
4...........3
3...........3.5
5...........4
to
id.........order
1...........1
5...........2
4...........3
3...........4
5...........5
In MySQL you can do this:
update t join
(select t.*, (#rn := #rn + 1) as rn
from t cross join
(select #rn := 0) const
order by t.`order`
) torder
on t.id = torder.id
set `order` = torder.rn;
In most databases, you can also do this with a correlated subquery. But this might be a problem in MySQL because it doesn't allow the table being updated as a subquery:
update t
set `order` = (select count(*)
from t t2
where t2.`order` < t.`order` or
(t2.`order` = t.`order` and t2.id <= t.id)
);
There is no need to re-number or re-order. The table just gives you all your data. If you need it presented a certain way, that is the job of a query.
You don't even need to change the order value in the query either, just do:
SELECT * FROM MyTable WHERE mycolumn = 'MyCondition' ORDER BY order;
The above answer is excellent but it took me a while to grok it so I offer a slight rewrite which I hope brings clarity to others faster:
update
originalTable
join (select originalTable.ID,
(#newValue := #newValue + 10) as newValue
from originalTable
cross join (select #newValue := 0) newTable
order by originalTable.Sequence)
originalTable_reordered
on originalTable.ID = originalTable_reordered.ID
set originalTable.Sequence = originalTable_reordered.newValue;
Note that originalTable.* is NOT required - only the field used for the final join.
My example assumes the field to be updated is called Sequence (perhaps clearer in intent than order but mainly sidesteps the reserved keyword issue)
What took me a while to get was that "const" in the original answer was not a MySQL keyword. (I'm never a fan of abbreviations for that reason -- the can be interpreted many ways at times especially at these very when it is best they not be misinterpreted. Makes for verbose code I know but clarity always trumps convenience in my books.)
Not quite sure what the select #newValue := 0 is for but I think this is a side effect of having to express a variable before it can be used later on.
The value of this update is of course an atomic update to all the rows in question rather than doing a data pull and updating single rows one by one pragmatically.
My next question, which should not be difficult to ascertain, but I've learned that SQL can be a trick beast at the best of times, is to see if this can be safely done on a subset of data. (Where some originalTable.parentID is a set value).

Can't understand a mysql query

I had a question yesterday about ordering a mysql query by rand(). And I got a good answer here:
https://stackoverflow.com/a/16597706/2333744
THe code for the answer is below.
create temporary table results as
(Select *, #rn := #rn + 1 as rn, rand() as therand
from table1 inner join
table2
on table1.in = table2.in cross join
(select #rn := 0) const
where table1.T = A
);
select *
from results
where therand < 1000/#rn
order by therand
limit 500;
I understand everything except for
cross join (select #rn : = 0) const
I'm not sure what this is doing and if its important. When I remove it I get no performance change. Can anyone understand this part?
The User-Defined Variable #rn used in this case just for making a serial number column as explained in the answer of the previous question where you get this from.
The const is not used as a keyword here ... so don't be 'const-fused' by that. It is just a given name to (select #rn := 0) ... It could have been any other name like A, B, oops, aah, etc ... (see the second link below)
See example use in the folowing links to better understand the User-Defined Variables:
Create a Cumulative Sum Column in MySQL
MySql: Select Query- Make A Cumulative Sum Column
frankly, all this does is to reset the #rn variable. Is is "packed" into a select to avoid running 2 queries. The const means that it is constant, hence only evaluated once.
You could run into trouble when you remove it and add further queries into a single transaction.
Best regards
Zsolt