managing concurrency using update queries where condition - php

I am using mysql, php.
table
user_meetup
id, user_id, meetup_id with unique on (user_id, meetup_id)
if my meetup is limited by places(10 places) 9 users already marked rsvp and if 2 users doing RSVP at the same time (i just have consider a case of concurrency)
A -> select count(id) from user_meetup -> result : 9 hence go ahead
B -> select count(id) from user_meetup -> result : 9 hence go ahead
A -> insert ......
B -> insert ......
now both of them will get place and my count became 11,
my solution was to add one more table
user_meetup_count
id, meetup_id, user_count ( count set to 0 as default value and record is created as soon as meetup is created)
now if use update query like
update user_meetup_count set user_count = user_count + 1 where user_count < 10
and if I write in user_meetup table based on number of rows updated by above query
if it returns 0 mean its full and if it returns 1 I got the place
will this work in case 2 user try at same time ?
is my approach to solve concurrency problem right ?
is there a better way ?
what are the tools to testing this type of situations ?

Use lock tables before counting. And unlock it after inserting.
Or you could use GET_LOCK then RELEASE_LOCK. With this you do not need to lock all entry table.
Or explore theme about gap locking for innodb tables. With this you need to use transactions.
And you could use jMeter for testing your queries.

Related

mysql query for grouping datga based on certain condition

I am working on a quiz based project where user visits and gives quiz. I want to list count of users who must have appeared and failed in each of the 5 modules Quizzes. I am getting this properly. Now if a user reappears in any of the 5 quizzes and passes in it he/she must not be included in the earlier mentioned count.
My query for counting number of users appeared and failed in all 5 modules is,
SELECT count(DISTINCT module_id) as module_id from tbl WHERE user_id = $userId AND cleared = 0,
where user_id is user's id and cleared is set to 0 if failed and 1 if passed.
Although the above query counts number of modules that a user has passed. I am using this into a function and then comparing in if statement.
I just want to figure out what changes can I bring in here so that if a user passes in any module, they should not be included in this. I have tried but got no correct query buildup. Any suggestion will be helpful.
Use conditional aggregation:
SELECT user_id
FROM tbl
GROUP BY user_id
HAVING COUNT(DISTINCT module_id) = 5 AND SUM(cleared) = 0;
-- ^^ appeared in all 5 modules ^^ and failed them all
If you want to restrict the above query to just one or a few users, then you may add back the WHERE clause.

How to update on union

I have a query that does a union on 2 tables. I want to update a column of the result.
something like this:
select * from(
select a.*,'10' as srv from px_conversions_srv10 a
union all
select b.*,'12' as srv from px_conversions_srv12 b
) as ff where ff.adv_transaction_id in(1333764016);
update ff SET ff.`status`=8;
Thanks
Just run two updates:
update px_conversions_srv10
set status = 8
where adv_transaction_id in (1333764016);
update px_conversions_srv12
set status = 8
where adv_transaction_id in (1333764016);
You can run these inside a single transaction if you want them to take effect at exactly the same time.
Note: having multiple tables with the same columns is usually a sign of a poor database design. There are reasons why this might be useful (say, the tables have different replication requirements or different security requirements). But, in general, a single table is a better idea.
Since it is coming from two different tables you need to find out from which table the result comes. You can do this by adding a column to the query and later decide from the column value which table to update. You do this already with the srv column!
The update statement must be on the original table, since the union is only produced by the query. It is not a physical table in the database.
By extension of this logic, to answer the question in the title, you CANNOT execute an UPDATE on the result set of a SELECT query.
Maybe create a view table and then update it:
CREATE VIEW ff AS
select * from(
select a.*,'10' as srv from px_conversions_srv10 a
union all
select b.*,'12' as srv from px_conversions_srv12 b
) as ff where ff.adv_transaction_id in(1333764016);
update ff SET ff.`status`=8;

Mysql insert only if limit has not been reached

I hope I have got the title right as I don't really know how to word this!!
Here's the background...
I have a app inserting data to a db. The db holds a date field and data field (there are others but for simplicity these two are the only ones needed). There can only be 8 entries on the same date, no more. In normal operation this is not really an issue but twice a month the database gets hit hard towards the end of the day.
The way I currently do it, is to query how many records there are for a date. If that's less than 9 I insert the data.
It has never happened yet but my concern is that request A comes in and checks the DB and finds there are 7 records. OK to insert. But before A can insert, request B comes in and finds only 7 records. OK to insert. But that would then enter 9 records on one date. This can't happen.
I think there are two solutions to this but would like to know A, if I'm right at all! or B is there a better solution.
I think...
A) I could do it in a transaction. However, would I still no encounter the same problem? As far as I am aware as long as no queries in a transaction fail then it runs anyway.
or
B) Use a stored procedure to check first then insert. I have had very little experience with stored procedures so I must admit I have no idea what I'm talking about here!
Alternatively, is there a way to get a single query to check first if there is less than 9 entries??
Many Thanks in advance,
Gavin
You are afraid of that someone would insert a new record before other one insert it even in the million second?
I have this question too, so I just searched something for you, you can do this with two ways:
For example if you have a table which named date_table and it looks like:
date_field
----------
1998-07-13
1992-03-23
1998-09-17
1994-02-30
A) Count the rows, then insert it when it's under 8 rows. (Using PHP and MySQL)
First, get how many rows are there by this:
SELECT COUNT(*) AS Num FROM date_table WHERE date_field = '1998-07-13'
so you'll get a Num field which told you how many rows were 1998-07-13,
then using PHP to prevent user insert the same date when it's equal 8 by:
<?php if($DB['Num'] >= 8) exit('Sorry, no more same value'); ?>
otherwise insert the row.
Or you don't trust PHP, or you think someone would insert the row more earlier than 1 million second
B) Insert it when it's only under 8 rows were same with only one query:
INSERT INTO date_table (date_field)
SELECT '1998-07-13'
FROM dual
WHERE (SELECT COUNT(*) FROM date_table WHERE date_field = '1998-07-13') < 9;
BEWARE: dual is a mysql system table, so you don't need to create it
If you don't understand how the above query works:
insert into TABLE_NAME (TABLE_FIELD)
select VALUE_YOU_WANT_TO_INSERT
from dual
where (select count(*)
from TABLE_NAME
where THE_FIELD_YOU_WANT_TO_CHECK = '1998-07-13'
) < LESS_THEN_HOW_MUCH;
EDIT: add more fields change the following lines:
INSERT INTO date_table (field1, field2, field3)
SELECT value1, value2, value3

Keep only 10 records per user

I run points system on my site so I need to keep logs of different action of my users into database. The problem is that I have too many users and keeping all the records permanently may cause server overload... I there a way to keep only 10 records per user and automatically delete older entries? Does mysql have some function for this?
Thanks in advance
You can add a trigger that takes care of removing old entries.
For instance,
DELIMITER //
CREATE definer='root'#'localhost' TRIGGER afterMytableInsert AFTER INSERT ON MyTable
FOR EACH ROW
BEGIN
DELETE FROM MyTable WHERE user_id = NEW.user_id AND id NOT IN
(SELECT id FROM MyTable WHERE user_id = NEW.user_id ORDER BY action_time DESC LIMIT 10);
END//
Just run an hourly cron job that deletes the 11th - n records.
Before insert a record you could check how many the user has first. If they have >=10 delete the oldest one. Then insert the new one.
If your goal is to have the database ensure that for a given table there are never more than N rows per a given subkey (user) then the correct way to solve this will be either:
Use stored procedures to manage inserts in the table.
Use a trigger to delete older rows after an insert.
If you're already using stored procedures for data access, then modifying the insert procedure would make the most sense, otherwise a trigger will be the easiest solution.
Alternately if your goal is to periodically remove old data, then using a cron job to start a stored procedure to prune old data would make the most sense.
When you are inserting a new record for a user. Just do a query like this before (Don't forget the where-condition):
DELETE FROM tablename WHERE userID = 'currentUserId' LIMIT 9, 999999
After that you can insert new data. This keeps the data always to ten records for each user.
INSERT INTO tablename VALUES(....)
DELETE FROM Table WHERE ID NOT IN (SELECT TOP 10 ID FROM Table WHERE USER_ID = 1) AND USER_ID = 1
Clearer Version
DELETE FROM Table
WHERE ID NOT IN
(
SELECT TOP 10 ID FROM Table WHERE USER_ID = 1
)
AND USER_ID = 1

Store database, good pattern for simultaneous access

I am kinda new to database designing so i ask for some advices or some kind of a good pattern.
The situation is that, there is one database, few tables and many users. How should i design the database, or / and which types of queries should i use, to make it work, if users can interact with the database simultaneously? I mean, they have access to and can change the same set of data.
I was thinking about transactions, but I am not sure, if that is the right / good / the only solution.
UPDATE:
By many i mean hundreds, maybe thousands at all. Clients will be connecting to MySQL through WWW page in PHP. They will use operations such: insert, update, delete and select, sometimes join. It's a small database for 5-20 clients and one-two admins. Clients will be updating and selecting info. I am thinking about transactions with storing some info in $_SESSION.
a simple approach that can be very effective is the row versioning.
add a version int field to the main table,
when insert, set it to 0
when update, increment it by one; in the where it should be the version field
EXAMPLE:
CREATE TABLE myTable (
id INT NOT NULL,
name VARCHAR(50) NOT NULL,
vs INT NOT NULL,
)
INSERT INTO myTable VALUES (1, 'Sebastian', 0)
-- first user reads, vs = 0
SELECT * FROM myTable WHERE id = 1
-- second user reads, vs = 0
SELECT * FROM myTable WHERE id = 1
-- first user writes, vs = 1
UPDATE myTable SET name = 'Juan Sebastian', vs = vs + 1 WHERE id = 1 AND vs = 0
(1 row affected)
-- second user writes, no rows affected, because vs is different, show error to the user or do your logic
UPDATE myTable SET name = 'Julian', vs = vs + 1 WHERE id = 1 AND vs = 0
(0 rows affected)
Use InnoDB as a engine type. In opposite to MyISAM it supports row-level blocking, so you wouldn't have to block entire table when someone is updating some record.

Categories