This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Create a trigger that updates a column on one table when a column in another table is updated
I got a database table with these values
UpVotes | DownVotes | PercentVotes
8 | 2 | 80
10 | 0 | 100
5 | 5 | 50
560 | 34 | 94
is it possible to get the mysql database to work like this:
UpVotes | DownVotes | PercentVotes
8 | 2 | (UpVotes/(UpVotes + DownVotes)) x 100 = 80
10 | 0 | (UpVotes/(UpVotes + DownVotes)) x 100 = 100
5 | 5 | (UpVotes/(UpVotes + DownVotes)) x 100 = 50
560 | 34 | (UpVotes/(UpVotes + DownVotes)) x 100 = 94
automatically without having to update it via any script? Just like in excel when you can then change the UpVotes value or DownVotes value and the PercentVotes value automatically becomes the new correct percent of upvotes according to the total votes.
Sorry for my bad english but i hope you get my point. :)
You can use a trigger for that. Something like this:
delimiter |
CREATE TRIGGER calc_percentages BEFORE INSERT on your_table
FOR EACH ROW
BEGIN
SET NEW.PercentVotes = NEW.UpVotes/(NEW.UpVotes + NEW.DownVotes);
END
|
delimiter ;
It will run on every insert and add the calculated value. If you want it to update after an update then just add another trigger for after update instead of before insert.
Since the percentage information is a calculated value you can calculate it in your selects on-the-fly and don't store it in your DB. But if you really need the performance gain then use a trigger to store it.
You would want to run something like this on the select, you may need to check the math though.
select UpVotes, DownVotes ((UpVotes / (UpVotes + DownVotes)) * 100) as PercentVotes from my_table;
Yes it is possible.
You can use triggers. Just set trigger on update and insert and it will change value of column.
if you are using triggers then try something like this
delimiter |
CREATE TRIGGER insertPercentVotes AFTER INSERT ON YourTableName
// calculate percentage here
// Execute Update query here
END;
|
delimiter //
CREATE TRIGGER ins_votepercent BEFORE INSERT ON votetable FOR EACH ROW
SET NEW.PercentVotes = (NEW.UpVotes/(NEW.UpVotes+NEW.DownVotes)) * 100
END;//
CREATE TRIGGER upd_votepercent BEFORE UPDATE ON votetable FOR EACH ROW
SET NEW.PercentVotes = (NEW.UpVotes/(NEW.UpVotes+NEW.DownVotes)) * 100
END;//
See MySQL Manual
Related
I have a MariaDB table with an auto-incremented index, but also a "sortorder" field that controls the, well, sort order, when data is queried and displayed.
E.g.
id title sortorder
1 this 10
2 that 30
3 other 20
4 something 25
So far, so good. I'd like to create a function to automatically re-order these though - well, not re-order, but redo the values of the sortorder column per the existing order. The desired outcome from the above after running the function would be this:
id title sortorder
1 this 10
2 that 40
3 other 20
4 something 30
Is this something that can be done with an SQL statement in MariaDB (I have not found anything for that yet), or do I need to do it in my (php) application?
The logic for the new sort order values is based on the ordering by the sortorder column.
The reason for renumbering is that the sort order values are going to be manually maintained in the application, but it may be occasionally helpful to start with a clean slate. Users will be trained to "leave some room" in the values to allow for future edits.
On day one, "sortorder" will get (manually) populated with, say, 10, 20, 30, etc. Or possibly 100, 200, 300, etc. So that if they need to reorder things in the future, this will allow changing one item's sortorder value to say 25, to put it between the items with 20 and 30. Make sense?
But eventually, it's possible that the users could paint themselves into a corner, or at any rate make things confusing for themselves. It would be nice to build them a button that simply goes through the rows, and re-sets all the sortorder values, to preserve the existing row order but to make the values of sortorder be spaced evenly by intervals anew.
This would require some subqueries to be written inside. Steps of what I did are as follows:
Table name I used is tt. You need to change it according to your table name.
First is to get all rows in sorted order of sortorder column.
Second, declare a variable, say #serial_no and keep incrementing it by 1 on every selected row. This is an old school technique but I find it more readable.
Assign new sortorder values in this new parent select query. For now, I have just multiplied it's serial number(as in rank) by 10. You can adjust accordingly.
In your update query, inner join current copy of the table being updated with this select query and update the new sortorder column values correctly by matching them on id column.
Snippet:
update tt A
inner join (
select id, title, (#serial_no := #serial_no + 1) as serial_no,#serial_no * 10 as `sortorder`
from (
select *
from tt
order by sortorder asc
) temp_derived,(SELECT #serial_no := 0) as sn
) B
on A.id = B.id
set A.sortorder = B.sortorder
Update:
I just realised the control is completely shifted from user to DB. If you wish to update multiple rows with their new sortorder values, I wish to propose a workaround technique since I have never seen updating multiple rows with new values submitted from user in bulk(happy to learn if there exists one).
You need to map old values with new values, say in an associative array in PHP.
Start a DB transaction in MySQL.
Insert all new rows in bulk.
Delete all previous old rows in one go with IDs sent from PHP (in a prepared statement preferably with the previously mapped assoc array)
Commit the transaction.
Rollback ofcourse if something goes wrong.
Update: This solution works in MySQL 8.0, but not in MariaDB, because MariaDB's support for CTE doesn't support UPDATE statements. I'll leave this solution here for readers who use MySQL, but it doesn't work for MariaDB.
mysql> select * from NoOneEverNamesTheirTableInSqlQuestions;
+----+-----------+-----------+
| id | title | sortorder |
+----+-----------+-----------+
| 1 | this | 10 |
| 2 | that | 30 |
| 3 | other | 20 |
| 4 | something | 25 |
+----+-----------+-----------+
mysql> with cte as (
select id, row_number() over (order by sortorder) * 10 as new_sortorder
from NoOneEverNamesTheirTableInSqlQuestions
)
update NoOneEverNamesTheirTableInSqlQuestions join cte using (id)
set sortorder = new_sortorder;
Query OK, 2 rows affected (0.01 sec)
Rows matched: 4 Changed: 2 Warnings: 0
mysql> select * from NoOneEverNamesTheirTableInSqlQuestions;
+----+-----------+-----------+
| id | title | sortorder |
+----+-----------+-----------+
| 1 | this | 10 |
| 2 | that | 40 |
| 3 | other | 20 |
| 4 | something | 30 |
+----+-----------+-----------+
In MariaDB you can do:
update t
join (
select id, 10 * row_number() over (order by sortorder) as rn10
from t
) x on x.id = t.id
set t.sortorder = x.rn10;
Result:
id title sortorder
--- ---------- ---------
1 this 10
2 that 40
3 other 20
4 something 30
See running example at db<>fiddle.
ID | PID | NAME | VALUE |
-------------------------------------
60 | 1 | Test1 | 9999 |
21 | 2 | Test2 | 9999 |
44 | 1 | Test3 | 9999 |
37 | 4 | Test4 | 9999 |
24 | 1 | Test5 | 9999 |
Hey all!
I am kind of new to PHP and DBs so I really dont know how to start with this.
So I want to want to make a sorting inside a DB where the IDs differ too much.
(that means that the first ID starts with 34 and the next one is something like 43 next is 55 etc.)
I have a table which looks like the one above.
Now what I would like to do is changing the values in the column VALUE depending on the values which are in PID.
This means that if in PID the value equals 1 the VALUE on the same row should become 1001 and for the next one 1002, next 1003.
If PID = 2 then VALUE should be changed to 2001 then 2002 then 2003 etc.
This would be for an already existing table but I would also like to include the VALUE values everytime I add a new statement into that table.
So a simple check in pseudocode:
If PID equals 1
then check VALUE column for the highest number that starts with "1"
make it +1 and add it into the column of that row
Is that possible to do?
what would you guys suggest me to do instead (to make things easier)?
If you need further info, tell me please and I will try to explain things better, I dont know if my explanation says what I'm trying to do.
Thank you in advance.
Cheers,
K.
You can use UPDATE .. JOIN and join to a derived table containing the "rank" of each ID , and update accordingly :
UPDATE YourTable t
JOIN(SELECT s.ID,s.PID,COUNT(*) as cnt
FROM YourTable s
JOIN YourTable s2
ON(s.pid = s2.pid AND s.id >= s2.id)) p
ON(t.id = p.id)
SET t.value = (1000*t.pid) + p.cnt
The inner query here basically "ranks" the data by a self join. It joins to it self by the condition s.pid = s2.pid AND s.id >= s2.id , in words - Same PID that happen before me including me, so the first one will join to 1 record, the second to two and so on.. Then you just update value column to pid*1000 , plus the rank.
I have thousands of entries in my database, each with 9 digits or less. I would like to do a mass update, find all rows with digits less than 9 and add 0's to make them equal 9 digits.
For example, my table looks like:
ID | Number
---------------
0 | 489379
1 | 854744329
2 | 56456669
I would like to make it look like:
ID | Number
---------------
0 | 000489379
1 | 854744329
2 | 056456669
How would I do this with a MySQL query?
The lpad function should solve your issue:
SELECT `id`, LPAD(`Number`, 9, '0')
FROM mytable
To answer the question in the comment, this can also be applied in an update statement:
UPDATE mytable
SET `Number` = LPAD(`Number`, 9, '0')
WHERE LENGTH(`Number`) < 9
Use a case statement
update table
set column1 = case when length(column1) = 4 then concatenate('00000', column1)....
Have an element in the case statement for every possible length of the column. Kind of manual, and there is likely an easier way, but this is one possibility.
I have a table which contains a standard auto-incrementing ID, a type identifier, a number, and some other irrelevant fields. When I insert a new object into this table, the number should auto-increment based on the type identifier.
Here is an example of how the output should look:
id type_id number
1 1 1
2 1 2
3 2 1
4 1 3
5 3 1
6 3 2
7 1 4
8 2 2
As you can see, every time I insert a new object, the number increments according to the type_id (i.e. if I insert an object with type_id of 1 and there are 5 objects matching this type_id already, the number on the new object should be 6).
I'm trying to find a performant way of doing this with huge concurrency. For example, there might be 300 inserts within the same second for the same type_id and they need to be handled sequentially.
Methods I've tried already:
PHP
This was a bad idea but I've added it for completeness. A request was made to get the MAX() number for the item type and then add the number + 1 as part of an insert. This is quick but doesn't work concurrently as there could be 200 inserts between the request for MAX() and that particular insert leading to multiple objects with the same number and type_id.
Locking
Manually locking and unlocking the table before and after each insert in order to maintain the increment. This caused performance issues due to the number of concurrent inserts and because the table is constantly read from throughout the app.
Transaction with Subquery
This is how I'm currently doing it but it still causes massive performance issues:
START TRANSACTION;
INSERT INTO objects (type_id,number) VALUES ($type_id, (SELECT COALESCE(MAX(number),0)+1 FROM objects WHERE type_id = $type_id FOR UPDATE));
COMMIT;
Another negative thing about this approach is that I need to do a follow up query in order to get the number that was added (i.e. searching for an object with the $type_id ordered by number desc so I can see the number that was created - this is done based on a $user_id so it works but adds an extra query which I'd like to avoid)
Triggers
I looked into using a trigger in order to dynamically add the number upon insert but this wasn't performant as I need to perform a query on the table I'm inserting into (which isn't allowed so has to be within a subquery causing performance issues).
Grouped Auto-Increment
I've had a look at grouped auto-increment (so that the number would auto-increment based on type_id) but then I lose my auto-increment ID.
Does anybody have any ideas on how I can make this performant at the level of concurrent inserts that I need? My table is currently InnoDB on MySQL 5.5
Appreciate any help!
Update: Just in case it is relevant, the objects table has several million objects in it. Some of the type_id can have around 500,000 objects assigned to them.
Use transaction and select ... for update. This will solve concurrency conflicts.
In Transaction with Subquery
Try to make index on column type_id
I think by making index on column type_id it will speed up your subquery.
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,type_id INT NOT NULL
);
INSERT INTO my_table VALUES
(1,1),(2,1),(3,2),(4,1),(5,3),(6,3),(7,1),(8,2);
SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON y.type_id = x.type_id
AND y.id <= x.id
GROUP
BY id
ORDER
BY type_id
, rank;
+----+---------+------+
| id | type_id | rank |
+----+---------+------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 4 | 1 | 3 |
| 7 | 1 | 4 |
| 3 | 2 | 1 |
| 8 | 2 | 2 |
| 5 | 3 | 1 |
| 6 | 3 | 2 |
+----+---------+------+
or, if performance is an issue, just do the same thing with a couple of #variables.
Perhaps an idea to create a (temporary) table for all rows with a common "type_id".
In that table you can use auto-incrementing for your num colomn.
Then your num shoud be fully trustable.
Then you can select your data and update your first table.
I know about the RAND() function in SQL to select random rows from a table, but the problem is I don't want it selecting different random rows each time I refresh the browser. I want the same set of random rows, which is selected based on a key.
For example, say this is my table:
----------------------
| id | word | literal|
----------------------
| 1 | say | YAS |
----------------------
| 2 | eat | TAE |
----------------------
| 3 | hit | TIH |
----------------------
| 4 | bad | DAB |
----------------------
| 5 |delve | EVLED |
----------------------
maybe if the key was 6, it would select rows 4 & 5 every time. But maybe if the key was 3, it would select rows 2 and 5. So it would select a set of random rows each time based on a key.
Is this possible?
You can use the : Rand(N) form of the MySQL function and take care to pass the same N each time you want the same sequence of generated random numbers. The N could stay the same during a specific session or it could be stored in a cookie for use over a longer period. It depends on how long you need the sequence to remain the same.
Good thought with the md5 hash, but there's a much easier way to do it. Generate your random number however you want and the use the $_SESSION superglobal to store the number.
Example:
session_start();
if(!isset($_SESSION["randomNumber"])){
$_SESSION["randomNumber"] = generateRandomQuery();
}
You'd then be able to use the number when you build your query. Using PDO, it'd be like this:
$number = $_SESSION["randomNumber"];
$query = $database->prepare("SELECT id FROM *databaseName* where id = :id");
$query->execute(array(":id" => $number));
An idea..
Save the row id in SESSION/cookie,
$var = value from $_SESSION/cookie
if (isset($var)){
$sql = select RAND...
} else {
$sql = select ..... where row = $var
}