I'm working on a project to add statistics for pageviews. I'm having trouble looking up and then storing in MySQL from PHP.
I have users stored in the database as:
ID Email_Address Visits
1 email#address.com NULL
2 email#address.com NULL
A variable is passed into the page called $ID which refers to which user has been visited.
Firstly, should the Visits column be a default of 0 rather than NULL?
Secondly, What query can I then use to go something like;
Lookup $ID, add 1 to Visits in that row. ie: Visits = Visits + 1?
Also if $ID is blank, it shouldn't do anything.
Cheers
Yes, it should be default of 0.
UPDATE `your-table`
SET `Visits` = `Visits` + 1
WHERE `ID` = :id
If the id is blank, don't run the query.
Your views column should probably default to zero.
Secondly, this is the query you should use:
update tablename set visits=visits+1 where id=:id
Replace tablename with the name of your table and bind the value of :id to the ID. (You are using prepared statements, right?) Also, if the ID is empty, just don't issue the query.
1) It really depends what you are trying to do. But, from what it looks like, I would default to 0 since it looks like a numeric field.
2) I don't want to write code for you, but I'll give you some links:
First you need to Select. Then add use the built in mysql PHP functions to add to views, then Update. Or you can do it all in one query. But, for beginners, I always advocate doing it in steps.
It also looks like ID is your primary key, which you shouldn't leave blank. Read about primary keys here.
Related
In my database (MySQL) I have a table (MyISAM) containing a field called number. Each value of this field is either 0 or a positive number. The non zero values must be unique. And the last thing is that the value of the field is being generated in my php code according to value of another field (called isNew) in this table. The code folows.
$maxNumber = $db->selectField('select max(number)+1 m from confirmed where isNew = ?', array($isNew), 'm');
$db->query('update confirmed set number = ? where dataid = ?', array($maxNumber, $id));
The first line of code select the maximum value of the number field and increments it. The second line updates the record by setting it freshly generated number.
This code is being used concurrently by hundreds of clients so I noticed that sometimes duplicates of the number field occur. As I understand this is happening when two clients read value of the number field almost simultaneously and this fact leads to the duplicate.
I have read about the SELECT ... FOR UPDATE statement but I'm not quite sure it is applicable in my case.
So the question is should I just append FOR UPDATE to my SELECT statement? Or create a stored procedure to do the job? Or maybe completely change the way the numbers are being generated?
This is definitely possible to do. MyISAM doesn't offer transaction locking so forget about stuff like FOR UPDATE. There's definitely room for a race condition between the two statements in your example code. The way you've implemented it, this one is like the talking burro. It's amazing it works at all, not that it works badly! :-)
I don't understand what you're doing with this SQL:
select max(number)+1 m from confirmed where isNew = ?
Are the values of number unique throughout the table, or only within sets where isNew has a certain value? Would it work if the values of number were unique throughout the table? That would be easier to create, debug, and maintain.
You need a multi-connection-safe way of getting a number.
You could try this SQL. It will do the setting of the max number in one statement.
UPDATE confirmed
SET number = (SELECT 1+ MAX(number) FROM confirmed WHERE isNew = ?)
WHERE dataid = ?
This will perform badly. Without a compound index on (isNew, number), and without both those columns declared NOT NULL it will perform very very badly.
If you can use numbers that are unique throughout the table I suggest you create for yourself a sequence setup, which will return a unique number each time you use it. You need to use a series of consecutive SQL statements to do that. Here's how it goes.
First, when you create your tables create yourself a table to use called sequence (or whatever name you like). This is a one-column table.
CREATE TABLE sequence (
sequence_id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`sequence_id`)
) AUTO_INCREMENT = 990000
This will make the sequence table start issuing numbers at 990,000.
Second, when you need a unique number in your application, do the following things.
INSERT INTO sequence () VALUES ();
DELETE FROM sequence WHERE sequence_id < LAST_INSERT_ID();
UPDATE confirmed
SET number = LAST_INSERT_ID()
WHERE dataid = ?
What's going on here? The MySQL function LAST_INSERT_ID() returns the value of the most recent autoincrement-generated ID number. Because you inserted a row into that sequence table, it gives you back that generated ID number. The DELETE FROM command keeps that table from snarfing up disk space; we don't care about old ID numbers.
LAST_INSERT_ID() is connection-safe. If software on different connections to your database uses it, they all get their own values.
If you need to know the last inserted ID number, you can issue this SQL:
SELECT LAST_INSERT_ID() AS sequence_id
and you'll get it returned.
If you were using Oracle or PostgreSQL, instead of MySQL, you'd find they provide SEQUENCE objects that basically do this.
Here's the answer to another similar question.
Fastest way to generate 11,000,000 unique ids
Can this be done in a single query? The table has an auto increment field which I need to know the number to fill url field in the table.
Table
id(AI) | title | url
What I am expecting is something like
INSERT INTO table (title,url) VALUES ('name','CONCATENATION OF title AND ID');
I am currently doing this using 2 queries.
1.Writing the fields except URL.
Getting the id using mysqli_insert_id()
2.Updating the above written row.
P.S : The Table has other fields as well so changing the db design isnt really possible in this case.
It can't be done atomically. In theory, you could SELECT MAX(id) + 1 FROM yourtable, but please, please don't - although this is not guaranteed to give you the right result and is definitely not a safe approach.
This seems like bad practice, anyway. Why not concatenate the title and ID when you fetch it? Why must it be concatenated on insert?
I will not comment on the design of your database -- you are the judge of that. Just bear in mind that the following command gets the next auto-increment-ID for the specified table, and that this number could change in an instant if another user accesses the table before your code can use it.
I am using this code myself in a project for a similar reason to your own, and it works for me because the table is updated only a few times per day and never by more than one person at a time.
SELECT Auto_increment FROM information_schema.tables WHERE
table_name = '$name_of_your_table';
To be clear, this code gets the auto-increment ID that will be given to the next table entry for the specified table.
I'm uploading image file to storage server. Before uploading I should compose filename, which contains AUTOINCREMENT VALUE in it (for example, 12345_filename.jpg).
How could I get autoincrement value before inserting into DB?
I see only one solution
insert empty row
get it's autoincrement value
delete this row
insert row with real data using autoincrement value from p.1
Is there any other solutions?
Thank you
The autoincrement value is generated by the database itself, when the insertion is done ; which means you cannot get it before doing the actual insert query.
The solution you proposed is not the one that's often used -- which would be :
insert some half-empty data
get the autoincrement value that's been generated
do your calculations, using that autoincrement value
update the row to put the new / full data in place -- using the autoincrement generated earlier in the where clause of the update query, to identify which row is being updated.
Of course, as a security precaution, all these operations have to be made in a transaction (to ensure a "all or nothing" behavior)
As pseudo-code :
begin transaction
insert into your table (half empty values);
$id = get last autoincrement id
do calculations
update set data = full data where id = $id
commit transaction
well, try this:
$query = "SHOW TABLE STATUS LIKE 'tablename'";
$result = mysql_query($query);
$row = mysql_fetch_assoc($result);
var_dump($row);
output:
array(18) {
[...]
["Auto_increment"]=> string(4) "3847"
[...]
}
This will be your next auto_increment ID.
If you are using PDO, here is a "single line" solution :
$nextId = $db->query("SHOW TABLE STATUS LIKE 'tablename'")->fetch(PDO::FETCH_ASSOC)['Auto_increment'];
where tablename is replaced by your table.
There is no solution. You get the auto-increment value when you insert a new row, full stop. Inserting and deleting won't help, since the next auto-increment value will be one higher. Do to possibly multiple clients talking to the database at the same time, you can't predict the next value since it might be incremented between your guessing and your actual insert.
Find a different solution. Either insert a row and update it later, or generate an id for the filename that's independent of the auto-increment id.
#Pascal Martin makes a good point. In cases like this, I personally like to add another ID column, containing a random, 16-digit ID (which will also be the "public" ID in web apps and on web sites for security reasons). This random ID you can set beforehand in your application, work with it, and then set when the record is actually created.
Use a separate table just as a counter, like a postgresql sequence, and don't use auto_increment in your main table(s).
INSERT INTO CONTACTS1 (firstname, lastname) values('hi', select auto_increment from information_schema.TABLES where TABLE_NAME='CONTACTS1' and TABLE_SCHEMA='test')
Where ID is the Primary Key & Auto number column.
Well this is to old ,but if someone else need it.
You can get this kind of value at "information_schema" table where you could do something like
select AUTO_INCREMENT from TABLES where TABLE_SCHEMA = 'You're Database' and TABLE_NAME = 'Table Name'
So this kind of Meta Data are always stored in Information_schema .
I had a hard time with the title, so let me explain.
What I'm doing is using the jQuery UI to create sortable list elements on a page. Once the order is submitted, php assigns an incrementing value to the list elements based on their order, drops the existing id column, creates a new id column and inserts each list elements value WHERE title=x. This creates the proper order of ID's, and is working fine.
What I'd like to do now is change the column to auto_increment, such that if I insert a new entry, the id is assigned automatically, one number higher than the greatest number generated by the php script. I'm not using any foreign keys or anything, just this simple table.
Is this possible?
My mistake, I misread your question. You do not want to use the database itself to provide numbering based on your sort order. You can however use the SQL query itself to return an incrementing field. One sec and I'll update with that info...
Ok, here it is:
you need to use a variable like
set #n=0;SELECT
#n:=#n+1 as 'n',
col1,
col2
from table
However, i highly recommend you just create the numbering in your php code if at all possible.
----------------Original Post----------------
This is pretty easy with phpmyadmin. Let me know if your unable to install that and I'll dig up the necessary SQL.
All heck, here is the SQL:
alter table t1 modify f1 int(4) auto_increment
alter TABLE tbl auto_increment = xxx; //change xxx to be the next id it should use
you may need to run these in opposite order depending on your existing data set it will fail to add auto_increment if you don't change the value of auto_increment to be something not already in use.
I have a table named user_ips to keep track of users in case they delete their cookies or change browser. So anyway, the following code is simple. It updates entries in user_ips that are equal to the user's id and IP. If the query did not update any rows, then it means that IP for that user is not in the table, so it inserts it.
$site->query('UPDATE `user_ips` SET `last_time` = UNIX_TIMESTAMP(), `user_agent` = \''.$this->user_agent.'\' WHERE `ip` = '.$this->ip.' AND `userid` = '.$this->id);
if(mysql_affected_rows() == 0)
{
$site->query('INSERT INTO `user_ips` SET `userid` = '.$this->id.', `ip` = '.$this->ip.', `first_time` = UNIX_TIMESTAMP(), `last_time` = UNIX_TIMESTAMP(), `user_agent` = \''.$this->user_agent.'\'');
}
The problem is mysql_affected_rows() sometimes returns 0 even if a row with the user's current ID and IP exists. So then the code adds another row to the table with the same IP.
In case you are wondering, $site is mysql class I made for my website and the only query it executes is the one passed to it by query(), and nothing more, so this is not a problem with the class. Oh and the IP is stored as a long IP, so it does not need quotes around it.
I'm directly quoting the PHP documentation here:
When using UPDATE, MySQL will not update columns where the new value is the same as the old value. This creates the possibility that mysql_affected_rows() may not actually equal the number of rows matched, only the number of rows that were literally affected by the query.
So in your case, mysql_affected_rows() will return 0 when UNIX_TIMESTAMP() returns the same value (for example, two requests from the same client in the same second).
To build on slipbull's answer, the simplest way to handle this is perhaps to simply perform a SELECT query to evaluate whether or not an INSERT is necessary. Another solution would be to simply INSERT a record at user creation, as this would guarantee a valid record.
You could just set your primary key to span both userid and ip. This would ensure you get no duplicate entries, but you would have to suppress the error on the insert query if you are not going to add a select to check a record exists.
Another option might be to use the MySQL REPLACE command. http://dev.mysql.com/doc/refman/5.0/en/replace.html. Which will delete the row if it already exists, and then insert the row.
Although that might not be suitable for your precise needs.