INSERT into Table On Columns That Exist - php

Suppose I have a very large array of information for a user:
$user=array(
"name"=>"john",
"ip"=>"xx.xx.xx.xx",
"email"=>"john#something.com",
//lots more values
)
Let's also suppose that this information needs to go into more than one table. For instance a username needs to go table users, address needs to go into a details table, etc.
Now, I use a certain self-made function to insert into my tables that matches array keys to column names and array values to the values being inputted. Something similar to this:
function insert_sql($table, arr $values){
GLOBAL $dbc;
$sql="INSERT INTO `$table` (".implode(array_keys($values), ", ").") VALUES (".implode(array_values($values), ", ").")";
$dbc->prepare($sql)->execute();
return $dbc->lastInsertId();
}
//I don't actually use this function, just trying to show you what is being accomplished.
The problem is that my function uses all the keys and all the values, so when I just need certain parts of the array put into multiple tables, it doesn't work.
The question is:
How do I make an INSERT statement ignore a column if it doesn't exist? So if I insert name,email,address, into table users, but this table doesn't have an address column, I need it to insert the row with the name and email but simply ignore the fact that the address column is not there.
EDIT: The other option is to make an array with the columns of a table and use it to filter the values array. Although I am not really sure how to set this up.

Find given table column names:
SELECT
column_name
FROM
information_schema.columns
WHERE
table_name = 'tablename'
And then just whitelist your keys in $values array
Example:
function insert_sql($table, array $values){
global $connection;
$query = "SELECT column_name FROM information_schema.columns WHERE table_name = :tablename";
/* #var $stmt PDOStatement */
$stmt = $connection->prepare($query);
$stmt->execute(array(
'tablename' => $table
));
$columns = array_flip($stmt->fetchAll(PDO::FETCH_COLUMN, 0));
$values = array_intersect_key($values, $columns);
var_dump($values);
}

How do I make an INSERT statement ignore a column if it doesn't exist?
So if I insert name,email,address, into table users, but this table
doesn't have an address column, I need it to insert the row with the
name and email but simply ignore the fact that the address column is
not there.
You can't
Instead you should map your data to the appropriate tables with separate inserts.

Related

MySQL updating an unknown number of fields

With an INSERT statement I can do something like this, passing in an unknown number of placeholders and values:
$insert_SQL = $db->prepare("INSERT INTO laptops ($fields) VALUES ($placeholders)");
$insert_SQL->execute([$values]);
Is there a similar method when you're updating a record but don't know the number of values? Something like this:
$update_SQL = $db->prepare("UPDATE laptops ($fields) VALUES ($placeholders) WHERE id = $id");
$update_SQL->execute([$values]);
Sort of. You could use REPLACE INTO:
$update_SQL = $db->prepare("REPLACE INTO laptops ($fields) ($placeholders)");
$update_SQL->execute([$values]);
Caveats:
$fields must include id. $values must include $id.
The table must have a primary key or unique index on id. It uses this to determine if a matching row already exists.
It will delete and insert a row, not actually update it. This might matter if you have triggers on the table or for some performance considerations.

MySQL - Insert intro if exist update

I have unknown keys and values to import to database from CSV.
My code is
while($data = fgetcsv($handle,1000,",",'"'))
{
$data=array_map('addslashes',$data); // apply addslashes() to all values
$data=array_combine($csv_fields,$data); // csv fields assoc (key=>value)
$data=array_intersect_key($data,$tbl_fields); // discard redundant
$tbl_fields_str=implode("`,`",array_keys($data));
$tbl_vals_str=implode("','",array_values($data));
$q="INSERT INTO `cmid` (`cmid`,`$tbl_fields_str`) VALUES ('$cmidtrenutni','$tbl_vals_str') ON DUPLICATE KEY UPDATE (`$tbl_fields_str`) VALUES ('$tbl_vals_str')";
$conn->query($q);
}
I need to insert and if exist, update.
I try this code above but doesnt work.
I find something like http://dev.mysql.com/doc/refman/5.5/en/insert-on-duplicate.html
But this doesnt help in my way cause my table doesnt have defined fields. Keys and values are different on every input.
Any solution how to do this?
This is your query:
INSERT INTO `cmid` (`cmid`, `$tbl_fields_str`)
VALUES ('$cmidtrenutni', '$tbl_vals_str')
ON DUPLICATE KEY UPDATE (`$tbl_fields_str`) VALUES ('$tbl_vals_str');
The problem is the UPDATE part. You need to split the values so it looks like:
INSERT INTO `cmid` (`cmid`, `$tbl_fields_str`)
VALUES ('$cmidtrenutni', '$tbl_vals_str')
ON DUPLICATE KEY UPDATE
col1 = newcol1val,
col2 = newcol2val,
. . .
The short-hand that you are using is not valid syntax.
To import from a csv file, take a look at the LOAD DATA INFILE statement or the mysqlimport utility.
Try this one:
$q="INSERT INTO `cmid` (`cmid`,`$tbl_fields_str`) VALUES ('$cmidtrenutni','$tbl_vals_str') ON DUPLICATE KEY UPDATE cmid=cmid";
But I prefer using INSERT IGNORE for your problem:
$q="INSERT IGNORE INTO `cmid` (`cmid`,`$tbl_fields_str`) VALUES ('$cmidtrenutni','$tbl_vals_str')";
You cannot have "unknown" columns in mysql DB. if you want to store pairs of key-value in a mysql table, you should have a table with two columns : one would be named "key" and the other one "value". Add an extra column "cmid" to group your pairs.
This table should have a primary index on "cmid" and "key" columns.
Then you should insert values with a query like:
$sqlVals = "";
foreach ($data as $key => $val) {
$sqlVals .= "($cmidtrenutni, $key, $val),";
}
$sqlVals = substr($sqlVals, 0, -1); //remove last comma.
$query = "REPLACE INTO `myTable` (`cmid`, `key`,`value`) VALUES $sqlVals";

How to insert multiple MySQL table rows from a PHP array

I'm new to both PHP and MySQL and I have a pretty complicated task to do for my current know-how. It goes like this:
I'm trying to create a data-driven website with a hand made CMS that will allow a user with no knowledge of web design/web development to manage it. Where I'm stuck is when I try to create a query to handle the data inserted to the database. It is a bookstore and I'm inserting books in the database. I thought of creating 3 tables, one to store the books themselves, one to store the categories these books belong to (f.e. literature, mystery, fantasy, etc) and, since the relation between these two tables is many-to-many relationship, I also created a lookup table to link these two, using 2 columns populated with 2 foreign keys representing the IDs of the two tables.
The problem is I can't find how to insert multiple rows with same ID (the book ID) on one column and different second column (the category ID). The categories will be picked in an HTML form via checkboxes. I' ve seen in several posts here I need to make an array in PHP script to store the data from the checkboxes the user picked, but I have absolutely no clue on how to structure the query that will turn these checkboxes into multiple MySQL table rows.
I've seen people suggesting imploding the values, but that would store multiple values in one row, or am I mistaken? Because that is not what I want, I want (if f.e. the user checked 3 categories checkboxes) to create 3 rows in the lookup table.
Thanks in advance for your time seeing my question.
$categories = array(1,2,3,4);
$bookId = 5;
foreach ($categories as $categoryId) {
$data = array(
'book_id' => (int)$bookId,
'category_id' => (int)$categoryId
);
$query = "INSERT INTO `lookup` SET ";
$fields = array();
foreach ($data as $field => $value) {
$fields[] = "`$field` = '$value'";
}
$fields = implode(', ', $fields);
$query .= $fields;
mysql_query($query);
}
You can build up an insert using implode to insert many rows. Something like this:-
<?php
$Category = 123;
$Books = array(1,2,3,4,5,6,7,8,9,10);
$sql = "INSERT INTO book_category_link(NULL, category_id, book_id)
VALUES ($Category".implode("),($category", $Books).")";
?>
Or if you want to use the existing tables to get the ids then something like this:-
<?php
$Category = (123, 456, 789);
$Books = array(1,2,3,4,5,6,7,8,9,10);
$sql = "INSERT INTO book_category_link(NULL, category_id, book_id)
SELECT NULL, categories.id, books.id
FROM books
CROSS JOIN categories
WHERE books.id IN (".implode(",", $Books).")
AND categories.id IN (".implode(",", $Category).")";
?>
It's really simple. First you insert the the book into the books table and then get the ID of it. Let's say for this example it is the ID 5.
Lets say you have the following categorie id's from the checkboxes: array(21, 42, 64);
Now you can construct a query like this to insert them into the lookup table:
INSERT INTO `lookup` (book_id, category_id') VALUES (5, 21), (5, 42), (5, 64);
No, you can't insert into multiple tables in one MySQL command. You can however use transactions.
INSERT ...
SELECT LAST_INSERT_ID() INTO #mysql_variable_here;
INSERT INTO table2 (#mysql_variable_here, ...);
INSERT INTO table3 (#mysql_variable_here, ...);
if you want to add multiple rows inserted then u can use the syntax
INSERT INTO table (col1, col2) VALUES ('value', 'value'), (value, 'value');
The very basic idea is to iterate a loop on the categories submitted and run an insert query containing category ID and book ID into the lookup table.
$bookID= $_POST['bookid'];
foreach ($categories as $category) {
mysql_query("INSERT INTO lookup(bookid,categoryid) values($bookID,$catetory)");
}

dedupe mysql table but ignore empty values

I have a php script that uploads csv files into a mysql database.
The database has several columns. Among these columns is an 'email' field. I wrote some mysql that would remove rows that contained duplicate values in the email column. Below is the mysql:
$sql = "CREATE TABLE new_table as SELECT * FROM auto WHERE 1 GROUP BY email";
mysql_query($sql, $conn);
$query = mysql_query("SELECT COUNT(*) FROM new_table");
list($number) = mysql_fetch_row($query);
$query = mysql_query("SELECT COUNT(*) FROM auto");
list($number2) = mysql_fetch_row($query);
$result = $number2 - $number;
mysql_query("DROP TABLE auto");
mysql_query("RENAME TABLE new_table TO auto");
The code works, it removes duplicate values.
Problem:
It removes rows that contain no values. So it assumes that two or more emails values that are empty are duplicates and removes they're rows.
Question:
How do I tell mysql to ignore empty values.
Thanks for the help.
Edit
The where is my database table. One table.
The when is when I execute the code. I plan on putting in a php file to be executed on demand.
The result I expect is a mysql table without duplicate emails.
Something like this would work for a one-time alteration, by allowing NULL in email, and adding a UNIQUE constraint:
-- set empties to NULL
UPDATE tablename SET email = NULL WHERE LENGTH(email)=0;
-- drop all rows violating the UNIQUE constraint on email:
ALTER IGNORE TABLE tablename ADD UNIQUE (email);

How do I get all the ids of the row created by one multiple row insert statement

I'm new to php. So, please forgive me if this seems like a dumb question.
Say i have a MySQL insert statement insert into table (a,b) values (1,2),(3,4),(5,6). table 'table' has a auto increment field called 'id'.
how can I retrieve all the ids created by the insert statement above?
It will be great if i get an example that uses mysqli.
You can't. I would suggest that you maintain your own ids (using guid or your own auto-increment table) and use it when you insert into the table.
But it's possible to get the auto-increment value for the last inserted using LAST_INSERT_ID():
http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
AngeDeLaMort's answer is almost right. Certainly, the most appropriate way to deal with the problem is to insert one row at a time and poll the insert_id or generate the sequence elsewhere (which has additional benefits in terms of scalability).
I'd advise strongly against trying to determine the last insert_id and comparing this the most recent insert_id after the insert - there's just too may ways this will fail.
But...an alternative approach would be:
....
"INSERT INTO destn (id, data, other, trans_ref)
SELECT id, data, other, connection_id() FROM source";
....
"SELECT id FROM destn WHERE trans_ref=connection_id()";
....
"UPDATE destn SET trans_ref=NULL where trans_ref=connection_id()";
The second query will return the ids generated (note that this assumes that you use the same connection for all 3 queries). The third query is necessary because connection ids to go back into the pool when you disconnect (i.e. are reused).
C.
In some cases, if you have another identifier of sort such as a UserID, you could filter your query by UniqueID's greater than or equal to mysql_insert_id(), limit by the number of affected rows and only display those by the user. This would really only work inside of a transaction.
$SQL = "INSERT INTO Table
(UserID, Data)
VALUES
(1,'Foo'),
(1,'Bar'),
(1,'FooBar')";
$Result = mysql_query($SQL);
$LastID = mysql_insert_id();
$RowsAffected = mysql_affected_rows();
$IDSQL = "SELECT RecordID
FROM Table
WHERE UserID = 1
AND RecordID >= '$LastID'
LIMIT '$RowsAffected'";
$IDResult = mysql_query($IDSQL);
as a follow up to AngeDeLaMort:
You could seperate your inserts and do it something like this:
$data = array (
array(1,2),
array(3,4),
array(5,6)
);
$ids = array();
foreach ($data as $item) {
$sql = 'insert into table (a,b) values ('.$item[0].','.$item[1].')';
mysql_query ($sql);
$id[] = mysql_insert_id();
}
Now all your new id's are in the $id array.
Maybe I can do this
$insert = "insert into table (a,b) values (1,2),(3,4),(5,6)";
$mysqli->query($insert);
$rows_to_be_inserted=3;
$inserted_id = $mysqli->insert_id // gives me the id of the first row in my list
$last_row_id = ($inserted_id+$rows_to_be_inserted)-1;
$mysql->query("select * from table where id between $inserted_id and $last_row_id");
what to you guys say?

Categories