Combined primary keys in MySQL - php

This may sound a bit daft - but I would like to set up the following scenario:
ClientID,AdvertID
1,1
2,1
3,1
2,2
2,3
1,2
Thus, both ClientID and AdvertID must be primary keys, but only AdvertID is auto-incremental. AdvertID must restart numbering relevant to which ClientID it's being linked to. Also, ClientID will be a foreign key as well.
What I have done thus far was just create a combined primary key when I created the table in PHPMyAdmin, but this just continues the auto-incremental value without regard to the actual clientID
Does anybody know how to do this? I know it is possible, I have seen it before. I just have no idea how it was done.
Thanks in advance!
P.S: Just to be clear, this is for MySQL v5.5

LOCK TABLES tbl WRITE;
select AdvertID from tbl where ClientID = :c_id //save the number somewhere
insert into tbl (ClientID, AdvertID) value (:c_id, :saved_ad_id+1)
UNLOCK TABLES
First, gain exclusive write access to the table (just to be sure to avoid race conditions)
then, search the current advert value of your client, if necessary convert the null to a 0, then increase it +1
Third step, save the couple in the database
At last, unlock the table(s).
There is no need of auto incrementing fields, because there are no REAL auto incrementing fields in this table.

As far as I know, you cannot design a structure which will take care of when the value should restart its incrementing depending on its parent key. It depends on your Server-Side logic (in your case PHP) to make relevant cases whenever a new ClientID is recieved, to start populating AdvertID from the beginning.

The only thing I could imagine would be a before insert trigger that does your automatic counting logic for your keys.

Related

How do i set the increment value of autoincrement in mysql

I am trying to develop a system to assign room numbers to tenants of a hostel upon registration, using the auto increment feature of sql.
However, it automatically increases by one after every entry. Because the hostel accommodates four people in one room, I want to change this to 4, so that after every 4 entries I get only one id/room number.
How do I go about this? I am using php and sql. If the autoincrement feature is not possible can you please suggest another way to achieve this? Thanks.
You would need:
http://dev.mysql.com/doc/refman/5.1/en/replication-options-master.html#sysvar_auto_increment_increment
It works like this:
mysql> SET ##auto_increment_increment=4;
So when you insert 4 rows, the auto increment column will be:
4,8,12,16
as best of my knowledge you cannot change the steps of auto-increment field. I suggest add another field and write a trigger to update its value based on auto-increment field (auto-increment/4).
I don't think this is possible with autoincrement..
Maybe you can do something like this:
//Pseudo code
//First you get the count of the highest id, to see how many users are in the last room.
SELECT COUNT(*) FROM table WHERE id=(SELECT id FROM table ORDER BY id DESC LIMIT 1)
//If the result of the last query is >= 4 then insert the next customer with id +1
Don't use auto_increment for this - it can't handle a situation where multiple records will share the same number and although you can reset it manually (see below) it's also not designed for a situation where numbers may get reused in a random order.
You could just have a room_number field with one of the mysql integer types (e.g. tinyint, smallint, mediumint…) or you could separate your database into two tables, one for people (each of whom have an id) and a second to map those ids to rooms.
However you do it, you'd then write a select query to check which room numbers are available before you add the person's details to the database.
You may need to read up on relational databases if that doesn't sound very clear.
If you do need to reset the auto_increment (sometimes it's nice to do it if you've filled a database with test data which you're about to wipe, and you want the real "production" data to begin at 1) you can use:
ALTER TABLE [tablename] AUTO_INCREMENT = 1
https://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

mySQL: index table of 2 other table ID's

Ok, here's what I wanna do.
Table1(globalID [primary key, autoincremented] (is a global id i would use in PHP))
Table2 & table3(ID* [foreign primary key, referencing to globalID], other columns after this, different for each of the tables 2 and 3);
What I wannt to know is if there is a way, and how, to make it so that when I insert a row in table2 or table3 the globalID in talbe1 gets populated and it's value also inserted in table2 or table3 accordingly as an ID. I assume it would be accomplished with a trigger, but triggers are not my forte so if that's the only way it can be done please provide an example.
I was also thinking of expanding it to this scenario:
Table1(globalID [primary key, autoincremented] (is a global id i would use in PHP),
OtherID [an UUID]);
Table2 & table3(ID* [foreign primary key, referencing to OtherID], other columns after this, different for each of the tables 2 and 3);
But in that case there is still the almost nonexistent possibility that 2 users might generate the same UUID while inserting a new row in some table. I was wondering if i could avoid that by making the keys generated by the sql server automatically without me having to code it PHP server side.
If anyone has had this problem worked out and can also point out other things i need to watch out for or take into account please point them out. Also please provide an example of the solution.
EDIT:
#John B.
EDIT BY TosheX
thank you for the reply. I have been reading up on this in the meantime and i already have a solution outside of SQL (in php). Basically I use Yii PHP framework in which I create a model of the table, and create an active record to populate a new row. now when I do that the ID in table1 is generated, and afterwards I still have a variable that points to that populated row and I can just read the generated ID since it's automatically fetched (without having to check for the last record in the database, which may return the record someone created milliseconds after me). Afterwards I just need to create a row inside the appropriate table2 or 3 and assign the already generated value for the ID.
i found the idea here:
http://www.yiiframework.com/forum/index.php/topic/14593-alternate-for-mysql-insert-id/
I really wanted an inbuilt solution in the database, but since I have experience with databases and do know enough about triggers (enough to not like them :D ) i know it's a very tricky code to get right so I was wondering if there are alternatives to auto-populate or something.
Still, I really appreciate your response, since you took your time and all. I have been working in msSQL more too, and I find logic in what you said. UUID in mySQL is the same as GUID in msSQL and I have considered that too as you can see.
Taking all this into account I will go with the solution i found, but I will accept your answer since you did try to help out, and you did bring some good ideas to the table.
Thanks again.
I'm a mssql person, but I dont think it is possible, unless you can create a "BEFORE INSERT" trigger for your db (and even then it will be problematic), because:
Whatever the ID of Tables 2 & 3 is, be it an integer or a guid, these are primary keys: Being a primary key, this column will not allow nulls, yet what you require is essentially to create new rows in these tables, with this column null, and then generate a new row in Table1, and use the new value in that table to update the new row in either Table2 or Table3.
Therefore, you'll probably need to either:
Do a solution in code (ie outside of SQL) whereby you cause the creation of a new row in Table1 first, then use this value when subsequently creating in Table2/3, or:
On tables2/3 have a autoincremented column for your primary key lets call it "PK", then have ID as a separate ordinary FK but which allows nulls, plus a trigger as follows (though you might need to fiddle with the syntax a little bit, because I am a mssql person, and I don't know your db's syntax) :-(
Also this example is designed to work with a multiple row insert, and to achieve this uses a cursor, and my syntax for cursors is dodgy at best even in mssql!
CREATE TRIGGER trg_Table2 ON dbo.Table2
FOR INSERT /* might be AFTER INSERT etc */
AS BEGIN
SET nocount on;
Declare ICursor Scroll For SELECT PK FROM Inserted;
Declare #PK int,
#GlobalID int,
#ID guid;
OPEN ICursor
FETCH Next From ICursor INTO #PK
WHILE ##Fetch_Status BEGIN
INSERT Table1 (OtherID) VALUES (NewID());
SELECT #GlobalID = ##Identity
SELECT #ID = OtherID FROM Table1 WHERE GlobalID = #GlobalID
UPDATE Table2 SET ID = #ID WHERE PK = #PK
FETCH Next FROM ICursor INTO #PK
END
Close ICursor
Deallocate ICursor
END
Hope this helps

how to prevent auto increment from skipping id numbers in a mysql database?

Okay, so let's say I have a mysql database table with two columns, one is for id and the other is for password. If I have three rows of data and the id values go from 1 to 3 and I delete row 3 and then create another row of data, I will see id=4 instead of id=3 on the newly created row. I know this has to do with the auto increment value but I was wondering if I can add some code in a php file that will automatically reset all the id numbers such that you start at id=1 and go up to the last id number in increments of 1 after a row has been deleted?
My goal is to create a form where the user enters a password and the system will match the password with a password value in the database. If there is a match, the row with the matched password will be deleted and the column of id numbers will be reordered such that no id numbers are skipped.
Update: I'm making a rotating banner ad system by setting a random number from 1 to 4 to a variable so that the php file will retrieve a random ad from id=1 to id=4 by using the random number variable. If the random number happens to be 3 and id=3 does not exist, there will be a gap in the row of banner ads. If there is a way to work around big gaps in this situation, please tell me. thanks in advance
Just execute the following SQL query:
ALTER TABLE `tbl_name` AUTO_INCREMENT = 1;
…but it sounds like a terrible idea, so don't do it. Why is the value of your primary key so important? Uniqueness is far more important, and reseting it undermines that.
You can only use
ALTER TABLE 'tbl' AUTO_INCREMENT=#
to reset to a number above the highest value number. If you have 1, 2, 3, and you delete 2, you cannot use this to fill 2. If you delete 3, you could use this to re-use 3 (assuming you haven't put anything higher). That is the best you can do.
ALTER TABLE 'table' AUTO_INCREMENT = 1;
However running this code is not the best idea. There is something wrong with your application if you depend on the column having no gaps. Are you trying to count the number of users? if so use COUNT(id)? Are you trying to deal with other tables? If so use a foreign key.
If you are dead set on doing this the Wrong Way you could try to look for the lowest free number and do the incrementing on your own. Keep in mind the race conditions involves however.
Also, keep in mind that if you change the actual numbers in the database you will need to change all references to it in other tables and in your code.
Well, you can actually just specify the id number you'd like a record to have as part of your insert statement, for example:
INSERT INTO person VALUES(1,'John','Smith','jsmith#devnull.fake','+19995559999');
And if there's not a primary key collision (no record in the database with id=1), then MySQL will happily execute it.
The ALTER TABLE 'tbl' AUTO_INCREMENT=# thing also works, and means you don't have to keep track of the counter.
While you're thinking about this, though, you might want to read some of the discussion on natural vs surrogate keys. The idea of having your id # be specifically important is a bit unusual and might be a sign of a troubled design.
You could do that by:
Inventing a mechanism that provides the next available id when you want to insert (e.g. a transaction involving reading and incrementing an integer column somewhere -- pay special attention to the transaction isolation level!)
Using UPDATE to decrement all ids greater than the one you just deleted (again, with a transaction -- don't forget that foreign keys must be ON UPDATE CASCADE!)
But it begs the question: why do you want this? is it going to be worth the trouble?
It's almost certain that you can achieve whatever your goal is without such witchery.
Update (to address comment):
To select a random number of rows, you can do e.g. in MySQL
SELECT id FROM banners ORDER BY RAND() LIMIT 5
to select 5 random, guaranteed existing banner ids.
A word of caution: there are quite a few people who view ORDER BY RAND() as a bad performance hog. However, it is IMHO not quite right to put every case in the same basket. If the number of rows in the table is manageable (I would consider anything below 10K to be not that many) then ORDER BY RAND() provides a very nice and succint solution. Also, the documentation itself suggests this approach:
However, you can retrieve rows in
random order like this:
mysql> SELECT * FROM tbl_name ORDER BY RAND();
ORDER BY RAND() combined with
LIMIT is useful for selecting a random
sample from a set of rows:
mysql> SELECT * FROM table1, table2 WHERE a=b AND c ORDER BY RAND() LIMIT 1000;
RAND() is not meant to be
a perfect random generator. It is a
fast way to generate random numbers on
demand that is portable between
platforms for the same MySQL version.

Best way to INSERT autoincrement field? (PHP/MySQL)

I have to insert data into two tables, Items and Class_Items. (A third table, Classes is related here, but is not being inserted into).
The primary key of Items is Item_ID, and it's an auto-incrementing integer. Aside from this primary key, there are no unique fields in Items. I need to know what the Item_ID is to match it to Classes in Class_Items.
This is all being done through a PHP interface. I'm wondering what the best way is to insert Items, and then match their Item_ID's into Class_Items. Here are the two main options I see:
INSERT each Item, then use mysql_insert_id() to get its Item_ID for the Class_Items INSERT query. This means one query for every Item (thousands of queries in total).
Get the next Autoincrement ID, then LOCK the Class_Items table so that I can just keep adding to an $item_id variable. This would mean just two queries (one for the Items, one for the Class_Items)
Which way is best and why? Also, if you have an unlisted alternative I'm open to whatever is most efficient.
The most efficient is probably going to be to use parameterized queries. That would require using the mysqli functions, but if you're to the point of needing to optimize this kind of query you should think about being there anyway.
No matter how you cut it, you've got two inserts to make. Doing the first, grabbing the new ID value as you've described (which imposes insignificant overhead, because the value is on hand to mysql already,) and using it in the second insert is pretty minimal.
I would investigate using stored procedures and/or transactions to make sure nothing bad happens.
I'm working on a project with mysql and what I did is the following (without using autoincrement fields):
1- I created a table called SEQUENCE with one field of type BIGINT called VALUE with an initial value of 1. This table will store the id value that will be incremented each time you insert a new record.
2- Create a store procedure and handle the id increment inside it within a transaction.
Here is an example.
CREATE PROCEDURE `SP_registerUser`(
IN _username VARCHAR(40),
IN _password VARCHAR(40),
)
BEGIN
DECLARE seq_user BIGINT;
START TRANSACTION;
#Validate that user does not exist etc..........
#Register the user
SELECT value FROM SEQUENCE INTO seq_user;
UPDATE SECUENCE SET value = value + 1;
INSERT INTO users VALUES(seq_user, _username, SHA1(_password));
INSERT INTO user_info VALUES(seq_user, UTC_TIMESTAMP());
COMMIT;
END //
In my case I want to store the user id in two different tables (users and user_info)

How can we re-use the deleted id from any MySQL-DB table?

How can we re-use the deleted id from any MySQL-DB table?
If I want to rollback the deleted ID , can we do it anyhow?
It may be possible by finding the lowest unused ID and forcing it, but it's terribly bad practice, mainly because of referential integrity: It could be, for example, that relationships from other tables point to a deleted record, which would not be recognizable as "deleted" any more if IDs were reused.
Bottom line: Don't do it. It's a really bad idea.
Related reading: Using auto_increment in the mySQL manual
Re your update: Even if you have a legitimate reason to do this, I don't think there is an automatic way to re-use values in an auto_increment field. If at all, you would have to find the lowest unused value (maybe using a stored procedure or an external script) and force that as the ID (if that's even possible.).
You shouldn't do it.
Don't think of it as a number at all.
It is not a number. It's unique identifier. Think of this word - unique. No record should be identified with the same id.
1.
As per your explanation provided "#Pekka, I am tracking the INsert Update and delete query..." I assume you just some how want to put your old data back to the same ID.
In that case you may consider using a delete-flag column in your table.
If the delete-flag is set for some row, you shall consider program to consider it deleted. Further you may make it available by setting the delete-flat(false).
Similar way is to move whole row to some temporary table and you can bring it back when required with the same data and ID.
Prev. idea is better though.
2.
If this is not what you meant by your explanation; and you want to delete and still use all the values of ID(auto-generated); i have a few ideas you may implement:
- Create a table (IDSTORE) for storing Deleted IDs.
- Create a trigger activated on row delete which will note the ID and store it to the table.
- While inserting take minimum ID from IDSTORE and insert it with that value. If IDSTORE is empty you can pass NULL ID to generate Auto Incremented number.
Of course if you have references / relations (FK) implemented, you manually have to look after it, as your requirement is so.
Further Read:
http://www.databasejournal.com/features/mysql/article.php/10897_2201621_3/Deleting-Duplicate-Rows-in-a-MySQL-Database.htm
Here is the my case for mysql DB:
I had menu table and the menu id was being used in content table as a foreign key. But there was no direct relation between tables (bad table design, i know but the project was done by other developer and later my client approached me to handle it). So, one day my client realised that some of the contents are not showing up. I looked at the problem and found that one of the menu is deleted from menu table, but luckily the menu id exist in cotent table. I found the menu id from content table that was deleted and run the normal insert query for menu table with same menu id along with other fields. (Id is primary key) and it worked.
insert into tbl_menu(id, col1, col2, ...) values(12, val1, val2, ...)

Categories