Generate unique key for users with same email-address - mysql

We have a table with orders of customers like:
CREATE TABLE `orders` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`number` varchar(20) NOT NULL,
`ordered` datetime DEFAULT NULL,
`email` varchar(255) NOT NULL,
...
);
The table is already filled with data. I need to add a field:
`user` int(10) unsigned NOT NULL,
which contains a unique number for each customer. A customer is defined by the same email-address, so all orders with the email 'test#example.com' should get a 1, with 'something_else#example.com' should get a 2 and so on.
For this 'user'-number it doesn't matter if it starts with 1 or is somehow incrementing, it just should be different for every email-address.
Is there a way to do this in one SQL-statement? I know how to do it with some php-code for example, but we where curious if it's possible just with SQL. We know it would be a better design if there was a table "customer", but it's not our design, we just trying to fix the worst things ;)

It's not possible to do what you describe in one SQL statement.
Even if you didn't care to make the user id unique per email, your ALTER TABLE wouldn't work. You show adding a column that is NOT NULL but has no DEFAULT. So what value is it supposed to add to the table, given that the table has rows in it? It can't use NULL, and it has no DEFAULT value. You can't add the column as an AUTO_INCREMENT because you already have an id column that is AUTO_INCREMENT, and MySQL doesn't allow a table to have two such columns.
Here's the way I'd do it:
CREATE TABLE customers (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL
);
INSERT INTO customers (email)
SELECT DISTINCT email FROM orders;
ALTER TABLE orders ADD COLUMN user_id INT UNSIGNED; -- this allows NULLs until we fill it
UPDATE orders JOIN customers USING (email)
SET orders.user_id = customers.id;
Before the next step, make sure that it has populated orders.user_id the way you think is correct. Once you drop the orders.email column, there's no undo. It would be a good idea to make a backup first.
ALTER TABLE orders DROP COLUMN email,
MODIFY COLUMN user_ID INT UNSIGNED NOT NULL; -- disallow NULLs from now on

Related

How to use more than 1 auto-increment column in MySQL

I want to create a table name Users where I should have have columns User, cookieID, sessionID, Geo and then I want to first three columns to have some random unique value assigned automatically. I tried to make all three columns AUTO_INCREMENT with User column PRIMARY and 'cookieIDandsessionIDcolumnUNIQUE`. The SQL code is:
CREATE TABLE `users` ( `User` VARCHAR(20) NOT NULL AUTO_INCREMENT ,
`cookieID` INT(20) NULL DEFAULT NULL AUTO_INCREMENT ,
`sessionID` INT(20) NULL DEFAULT NULL AUTO_INCREMENT ,
`Geo` VARCHAR(30) NULL DEFAULT NULL ,
PRIMARY KEY (`User`), UNIQUE (`cookieID`), UNIQUE (`sessionID`), UNIQUE (`Geo`));
But, it did not work because only one column can be declared as AUTO_INCREMENT which must be PRIMARY.
What is the another approach to do this?
Since the auto-increment cannot be applied to multiple to rows and there no option for sequence in MySQL. You can use triggers for the unique update of the row with datetime.
Change to table creation to be of single auto-increment row.
CREATE TABLE `users` ( `User` VARCHAR(20) NOT NULL,
`cookieID` INT(20) NULL DEFAULT NULL,
`sessionID` INT(20) NULL DEFAULT NULL AUTO_INCREMENT ,
`Geo` VARCHAR(30) NULL DEFAULT NULL,
PRIMARY KEY (`User`), UNIQUE (`cookieID`), UNIQUE (`sessionID`), UNIQUE (`Geo`));
Create a trigger on the same table as below. You can set the unique values under the SET for as many column as you want.
CREATE DEFINER=`root`#`localhost` TRIGGER `users_BEFORE_INSERT` BEFORE INSERT ON `users` FOR EACH ROW BEGIN
SET
NEW.cookieID = (SELECT curdate()+curtime());
END
Now when you insert into the table as below.
insert into `users`(`User`) values("test");
You table looks like this.
User cookieID sessionID Geo
test 20315169 0 NULL
If the value which are auto incrementing, you wanna keep both values the same. Then copy the value of one column to another during insertion time of new value.

Update table's column with values from another table's column

I have a table with random names (along with an id as primary key):
CREATE TABLE `people` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
);
I have inserted in it 100 random names along with their ids. I also have another table with other names:
CREATE TABLE `names` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
);
This table also has 100 (different) random names along with their ids. I want to update the column name of table people with the names from the column name of table names.
I obviously have to use UPDATE and SET but in most ways I saw that people are also using INNER JOIN. Personally, I am wondering if there is simpler way to do this (without using INNER JOIN) and I am missing it?
the update with inner join as eg:
update people
inner join names on people.id = names.id
set people.name = names.name
Is the simplest and also the more clear, compact and expressive.
others methods need normally subselect or implicit join based on where condition. In one case there query is more verbouse and in the second the query is more confused and often less performant.

modeling issue with field with 4 values (1 or more)

lets say I have an account object in my application, which currently represented as:
CREATE TABLE Account (
accountId int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
PRIMARY KEY (accountId)
);
Now, Account object need to also have Solution field...and Status have 4 different possible values:
Solution1, Solution2, Solution3, Solution4
What would be the right way to represent it in the database?
Account can have few statuses, and status can have few accounts...
So at first I thought create in the db table of Solutions and than have another table to hold the relationship, but its seems too complicated for a field that have only 4 possible values...
Create a junction table to represent the relationships between accounts and solutions:
CREATE TABLE account_solution (
accountId int NOT NULL,
solutionId int NOT NULL
PRIMARY KEY (accountId, solutionId)
)
For your solution table, since there are only 4 values, you might be able to take advantage of MySQL's enum type, e.g.
CREATE TABLE solution
solutionId int NOT NULL PRIMARY KEY,
status ENUM('Solution1', 'Solution2', 'Solution3', 'Solution4')
);
You can use set Mysql SET type
CREATE TABLE Account (
accountId int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
status set('Solution1','Solution2','Solution3','Solution4') NOT NULL,
PRIMARY KEY (accountId)
);
And if you want to select a specific status
SELECT *
FROM `Account`
WHERE FIND_IN_SET( 'Solution2', `status` ) >0

MySQL table linking 1:n

So first up I'm not sure if this is a double post or not because I don't know how the exact approach or feature is called and if it even exist.
I know that MySQL has a feature called joins
My plan is to link two MySQL tables in relation 1:n one is t_user the other one t_event.
t_user:
CREATE TABLE t_user (
uId INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(25) NOT NULL,
...
)
t_event:
CREATE TABLE t_event (
eId INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(40) NOT NULL,
date DATETIME NOT NULL,
members ???,
...
)
I want the users to "subscribe" to the events and get stored in the members column as a list (?). This would be no problem if only one user would subscribe to one event. But I have no idea how to setup the t_event table to store more than one user and how to query for all the events a user has "subscribed" for.
This is usually done via third table:
CREATE TABLE t_eventsusers (
eId INT(6),
uId INT(6)
)

correct way to place data in table OneToONe

I am confused about the correct/most efficient way to place data in my dababase table when there is a OneToOne relationship.
For example, I have a users table.
I now wish for each user to be able to state his current country location.
i then want to be able to search the datatable for users by current location.
The way that I have done this is to create 3 separate tables. i.e
table one - users : just contains the user information:
CREATE TABLE users(
id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
firstName VARCHAR(30) NOT NULL,
lastName VARCHAR(40) NOT NULL,
);
Table two country list: a list of countries and respective Ids for each country
PHP Code:
CREATE TABLE countrylist(
country_id MEDIUMINT UNSIGNED NOT NULL,
country VARCHAR(60) NOT NULL,
INDEX country_id ( country_id, country ),
INDEX countrylist (country, country_id ),
UNIQUE KEY (country)
);
Table 3; contains the userId and the countryId he lives in:
PHP Code:
CREATE TABLE user_countrylocation(
country_id VARCHAR(60) NOT NULL,
id MEDIUMINT UNSIGNED NOT NULL,
INDEX country_id (country_id, id ),
INDEX user_id (id, country_id )
);
Alternatively, should I place the countryId in the users table and completely get rid of the user_countrylocation. i.e in each user column, I will place a country_id for the country he lives in.
The problem is that I have over 20 similar tables as above that give details on users; i.e languages spoken, age-group, nationality etc.
My concerns is that if I place this unique information in each users column in the user table, then what would be the most efficient way to search the database: that is why I opted for the style above.
So, I really request for some advice on the most efficient/correct way to plan the database.
If you are going to have a huge data then you should keep the same approach and use the following method to keep the one to one constraint satisfied
if you don't have a huge data then you should keep the look up tables like country and use the reference for user in a column. but then you may need to allow them nulls that is make such optional information columns nullable.
The most efficient and exactly correct way is to first delete the data from the third table "user_countrylocation" for the user to be updated. Then insert the new location for the user. don't forget to use transaction.
your table 3 should have
country_id MEDIUMINT UNSIGNED NOT NULL,
instead of
country_id VARCHAR(60) NOT NULL,
and also change tyhe column name from id to user_id in all tables.
if you are using a stored procedure it would be like
create procedure sp_UpdateUserCurrentCountry (
#userID MEDIUMINT UNSIGNED,
#CountryID MEDIUMINT UNSIGNED)
begin
as
delete from user_countrylocation
where user_id = #userID
insert into user_countrylocation
(
country_id,
user_id
)
values
(
#CountryID,
#userID
)
END
One to One relations are usually mapped via Foreign Keys linking the two tables together. A third mapping table is only required for Many to Many relationships. So, you should ideally have a Foreign Key Country_ID in your Users table.
Your SELECT query would then look like
SELECT * FROM Users
WHERE Country_ID = (
SELECT Country_ID FROM Countries
WHERE Country_Name = 'USA'
);