SQL insert foreign key if not exists - mysql

Suppose I have a table user:
PK id: int
firstname: varchar
lastname: varchar
FK group_id: int
And a table group:
PK id: int
name: varchar
Now I want to be able to send to the server a student JSON object that contains:
{
"firstname": "john",
"lastname": "doe",
"group": "group_name"
}
How to insert it in the tables? How can I ensure that a row with "group_name" exists and if not, create it and then and only then insert the student with the corresponding FK group_id?
What I would do is something like:
select id from group where name="group_name"
if not exists:
insert into group values ("group_name")
insert into user values("john", "doe", the_existing_or_newly_inserted_group_id)
But it seems a bit overkill in terms of number of requests.

In MySQL, you can do this with just two statements:
insert into groups (group_name)
select s.group_name
from (select :group_name as group_name) s
where not exists (select 1 from groups g where g.group_name = s.group_name);
insert into users (firstname, lastname, group_id)
select :firstname, :lastname, g.group_id
from groups g
where g.group_name = :group_name;
The first statement create the new group if it does not yet exists. The second statement recovers the id of the group and inserts the user information.
For this to properly work, group_name must be a unique key in the groups table.
Notes:
You might want to wrap the statements in a single transaction so concurrency is properly managed
Values preceded by : represents the parameters of the queries, that should be passed from the application
Both user and group are language keywords, hence poor choices for object names; I changed the table names to users and groups.

Related

So how i format this correctly

I have the following table
ID
Name
Currencies
Aliases
1
User
["USD","EURO"]
{"User2":["3"]}
I want to write SQL that returns the following result based on currencies and aliases to show people that have an alias of a user and users that have same currencies
NAME
Currencies
Aliases
User
1
NULL
User2
NULL
1
My initial SQL is the following
SELECT NAME
FROM table
WHERE JSON_CONTAINS(Currencies,'"EURO"',"$")
OR JSON_CONTAINS(Aliases,'"3"',"$");
The problem with the code above that I can't differentiate if those users share the same aliases or the same currencies, it doesn't really need to be the same format of the table above anyway to diff is ok
btw i am using MySQL(10.5.10-MariaDB-1:10.5.10+maria~bionic)
samples:
CREATE TABLE IF NOT EXISTS table(
ID INT,
NAME VARCHAR(255),
Currencies LONGTEXT,
Aliases LONGTEXT,
PRIMARY KEY(ID)
);
insert data:
REPLACE INTO table(ID, NAME, Currencies, Aliases) VALUES (:id, :name, :currencies, :aliases);
If you want flags, just move the expressions to the SELECT:
SELECT NAME, JSON_CONTAINS(Currencies, '"EURO"', '$'),
JSON_CONTAINS(Aliases, '"3"', '$')
FROM table
WHERE JSON_CONTAINS(Currencies, '"EURO"', '$') OR
JSON_CONTAINS(Aliases, '"3"', '$');

MySQL : add data from another database, starting on specific row, user_id is primary, need to insert incrementing numbers starting from 20

I have an user table with 19 rows (first row is admin). I need to add more, so I have another database with a table having more than 1.400.000 users.
My table has an "user_id" as primary key, INT(11), no auto-increment. I need to add users starting in row 20, and only "first_name", "last_name" and "email".
My first try:
INSERT INTO mydatabase.users (first_name, last_name, email) SELECT first_name, last_name, email FROM anotherdatabase.users
That gets me a "Field 'user_id' doesn't have a default value".
I understand it is because user_id is primary key and cannot be null. So, again, it is int(11), non auto-increment.
So I want to add 20,21,22,23 and so on, along the other data. I searched a lot for about 5 hours and can´t seem to find anything I can understand.
Thank you in advance.
To get your numbers starting from 20:
INSERT INTO mydatabase.users (user_id, first_name, last_name, email)
SELECT 19+row_number() over (),
first_name,
last_name,
email
FROM anotherdatabase.users
19+row_number() over ()
'row_number()' = Number of current row within its partition (Mysql documentation)
'over ()' - defines a partition without condition; so in fact counts for the entire table
Details for both can be found under MySQL Window Function concepts and syntax
19+ - ...
Alternatively - if the MySQL version < 8.0:
INSERT INTO mydatabase.users (user_id, first_name, last_name, email)
SELECT (select 20+count(*)
from anotherdatabase.users u1 where u1.id < u.id) as id,
first_name,
last_name,
email
FROM anotherdatabase.users u
This does rather assume that you have a user_id on the other table as well.
The inner select just counts how many records are in the original database that have a smaller id than the record that is being returned at that point (and adds 20 to have the numbering start at 20).
And a third option (not depending on the existence of user_id):
INSERT INTO mydatabase.users (user_id, first_name, last_name, email)
SELECT ( #row_num:=#row_num+1 AS user_id,
first_name,
last_name,
email
FROM anotherdatabase.users u,
(SELECT #row_num:=19) var;
I'm not a great fan of this as it involves creation of variables to keep track of the row number.
Write a procedure that use variable. for i < 1400020 , you would need to count the number of rows in anotherdatabase.users then add the figure with 20.
DELIMITER $$
CREATE PROCEDURE insert_test_data()
BEGIN
DECLARE i INT DEFAULT 20;
WHILE i < 1400020 DO
INSERT INTO mydatabase.users (user_id, first_name, last_name, email)
SELECT i,first_name, last_name, email FROM anotherdatabase.users;
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
CALL insert_test_data();
DROP PROCEDURE insert_test_data;
I found a simpler way, using variables. I still can´t believe it. It worked like a charm
SET #i=19;
INSERT INTO mydatabase.users(user_id, first_name, last_name, email)
SELECT #i:=#i+1,
first_name,
last_name,
email
FROM anotherdatabase.users;

Unique index on 2 columns in mysql

I have one table in mysql named 'UserFriends' where I am updating my websites user's friends details.
here is the schema of the table (UserFriends)
id int,
Userid int,
friendid int,
createdate timespan
now, I want to create unique index on userid & friendid. that i have created unique index well. so, right now i am not able to insert same userid and friendid as duplicate. but if i am inserting same value in opposite field it accept without generating error.
example :
insert into userfriends ( userid, friendid )
select 1, 2 --- insert perfect
insert into userfriends ( userid, friendid )
select 1, 2 --- show error because unique index comes in a picture
now i am inserting
insert into userfriends ( userid, friendid )
select 2, 1 --- records insert here (i don't want this)
How do i prevent this?
First things first: for your inserts, don't use 'select' to generate the values, as it's liable to become confusing. Instead do it this way:
INSERT INTO userfriends (userid, friendid) VALUES (1, 2)
That said, the only way to have a unique qualifier that works backwards too is to do it programmatically. If you want to prevent x,y from being accepted, run SELECT * FROM userfriends WHERE userid = y AND friendid = x and if you get a result, reject the insert before it happens. If you don't, then insert into the table as normal, and your unique key will deny it from there on.

Inserting Information into Parent/Child Tables via a Stored Procedure?

Hello I'm working on a database assignment and I'm stuck on how to do this one stored procedure. Although It works, sort of...
DELIMITER //
CREATE PROCEDURE AddANewCustomer(IN firstName char(20), IN lastName char(20), IN companyName char(45), IN streetAddress char(60), IN city char(30), IN province char(45), IN postalCode char(6), IN phoneNumber int(10))
BEGIN
DECLARE PersonID INT;
SELECT idPerson FROM Persons WHERE Persons.firstName = firstName AND Persons.lastName = lastName INTO PersonID;
IF PersonID IS NULL THEN
INSERT INTO Persons(firstName, lastName, streetAddress, city, province, postalCode, phoneNumber) VALUES (firstName, lastName, streetAddress, City, Province, postalCode, phoneNumber);
SELECT idPerson FROM Persons WHERE firstName = firstName AND lastName = lastName INTO PersonID;
END IF;
INSERT INTO Customers(idCustomer, companyName) VALUES (Last_Insert_ID(), companyName);
END //
DELIMITER ;
Basically I'm working with Super/Sub types. I want to take the information from the user and then update my parent table (Persons) and pass on the remaining information to my child table (Customers). idPerson is the auto-incrementing PK for Persons table, and I want to use that as a PK/FK for the Customers table's id, idCustomer.
If I run the procedure once, it'll spit out an error 'Result consist of more than one row' and only the Parent table gets updated... But if I run it again, it'll update the Child table properly. Which makes me think that the Last_Insert_ID() parameter is null the first time around and the idPerson only gets updated after the procedure is done.
I've researched for a fix all night and now I'm absolutely stumped on how to solve this.
Ouch.
Basically I'm working with Super/Sub
types.
I don't think so, but I could be wrong. Customer usually describes a relationship between two parties, one a buyer and the other a seller.
If I run the procedure once, it'll
spit out an error 'Result consist of
more than one row'
What do you think that means? Does this query return any rows?
SELECT lastname, firstname, count(*)
FROM Persons
GROUP BY lastname, firstname
HAVING count(*) > 1;
You check for a NULL id number,
IF PersonID IS NULL THEN
but you ignore the possibility that your SELECT statement might return 2 or 3 or 42 different id numbers, all for people who have the same first and last name. Is that wise? Phrased another way, do you have a UNIQUE constraint on {firstname, lastname}?
If PersonID is null, you insert a row into Persons, which sets a value that LAST_INSERT_ID() can return. But your second INSERT tries to use LAST_INSERT_ID() without regard to whether a row was previously inserted into Persons.
Finally, you have two slightly different versions of
SELECT idPerson
FROM Persons
WHERE Persons.firstName = firstName
AND Persons.lastName = lastName
INTO PersonID;
I'm pretty sure you need one at most.

MySQL Conditional Insert. Works in phpMyAdmin but not PHP Script

I am trying to create a conditional INSERT into my MySQL databate from a PHP script. The following SQL syntax works in phpMyAdmin, but not in my PHP Script:
INSERT INTO profiles (id, firstname)
SELECT "22","John" from profiles
WHERE NOT EXISTS (
SELECT * FROM li_profiles
WHERE li_p_firstname = "John"
)
(Note that "id" is the primary key, "firstname" is not a key or unique)
Something weird that might be part of the issue is that when I run that SQL in phpMyAdmin, while it does "work" (meaning that a new record is added with the id "22" and the firstname "John") I get the following warning: "#1062 - Duplicate entry '22' for key 1"
But the table didn't have a previous entry with id of 22. ??!!
What's going on?
Change SELECT to VALUES
INSERT INTO profiles (id, firstname) VALUES("22","John") FROM profiles WHERE NOT EXISTS ( SELECT * FROM li_profiles WHERE li_p_firstname = "John" )
Also, if you are using auto-increment values, you should specify the next value. Also, if its an integer, give an integer (22) not a string ("22")
You'll get a duplicate entry for the iD because you are inserting a new row for each row in the profiles table; for every row in the profiles table there is no John in the li_profiles table. You might try
INSERT INTO profiles (id, firstname)
SELECT "22","John" from profiles
WHERE NOT EXISTS (SELECT * FROM li_profiles
WHERE li_p_firstname = "John")
LIMIT 1;
which would eliminate the duplicate problem (if it works, sorry but I haven't checked this myself).
I figured it out in a different way. (I'm told that the HAVING statement is slow, so I'm not sure that it's the best way... but it the only method I've gotten to work.)
INSERT INTO profiles (id,firstname)
SELECT 22,'John'
FROM li_profiles
WHERE firstname = 'John'
HAVING COUNT(*) = 0;