AUTO_INCREMENT for two tables - mysql

I have two tables in my database for users:
users
id|username|password|registration_date|
1 |bruce |****** |2017-03-04 |
2 |jason |***** |2017-03-06 |
3 |brad |******* |2017-03-12 |
google_users
id|username|password|registration_date|
1 |jimmy |***** |2017-03-05 |
2 |wade |******* |2017-03-08 |
I want to apply the same AUTO_INCREMENT index for both tables when a new user signs up with google.
Something like this:
users
id|username|password|registration_date|
1 |bruce |****** |2017-03-04 |
3 |jason |***** |2017-03-06 |
5 |brad |******* |2017-03-12 |
google_users
id|username|password|registration_date|
2 |jimmy |***** |2017-03-05 |
4 |wade |******* |2017-03-08 |
How can I do this?

I'm going to vote against this table design and recommend that that you just maintain a single users table:
users (id, username, password, registration_date)
To keep track of the method by which they signed up, you may create a second table:
accounts (id, user_id, type_id)
The type_id can point to yet a third table, indicating whether Google or something else were the source of the signup. Note also that the accounts table can have a user with more than one signup relationship, if you would need that.
The basic idea is that maintaining an auto increment column across two tables will either be impossible, or at the very least ugly. This is not a feature which is usually supported/needed in SQL. So if you find yourself having this need, you should first look closely at your database design.

Not suggested, but if you really want it to happen this way:
You can try to implement this setting in MySQL:
mysql> SHOW VARIABLES LIKE 'auto_inc%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 2 |
and then for your tables, you can do:
ALTER TABLE users AUTO_INCREMENT = 1;
ALTER TABLE google_users AUTO_INCREMENT = 2;
So, now, your auto-increment will be incremented by 2 and it gives you the expected result.
But as I said, this will impact your whole DB. All your increments will be done by 2 instead of 1.

Related

Keep certain rows as constant in a MySQL table

I have a situation where I have a table, for example:
| id | type |
------------------
| 0 | Complete |
| 1 | Zone |
Now, I always want my database to be populated with these values, but additionally users should be able to CRUD their own custom types beyond these. For example, a user might decide they want a "Partial Zone" type:
| id | type |
---------------------
| 0 | Complete |
| 1 | Zone |
| 2 | Partial Zone |
This is all fine. But I don't want anyone to be able to delete/modify the first and second rows.
This seems like it should be so simple, but is there a common strategy for handling this case that ensures that these rows go unaffected? Should I put a lock column on the table and only lock these two values when I initially populate the database on application setup? Is there something much more obvious and elegant that I am missing?
Unless I'm missing something, you should be able to just add a third column to your table for the user ID/owner of the record. For the Complete and Zone records, the owner could be e.g. user 0, which would correspond to an admin. In your deletion logic, just check the ID column and do not allow admin records to be deleted by anyone from the application.
If this won't work, you could also consider having two tables, one for system records which cannot be deleted, and another one for user created records. You would have to possibly always take a union of the two tables when you query.

How to merge rows with duplicate uniqe key on update in MySQL

I have following table structure:
+------------------+ +---------------------+
| Users | | Data |
+------------------+ +---------------------+
| id | uname_UK | | id |user_id_FK |data|
+-----|------------| +---------------------+
| 1 | foobar | | 1 | 1 | aa |
| 2 | bazqui +<-------+ 2 | 3 | bb |
| 3 | foobaz | | 3 | 2 | cc |
+------------------+ | 4 | 2 | dd |
+---------------------+
The problem now is, that during storing data in database there was typo. The user named foobaz should be named foobar. The uname column has a Unique constraint.
My question is how to easily fix this problem? When I update the username table, I get error - duplicate uniqe key, as expected. In the end I would like to have the foreign keys updated too.
My idea was do some trigger magic, but I was hoping there would be some more elegant solution. Another constraint here is, that the update is initiated through frontend, so I cannot use PHP.
Alternate way would be to drop the Unique constraint and make some cron job, to periodically update the database and remove the duplicate entries.
Thanks.
Why not just delete the record? Update all data to the user you want to keep and delete the obsolete user.
In Oracle you can do this using the merge into statement. I don't know if that is possible to do in one statement in MySQL, but you might as well execute a separate delete for it. You can make it trigger magic, but I doubt if it's a good decision to always autmagically merge the users. The new username might be a typo too.
So in a normal application, if this would happen so often, I would make a 'merge users' functionality that lets you do just this.
What you should do, is figure out what it means to your data that two users are actualy one. In this case, since there are two records in Data for user ID 2, it seems as if it's okay for users to have several records in Data and you can just
UPDATE data
SET user_id_FK = 1
WHERE user_id_FK = 3;
DELETE FROM users
WHERE id = 3;
In general, you need to figure this out at an application level.
What if there's a foo counter for each user? You should probably add the value from the user you'll be deleting to the value you're keeping.
What if a user has an address?
What if a user can only have one e-mail address and your duplicate user has a different one? Which do you keep?
This is not an easy question with a general answer.

Constrain database to hold one value or the other never both

Is it possible to add a database constraint to limit a row to have a single value in one of two columns, never more and never less? Let me illustrate:
Sales Order Table
---------------------------------
id | person_id | company_id |
Rows for this would look like:
id | person_id | company_id |
---|-----------|------------|
1 | 1 | null |
2 | 2 | null |
3 | null | 1 |
4 | null | 2 |
In this illustration, the source of the sales order is either a person or a company. It is one or the other, no more or less. My question is: is there a way to constrain the database so that 1) both fields can't be null and 2) both fields can't be not-null? i.e., one has to be null and one has to be not-null...
I know the initial reaction from some may be to combine the two tables (person, company) into one customer table. But, the example I'm giving is just a very simple example. In my application the two fields I'm working with cannot be combined into one.
The DBMS I'm working with is MySQL.
I hope the question makes sense. Thank you in advance for your help!
This may come as a shock...
mysql doesn't support CHECKconstraints. It allows you to define them, but it totally ignores them.
They are allowed in the syntax only to provide compatibility with other database's syntax.
You could use a trigger on update/insert, and use SIGNAL to raise an exception.

temp and main tables - mysql

I have a scenario where I need to insert the data into table temporarily and later on approval or confirmation, make it permanent. The data will be inserted by a user and approval or denial needs to be done by Super User.
What I think of now is to have two different but identical tables (temporary and main) and the user will insert the data into temp table. After confirmation of Super User, the data will be moved to main table. But the problem comes when a database contains very large number of tables then this process will become more complex.
EDIT : This implies to CREATE EDIT & DELETE commands.
Is there any simpler or better approach of doing this?
Please suggest.
Using a version table (related to comment):
The idea here is to have a version table; when your user changes a piece of information the new version is stored in this table along with the related ID.
Then all you need to do is join on the PersonID and select the most recent accepted version.
This means the user can make as many updates as they want but they won't show until the super user accepts them, it also means the data is never destroyed (stored in the version table) and they don't need to implement rollback as it's already there!
See: http://sqlfiddle.com/#!3/cc77f/4
People Table:
ID | Age Etc... (Info That Doesn't Change)
-----------------------
1 | 12
2 | 16
3 | 11
People Version Table:
VersionID | PersonID | Name | Approved
-----------------------
1 | 1 | Stevz | FALSE
2 | 1 | Steve | TRUE
3 | 2 | James | TRUE
4 | 3 | Jghn | FALSE
5 | 3 | John | TRUE
Example table SQL
CREATE TABLE People
(
id int identity primary key,
age int
);
CREATE TABLE PeopleVersion
(
versionId int identity primary key,
peopleId int,
name varchar(30),
approved varchar(30)
);
Example Query
SELECT * FROM People p
INNER JOIN PeopleVersion v ON p.id = v.peopleID
WHERE v.approved = 'TRUE'
ORDER BY versionId DESC
A further insight:
You could even have three states of Approved; null meaning no admin has chosen yet, TRUE meaning it was accepted and FALSE meaning it was rejected
You could show the user the most recent from null and true, show the admin all three and show the other users of the site only versions that were true
Old Comments
Could you just add a field called approved to the table and then hide anything without the approval flag set to TRUE?
It could default to FALSE and only the super user would be able to see items with the flag set to FALSE
E.g.
Name | Age | Approved
-----------------------
Steve | 12 | FALSE
James | 16 | TRUE
John | 11 | FALSE
The user would only see James, but the SuperUser would see all three listed
Alternatively using your temporary and main tables is the other way of looking at this problem, though this may lead to problems as everything get's larger
The easiest approach is a flag within the table marking an entry either approved or not-yet approved.
Then just change the retrieving logic to only show entries where that flag is set to approved.

Create another table just to store a few options?

I'm creating a database with various tables. Let's take the user table, for example. It has fields such as marital status and system role. Each of those fields has predefined options. Does it make sense to create two new tables for each of those fields, so then when a user is added to the system, choices can be made available for selection e.g. single, married, divorced? It seems a bit of an overkill in terms of one extra query. Is this the best way to do it or do I have other options?
I would definitely create separate tables to store the available options for these various columns. This is a good thing to do as far as normalization goes, and will also save you headaches down the road when you need to add, remove, disable or change any of the options. Also, if don't create a separate table and populate the values directly in the user table, you may end up having to do something like select distinct RelationshipStatus from User to get the available options, which is not as performant as just selecting 10 or however many values from a separate table.
As someone commented, over-normalization can sometimes be a pain, but I've found that not normalizing something as a way to do a quick work-around almost always comes back to haunt you.
User
----
ID
RelationshipStatusId
...other columns
RelationshipStatus
------------------
ID
Value
Description
You can use the ENUM datatype in MySQL to better take care of this scenario. Storing such options in a seperate table is a bad idea until you have a lot of them..
mysql> DESC Classes;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| dept | char(4) | NO | | NULL | |
| level | enum('Upper','Lower') | NO | | NULL | |
+-------+-----------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM Classes;
+----+------+-------+
| id | dept | level |
+----+------+-------+
| 10 | MATH | |
+----+------+-------+
1 row in set (0.00 sec)
mysql> INSERT INTO Classes VALUES (11, 'ENG', 'Upper')
-> ;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM Classes;
+----+------+-------+
| id | dept | level |
+----+------+-------+
| 10 | MATH | |
| 11 | ENG | Upper |
+----+------+-------+
2 rows in set (0.00 sec)
For design's sake, create another table (what you don't want to do) with a proper PK. This will have the extra benefit of saving space, because imagine having 10000 registers with the word "married" on them.
Also, an alternative is using in your application a "dictionary", storing in a structure and Id and the value, like this:
Id Marital Status
1 Married
2 Single
.. ......
The same table, but not in a database but in the application, hardcoded, serialized or in an external file.
It depends on the size of the rows also. It would be better option to split the tables in to multiple in terms of speed.
For ex. you can keep the frequent used columns in user table and all other informations/optional ones in separate tables. In this case you need take care while displaying the data also.
I guess, there is no need for over-normalization as it will trouble you in writing queries. You need to take care of too many joins.
If your predefined conditions for Marital Status are: Married, Single and Divorced, I would just store a single character like: M, S and D and would provide these options in a DropDown with fixed values.
I think Marital Status has no further possibilities unless you think of something like:
Want to be Divorced
Married but living alone.
For user role also, I would do something like that:
A - Administrator
P - Power User
R - Restricted User
G - Guest
In case you need something more elaborate, I won't create further tables.