I'd like to know whether it is possible / advisable (why?), to select data from a table and update the same entry in the same pass
Example
SELECT message_content, message_times_read FROM messages WHERE message_id = 1
Then
UPDATE messages SET message_times_read = message_times_read + 1 WHERE message_id = 1
Can I do them in one call? More importantly, if this is possible, should I? (i.e. is this considered proper practice, is this compatible across different versions, is there performance benefit?)
You must to create a Trigger if you want to do multiples queries in a same query. Check this link if you want to lear more about it: https://www.sitepoint.com/how-to-create-mysql-triggers/
Related
I've come across some odd defensive code. Basically it does a query like this:
select * from A join B on (A.b_id=B.id)
Then it iterates through the result set and whenever it meets a new row from table B, it caches it (by id). Afterwards only the cached copy is used, even for subsequent rows.
It looks like it was trying to safeguard against a result set like this:
A.id | A.value | B.id | B.value
------+---------+------+---------
1 | First | 1 | Yay
2 | Second | 1 | Nay
But is this even possible? Even if the row in table B is updated while the select query is fetched half way, will it really be visible? Can the update even proceed while someone is querying the table?
For what it's worth, I think the table at the time was MyISAM, although it's been since converted to InnoDB. Also, the code which is running the query is written in PHP. As far as I can tell, it uses the default transaction isolation level and fetch mode.
OK, it seems I need some clarifications. Here's a code, similar to what I've found:
$sql = "select A.id a_id, A.value a_value, B.id b_id, B.value b_value from A join B on (A.b_id=B.id)";
$res = mysql_query($sql);
$cacheB = array();
$A = new classA();
$B = new classB();
while ($row = mysql_fetch_assoc($res)) {
$A->setData($row);
if ( !isset($cacheB[$row['b_id']]) ) {
$cacheB[$row['b_id']] = $row;
}
$B->setData($cacheB[$row['b_id']]);
// Do some processing depending on $A and $B
}
This code is a CLI application running from a cron job. The data from $A and $B isn't returned to anything, but depending on the contents, some external services may be called and some other DB tables may be modified. The contents of classA, classB and the processing are not relevant to this question.
My question is - is there a point for this "safeguard", or is it a deadweight that can be deleted? Let's assume that the processing part would actually be sensitive to a change in the values of B (although in reality I doubt it, but still).
Can a field change during the execution of a MySQL query and both values be present in the result set?
No.
In MyISAM the entire table is locked by each query, so it's not possible at all, by design (see table locking).
In InnoDB queries are isolated and a select is a consistent read as mentioned in the doc Locks Set by Different SQL Statements in InnoDB. A consistent read is defined as "A read operation that uses snapshot information to present query results based on a point in time, regardless of changes performed by other transactions running at the same time."
Even if the row in table B is updated while the select query is fetched half way, will it really be visible?
Yes, even then, it's impossible.
Can the update even proceed while someone is querying the table?
In MyISAM no, it'll have to wait, as explain in the doc: "Table locking enables many sessions to read from a table at the same time, but if a session wants to write to a table, it must first get exclusive access, meaning it might have to wait for other sessions to finish with the table first. During the update, all other sessions that want to access this particular table must wait until the update is done."
In InnoDB yes, but the queries are isolated and work on different "snapshot" of the database as explained, so it doesn't matter. Transactions are particularly useful in this case if you have any doubt by the way.
The code you are showing might or might not have another purpose, this I can't say. But if the only purpose it to prevent something that cannot happen to happen, then it's completely redundant and can be safely removed.
Just in case:
Right now in your while loop you have a row
{ a_id1, a_value1, b_id1, b_value1 }
And you set $B and save in the cache the whole row not just the values from B.
so next row in the loop will have a different a_id, but same b_id
{ a_id2, a_value2, b_id1, b_value1 }
But in this case you will set $B using the cached version of $row so you will have a_id1 instead of a_id2
My guess is $B->setData() only care about fields related to B so using the cache version doesn't make any difference, but if that isn't the case you are cloning the A values from the first row on the following rows with same b_id1.
I wrote a small script which uses the concept of long polling.
It works as follows:
jQuery sends the request with some parameters (say lastId) to php
PHP gets the latest id from database and compares with the lastId.
If the lastId is smaller than the newly fetched Id, then it kills the
script and echoes the new records.
From jQuery, i display this output.
I have taken care of all security checks. The problem is when a record is deleted or updated, there is no way to know this.
The nearest solution i can get is to count the number of rows and match it with some saved row count variable. But then, if i have 1000 records, i have to echo out all the 1000 records which can be a big performance issue.
The CRUD functionality of this application is completely separated and runs in a different server. So i dont get to know which record was deleted.
I don't need any help coding wise, but i am looking for some suggestion to make this work while updating and deleting.
Please note, websockets(my fav) and node.js is not an option for me.
Instead of using a certain ID from your table, you could also check when the table itself was modified the last time.
SQL:
SELECT UPDATE_TIME
FROM information_schema.tables
WHERE TABLE_SCHEMA = 'yourdb'
AND TABLE_NAME = 'yourtable';
If successful, the statement should return something like
UPDATE_TIME
2014-04-02 11:12:15
Then use the resulting timestamp instead of the lastid. I am using a very similar technique to display and auto-refresh logs, works like a charm.
You have to adjust the statement to your needs, and replace yourdb and yourtable with the values needed for your application. It also requires you to have access to information_schema.tables, so check if this is available, too.
Two alternative solutions:
If the solution described above is too imprecise for your purpose (it might lead to issues when the table is changed multiple times per second), you might combine that timestamp with your current mechanism with lastid to cover new inserts.
Another way would be to implement a table, in which the current state is logged. This is where your ajax requests check the current state. Then generade triggers in your data tables, which update this table.
You can get the highest ID by
SELECT id FROM table ORDER BY id DESC LIMIT 1
but this is not reliable in my opinion, because you can have ID's of 1, 2, 3, 7 and you insert a new row having the ID 5.
Keep in mind: the highest ID, is not necessarily the most recent row.
The current auto increment value can be obtained by
SELECT AUTO_INCREMENT FROM information_schema.tables
WHERE TABLE_SCHEMA = 'yourdb'
AND TABLE_NAME = 'yourtable';
Maybe a timestamp + microtime is an option for you?
Imagine it like this. There is a field in my database called flags in which are added or removed data like this:
UPDATE people SET flags=flags|16 WHERE ....
UPDATE people SET flags=flags|128 WHERE ....
UPDATE people SET flags=flags&~16 WHERE ....
UPDATE people SET flags=flags&~128 WHERE ....
For instance this field can have value like 65536 or more or less. My question is - How to get specific flag from this field using PHP code? I mean something like this:
SELECT * FROM people WHERE flags=16;
But the result will return all people with not just number 16 in field but it will return people with flag 65536, people with 16 but not people with 2 or 1. Which SELECT query should I use here in my php code or maybe some specific PHP integrated functions? Thank you.
Assuming flags is a bitfield and you want to select rows where bit #4 (10000) is set
SELECT * FROM people WHERE flags & 16;
This is not ideal though as you're losing out on all that referential goodness that DBs are good for.
What you should have is two new tables; flags and people_flags. The former contains all the flags (id and name columns should be sufficient). The latter contains flag_id and people_id columns, creating a many-to-many relationship (see junction table).
The logic is not correct though. You cant have just one flag that contains 16 as its not unique.
Lets say you have DB with this rows
name:Jhon tel:012345 email:test#test.com flags:16
name:Mike tel:012344 email:mike#test.com flags:16
name:Sarah tel:012346 email:sarah#test.com flags:2442
to select flags 16 from this table with using Select statement you will get two rows of data you can either use Distinct or get flags to be unique
I have a table in MySQL that I'm accessing from PHP. For example, let's have a table named THINGS:
things.ID - int primary key
things.name - varchar
things.owner_ID - int for joining with another table
My select statement to get what I need might look like:
SELECT * FROM things WHERE owner_ID = 99;
Pretty straightforward. Now, I'd like users to be able to specify a completely arbitrary order for the items returned from this query. The list will be displayed, they can then click an "up" or "down" button next to a row and have it moved up or down the list, or possibly a drag-and-drop operation to move it to anywhere else. I'd like this order to be saved in the database (same or other table). The custom order would be unique for the set of rows for each owner_ID.
I've searched for ways to provide this ordering without luck. I've thought of a few ways to implement this, but help me fill in the final option:
Add an INT column and set it's value to whatever I need to get rows
returned in my order. This presents the problem of scanning
row-by-row to find the insertion point, and possibly needing to
update the preceding/following rows sort column.
Having a "next" and "previous" column, implementing a linked list.
Once I find my place, I'll just have to update max 2 rows to insert
the row. But this requires scanning for the location from row #1.
Some SQL/relational DB trick I'm unaware of...
I'm looking for an answer to #3 because it may be out there, who knows. Plus, I'd like to offload as much as I can on the database.
From what I've read you need a new table containing the ordering of each user, say it's called *user_orderings*.
This table should contain the user ID, the position of the thing and the ID of the thing. The (user_id, thing_id) should be the PK. This way you need to update this table every time but you can get the things for a user in the order he/she wants using ORDER BY on the user_orderings table and joining it with the things table. It should work.
The simplest expression of an ordered list is: 3,1,2,4. We can store this as a string in the parent table; so if our table is photos with the foreign key profile_id, we'd place our photo order in profiles.photo_order. We can then consider this field in our order by clause by utilizing the find_in_set() function. This requires either two queries or a join. I use two queries but the join is more interesting, so here it is:
select photos.photo_id, photos.caption
from photos
join profiles on profiles.profile_id = photos.profile_id
where photos.profile_id = 1
order by find_in_set(photos.photo_id, profiles.photo_order);
Note that you would probably not want to use find_in_set() in a where clause due to performance implications, but in an order by clause, there are few enough results to make this fast.
I'm trying to find a way to check if some IDs are already in the DB, if an ID is already in the DB I'd naturally try to avoid processing the row it represents
Right now I'm doing a single query to check for the ID, but I think this is too expensive in time because if I'm checking 20 id's the script is taking up to 30 seconds
I know i can do a simple WHERE id=1 OR id=2 OR id=3 , but I'd like to know of a certain group of IDs which ones are already in the database and which ones are not
I don't know much about transactions but maybe this could be useful or something
any thoughts are highly appreciated!
Depends how you determine the "Group of IDs"
If you can do it with a query, you can likely use a join or exists clause.
for example
SELECT firstname
from people p
where not exists (select 1 from otherpeople op where op.firstname = p.firstname)
This will select all the people who are not in the otherpeople table
If you just have a list of IDs, then use WHERE NOT IN (1,3,4...)
30 seconds for 20 queries on a single value is a long time. Did you create an index on the ID field to speed things up?
Also if you create a unique key on the ID field you can just insert all ID's. The database will throw errors and not insert those those ID's that already exist, but you can ignore those errors.