PHP/MySQL INSERT feature logic error - php

I recently asked a question about writing to multiple tables: PHP/MySQL insert into multiple data tables on submit
I have now tried out this code and there are no errors produced in the actual code but the results I am getting are strange. When a user clicks register this 'insert.php' page is called and the code can be found below.
<?php
$username = $_POST["username"];
$password = $_POST["password"];
$institution = $_POST["institution"];
$conn = pg_connect("database connection information"); //in reality this has been filled
$result = pg_query($conn, "INSERT INTO institutions (i_id, name) VALUES (null, '$institution') RETURNING i_id");
$insert_row = pg_fetch_row($result);
$insti_id = $insert_row[0];
// INSTITUTION SAVED AND HAS ITS OWN ID BUT NO MEMBER OF STAFF ID
$resultTwo = pg_query($conn, "INSERT INTO staff VALUES (NULL, '$username', '$password', '$insti_id'");
$insert_rowTwo = pg_fetch_row($resultTwo);
$user_id = $insert_rowTwo[0];
// USER SAVED WITH OWN ID AND COMPANY ID
// ASSIGN AN INSTITUTION TO A STAFF MEMBER IF THE STAFF'S $company_id MATCHES THAT OF THE
// INSTITUION IN QUESTION
$update = pg_query($conn, "UPDATE institutions SET u_id = '$user_id' WHERE i_id = '$insti_id'");
pg_close($conn);
?>
What the result of this is just the browser waiting for a server response but there it just constantly waits. Almost like an infinite loop I'm assuming. There are no current errors produced so I think it may be down to a logic error. Any ideas?

The errors:
RETURNING clause is missing in the second INSERT statement.
Provide an explicit list of columns for your second INSERT statement, too.
Don't supply NULL in the INSERT statements if you want the column default (serial columns?) to kick in. Use the keyword DEFAULT or just don't mention the column at all.
The better solution:
Use data-moidifying CTE, available since PostgreSQL 9.1 to do it all in one statement and save a overhead and round trips to the server. (MySQL knows nothing of the sort, not even plain CTEs).
Also, skip the UPDATE by re-modelling the logic. Retrieve one id with nextval(), and make do with just two INSERT statements.
Assuming this data model (you should have supplied that in your question):
CREATE TABLE institutions(i_id serial, name text, u_id int);
CREATE TABLE staff(user_id serial, username text, password text, i_id int);
This one query does it all:
WITH x AS (
INSERT INTO staff(username, password, i_id) -- provide column list
VALUES ('$username', '$password', nextval('institutions_i_id_seq'))
RETURNING user_id, i_id
)
INSERT INTO institutions (i_id, u_id, name)
SELECT x.i_id, x.user_id, '$institution'
FROM x
RETURNING u_id, i_id; -- if you need the values back, else you are done
Data model
You might think about changing your data model to a classical n:m relationship.
Would include these tables and primary keys:
staff (u_id serial PRIMARY KEY, ...)
institution (i_id serial PRIMARY KEY, ...)
institution_staff (i_id, u_id, ..., PRIMARY KEY(i_id, u_id)) -- implements n:m
You can always define institution_staff.i_id UNIQUE, if a user can only belong to one institution.

Related

Insert values from 1 form into 2 sql server tables (PHP) (SQL-Server)

I want to use one form to insert into two different Microsoft sql tables. I tryed to use 2 inserts, but didnt work.
if (isset($_GET['submit'])) {
$sth = $connection->prepare("INSERT INTO DB.dbo.Fehler (QualiID, TestaufstellungID, ModulinfoID, failAfter, Datum, Verbleib, DUTNr) VALUES ($QualiID, $TestaufstellungID,$ModulinfoID,'$failAfter','$Datum','$Verbleib','$DUTNr')");
echo "INSERT INTO DB.dbo.Fehler (QualiID, TestaufstellungID, ModulinfoID, failAfter, Datum, Verbleib, DUTNr) VALUES ($QualiID, $TestaufstellungID,$ModulinfoID,'$failAfter',$Datum,'$Verbleib','$DUTNr')";
$sth->execute();
if($sth)
{
echo "";
}
else
{
echo sqlsrv_errors();
}
$MID = $connection->prepare("MAX(MID) as MID FROM DB.dbo.Fehler WHERE DB.dbo.Fehler.TestaufstellungID = '". $TestaufstellungID . "'");
$MID->execute();
$sth2 = $connection->prepare("INSERT INTO DB.dbo.Fehlerinfo (MID, Tester, Test, Ausfallbedingungen, Fehlerbeschreibung, Ersteller) VALUES ($MID, '$Tester','$Test','$Ausfallbedingungen','$Fehlerbeschreibung','$Ersteller')");
$sth2->execute();
To understand MID is the Primary key of table Fehler and ist the foreign key in the second table Fehlerinfo
Thats why i have the select work around to get the last MID and want to save it in a variable $MID to insert it into the second table.
Is there a smarter solution possible?
As I mentioned in the comments, generally the better way is to do the insert in one batch. This is very over simplified, however, should put you in the right direction. Normally you would likely be passing the values for the Foreign Table in a Table Value Parameter (due to the Many to One relationship) and would encapsulate the entire thing in a TRY...CATCH and possibly a stored procedure.
I can't write this in PHP, as my knowledge of it is rudimentary, but this should get you on the right path to understanding:
USE Sandbox;
--Couple of sample tables
CREATE TABLE dbo.PrimaryTable (SomeID int IDENTITY(1,1),
SomeString varchar(10),
CONSTRAINT PK_PTID PRIMARY KEY NONCLUSTERED (SomeID));
CREATE TABLE dbo.ForeignTable (AnotherID int IDENTITY(1,1),
ForeignID int,
AnotherString varchar(10),
CONSTRAINT PK_FTID PRIMARY KEY NONCLUSTERED(AnotherID),
CONSTRAINT FK_FTPT FOREIGN KEY (ForeignID)
REFERENCES dbo.PrimaryTable(SomeID));
GO
--single batch example
--Declare input parameters and give some values
--These would be the values coming from your application
DECLARE #SomeString varchar(10) = 'abc',
#AnotherString varchar(10) = 'def';
--Create a temp table or variable for the output of the ID
DECLARE #ID table (ID int);
--Insert the data and get the ID at the same time:
INSERT INTO dbo.PrimaryTable (SomeString)
OUTPUT inserted.SomeID
INTO #ID
SELECT #SomeString;
--#ID now has the inserted ID(s)
--Use it to insert into the other table
INSERT INTO dbo.ForeignTable (ForeignID,AnotherString)
SELECT ID,
#AnotherString
FROM #ID;
GO
--Check the data:
SELECT *
FROM dbo.PrimaryTable PT
JOIN dbo.ForeignTable FT ON PT.SomeID = FT.ForeignID;
GO
--Clean up
DROP TABLE dbo.ForeignTable;
DROP TABLE dbo.PrimaryTable;
As i mentioned the answer how it works for me fine atm.
if (isset($_GET['submit'])) {
$failInsert = ("INSERT INTO DB.dbo.Fehler (QualiID, TestaufstellungID, ModulinfoID, failAfter, Datum, Verbleib, DUTNr) VALUES ($QualiID, $TestaufstellungID,$ModulinfoID,'$failAfter','$Datum','$Verbleib','$DUTNr')");
$failInsert .= ("INSERT INTO DB.dbo.Fehlerinfo (MID, Tester, Test, Ausfallbedingungen, Fehlerbeschreibung, Ersteller) VALUES (NULL, '$Tester','$Test','$Ausfallbedingungen','$Fehlerbeschreibung','$Ersteller')");
$failInsert .= ("UPDATE DB.dbo.Fehlerinfo SET DB.dbo.Fehlerinfo.MID = i.MID FROM (SELECT MAX(MID)as MID FROM DB.dbo.Fehler) i WHERE DB.dbo.Fehlerinfo.TestID = ( SELECT MAX(TestID) as TestID FROM DB.dbo.Fehlerinfo)");
$sth = $connection->prepare($failInsert);
$sth->execute();
}

Mysqli insert into multiple table with auto increment

hello i tried using mysqli to fetch autoincrement id from the first table to insert it for second table, this is my database design for each table
Table users
id_user PK auto-inc
username
password
nama
role
status
table siswa
id_siswa PK auto-inc
id_tingkatan
nama_siswa
jk_siswa
hp_siswa
nama_ortu
jk_ortu
hp_ortu
id_user
this is what i have tried
$query = "INSERT INTO users
(username, password, nama, role, status)
VALUES ('$name','$password','$nama','$role','$status')";
$koneksi->query($query);
$query = "INSERT INTO siswa
(id_tingkatan, nama_siswa, jk_siswa, hp_siswa,
nama_ortu, jk_ortu, hp_ortu, id_user)
VALUES ('$tingkatan','$nama','$jksiswa','$hpsiswa',
'$namaortu','$hportu', $koneksi->insert_id)";
$koneksi->query($query);
the data is inserted into users table but not to siswa table, any suggestion to fix this?
edit : overlooked variable only ($jkortu) fixed now, thanks
In the latter INSERT, the number of items in the first commalist does not correspond with the number of items in the second commalist. This should throw a syntax error.
You are missing a parameter.
You should always test for mysql errors, otherwise it will fail silently, and you have no idea what is going on:
if (!$server->query($query)) {
throw new Exception($server->error());
}

MySQL one-to-may relationship

After looking around on stackoverflow, I'm still having a little trouble understanding the one-to-many relationship in mysql. I have a request coming in from the user (form submission) which will be stored in one table. This is a dynamic form that lets the user add extra fields therefore those will be stored in a separate table. So in short, in my db design, there will be one table for the users with PRIMARY KEY AUTO INCREMENT and there will be another table for the hostnames PER user (multiple fields -array) and using a foreign key that references to the primary key in the user table. Sorry if this is long but trying to make this a good question.
Example:
User Table: (ONE)
1. John Doe, blah, 11-12-15
2. Sally Po, blah, 11-14-15
3. John Doe, blah, 11-15-15
(these are three separate requests)
(numbers are primary key auto incr.)
Host Name Table: (MANY)
1. www.johndoe.com
1. www.johndoe2.com
1. www.johndoe3.com
2. www.sallypo.com
2. www.sallypo2.com
(these numbers (foreign key) should match the primary key for each request)
Code (Leaving out the actual queries + pretty sure I shouln't be using last_id):
$sql = "CREATE TABLE IF NOT EXISTS userTable (
id int AUTO_INCREMENT,
firstName VARCHAR(30) NOT NULL,
date DATE NOT NULL,
PRIMARY KEY (id)
)";
//query
$sql = "CREATE TABLE IF NOT EXISTS hostNamesTable (
id int NOT NULL,
hostName VARCHAR(90) NOT NULL,
FOREIGN KEY (id) REFERENCES userTable(id)
)";
//query
$sql = "INSERT INTO userTable (firstName, date)
VALUES ('$firstName', '$date')";
//query
$last_id = mysqli_insert_id();
for($i = 0; $i < sizeof($hostName); $i++){
$sql = "INSERT INTO hostNamesTable (id, hostName)
VALUES ('$last_id', '$hostName[$i]')";
//query
}
What am I doing wrong? (is this the right way to go about it?)
note: I was trying to get the last_id of the user Table so that I can use it in the hostName table as the foreign key
EDIT: I'm using MySQLi with php
EDIT 2:
After the changes, this is the error I am getting now: Cannot add or update a child row: a foreign key constraint fails (d9832482827984hb28397429.hostNamesTable, CONSTRAINT hostNamesTable_ibfk_1 FOREIGN KEY (id) REFERENCES userTable (id))Error: INSERT INTO hostNamesTable (id, hostName, ) VALUES ('', 'secondhost.net')
--Looks like the $last_id isn't even being recorded?
EDIT 3: Started working. Not sure what it was but I think it was because of some type.
why dont you just add an extra column in the hostNames table which is called "ref_user" and contains the ID of the user you are reffering to? So you can use unique IDs in both tables.
Make a query like:
SELECT * FROM hostNames WHERE ref_user = (SELECT id FROM userTable WHERE <uniqueColumn> = <uniqueIdentifierOfUser>);
But the included request must return only one line from users.
try adding mysqli $link as a parameter in your mysqli_insert_id
$last_id = mysqli_insert_id($link);
i presume you have this somewhere in your code
$link = mysqli_connect("localhost", "mysql_user", "mysql_password", "mysql_db");
if this doesn't work, try using mysql LAST_INSERT_ID() function
$last_id = $mysqli->query("SELECT LAST_INSERT_ID() AS last_id")->fetch_object()->last_id;

INSERT INTO two different tables, but have the same ID?

I have a database of Users and another table for Teachers. Teachers have all the properties as a user but also an e-mail address. When inserting into the DB how can I insert the info, ensuring that the ID is the same for both?
the ID currently is on automatic incrament.
this is what I have at the moment:
$sqlQuery="INSERT INTO user(firstName,lastName,DOB,title,password,classRoomID)
VALUES('$myFirstName','$myLastName','$myDOB','$myTitle','$newPassword','$myClassRoom')";
$result=mysql_query($sqlQuery);
$sqlQuery = "INSERT INTO teacher(email) VALUES ('$myEmail')";
$result=mysql_query($sqlQuery);
thank you!
use MYSQL function LAST_INSERT_ID()
OR php mysql http://ro1.php.net/manual/en/function.mysql-insert-id.php
why to use separate table for teachers. instead, you can have email field with in user table and additional field with flag (T ( for teacher) and U (for user). Default can be a U. This have following Pros.
Will Not increase table size as email would be varchar
Remove extra overhead of maintaining two tables.
Same Id can be used
If you want to have that as separate table then answer you selected is good one but make sure last insert id is called in same connection call.
After the first insert, fetch the last inserted id:
$last_id = mysqli_insert_id(); // or mysql_insert_id() if you're using old code
Or you could expand your second query and use mysql's integrated LAST_INSERT_ID() function:
$sqlQuery = "INSERT INTO teacher(id, email) VALUES ((SELECT LAST_INSERT_ID()), '$myEmail')";
Try this:
$sqlQuery="INSERT INTO user(firstName,lastName,DOB,title,password,classRoomID)
VALUES('$myFirstName','$myLastName','$myDOB','$myTitle','$newPassword','$myClassRoom')";
$result=mysql_query($sqlQuery);
$id = mysql_insert_id();
$sqlQuery = "INSERT INTO teacher(id, email) VALUES (' $id ','$myEmail')";
$result=mysql_query($sqlQuery);
Insert data into two tables & using the same ID
First method
$sqlQuery1 = "INSERT INTO user(firstName,lastName,DOB,title,password,classRoomID) VALUES('$myFirstName','$myLastName','$myDOB','$myTitle','$newPassword','$myClassRoom')";
$result1 = mysqli_query($conn, $sqlQuery1);
$lastID = mysqli_insert_id($conn);
$sqlQuery2 = "INSERT INTO teacher(email, lastID) VALUES ('$myEmail', 'lastID')";
$result2 = mysqli_query($conn, $sqlQuery2);
If the first method not work then this is the second method for you
$sqlQuery1 = "INSERT INTO user(firstName,lastName,DOB,title,password,classRoomID) VALUES('$myFirstName','$myLastName','$myDOB','$myTitle','$newPassword','$myClassRoom')";
$result1 = mysqli_query($sqlQuery1);
$sqlQuery2 = "INSERT INTO teacher(email) VALUES ('$myEmail')";
$result2 = mysqli_query($sqlQuery2);
You can set the Foreign Key in your database table (phpMyAdmin/ MySQL Workbench) to let the Foreign Key follow the Primary Key (ID). Then the data after insert will auto-follow the Primary Key ID.
Example here,
Teachers table set ID - Primary Key
Users table set UserID - Foreign Key (will follow the Teachers table ID)
if you're using MySQL WorkBench, you can refer to this link to set a foreign key.
https://dev.mysql.com/doc/workbench/en/wb-table-editor-foreign-keys-tab.html
Hope I can help any of you.
Though you can use the LAST_INSERT_ID() function in order to get the last insert id, the best approach in this case is to create a column reference to user id table.
teacher
id | user_id | email
So the teacher.id could be anyting, but the user_id column is the real reference to user table.
If you use InnoDB table, you can make the database consistent using Foreign keys
You Should Use A Transaction In MySQL. First insert In One Table And GET LAST_INSERT_ID().
Insert LAST_INSERT_ID() In Second Table.
$sqlQuery="INSERT INTO user(firstName,lastName,DOB,title,password,classRoomID)
VALUES('$myFirstName','$myLastName','$myDOB','$myTitle','$newPassword','$myClassRoom')";
$sqlQuery = "INSERT INTO teacher(LAST_INSERT_ID(), email) VALUES (' $id ','$myEmail')";
$result=mysql_query($sqlQuery);

Writing to a data table with just foreign keys

I have a PostGreSQL database with the following data model:
CREATE TABLE staff (s_id serial UNIQUE, name, username, password, email)
CREATE TABLE institution (i_id serial UNIQUE, name);
CREATE TABLE isStaffOf (i_id, s_id); //foreign key references
When a user submits the form I have a PHP script which writes the information to first two data tables and that automatically generates the s_id and i_id values. Great!
I've tried out a few PHP modifications to get the system writing both the s_id and i_id into the isStaffOf relation so it can enforce the explicit 1..1 relationship required for my project but on submit it says I have an insufficient data type. See below for the PHP code.
$name = $_POST["name"];
$username = $_POST["username"];
$password = $_POST["password"];
$email = $_POST["email"];
$institution = $_POST["institution"];
$conn = pg_connect("host=***** port=**** dbname=****** user=**** password=******");
$staffWrite = pg_query("INSERT INTO staff(name, username, password, email) VALUES ('$name', '$username', '$password', '$email')");
$instiWrite = pg_query("INSERT INTO institution(name) VALUES ('$institution')");
$instiFK=pg_query("SELECT i_id FROM institution WHERE name='$institution'");
$staffFK=pg_query("SELECT s_id FROM staff WHERE name='$username'");
$sql=("INSERT INTO isstaffof(i_id, s_id) VALUES ('$instiFK', '$staffFK')");
$result = pg_query($sql);
That is the script I have at the moment but its not working. Any ideas on how to fix this so that when a user submits all the data tables will be filled and the referential integrity enforced? I'm almost there and still trying things out but to no avail.
The error message:
ERROR: invalid input syntax for integer: "Resource id #4" LINE 1: INSERT INTO isstaffof(i_id, s_id) VALUES ('Resource id #4', ...
Please let me know if you need more explanation but I'm sure its clear what I want to achieve and I'm convinced I can do it without so many pg_query calls.
As of PostgreSQL version 9.1, you can use a single query to INSERT in all 3 tables, a writable common table expression:
WITH
step_1 AS(
INSERT INTO staff (name) VALUES('Frank') RETURNING s_id
),
step_2 AS (
INSERT INTO institution (name) VALUES('PostgreSQL') RETURNING i_id
)
INSERT INTO isStaffOf (i_id, s_id) SELECT i_id,s_id FROM step_1, step_2;
Simple, fast and reliable.
You could do some other tricks as well, when some records already exists and just want to select the primary key value.
WHEN creating the isstaffof table, you at least have to specify the datatypes, and you can specify they are foreign keys.
CREATE TABLE isstaffof
(i_id BIGINT REFERENCES institution(i_id)
, s_id BIGINT REFERENCES staff(s_id)
);
NOTE: avoid using MixedCase for identifiers. It might cause terrible problems, if you ever have to port to a different DBMS platform.
UPDATE: Just put it in a plain INSERT INTO ... SELECT ... FROM ... statement.
CREATE TABLE staff (s_id serial UNIQUE, sname varchar);
CREATE TABLE institution (i_id serial UNIQUE, iname varchar);
CREATE TABLE isstaffof
(i_id BIGINT REFERENCES institution(i_id)
, s_id BIGINT REFERENCES staff(s_id)
);
INSERT INTO isstaffof(i_id,s_id)
SELECT i.i_id,s.s_id
FROM staff s, institution i
WHERE i.iname='$institution'
AND s.sname = '$staff'
;
Now, you can wrap that into your front-end code.
You'll have to fetch the result with pg_fetch_assoc or something similar. The way you're using it now you're passing the whole pg_query resource as an integer - obviously php can't convert that to a valid integer.
$name = $_POST["name"];
$username = $_POST["username"];
$password = $_POST["password"];
$email = $_POST["email"];
$institution = $_POST["institution"];
$conn = pg_connect("host=***** port=**** dbname=****** user=**** password=******");
$staffWrite = pg_query("INSERT INTO staff(name, username, password, email) VALUES ('$name', '$username', '$password', '$email')");
$instiWrite = pg_query("INSERT INTO institution(name) VALUES ('$institution')");
$instiFK=pg_query("SELECT i_id FROM institution WHERE name='$institution'");
$rowInsti = pg_fetch_assoc($instiFK);
$staffFK = pg_query("SELECT s_id FROM staff WHERE name='$username'");
$rowStaff = pg_fetch_assoc($staffFK);
$result = pg_query("INSERT INTO isstaffof(i_id, s_id) VALUES (".$rowInsti['i_id'].", ".$rowStaff['s_id'].")");
As an alternative you could probably use pg_last_oid directly after every insert to get the id. (Even more beautiful would the RETURNING-solution be. Just read the comments on the php.net page)

Categories