Fastest way to check array items existence in mySQL table - php

User writes a series of tags (, separated) and posts the form.
I build an array containing the tags and delete dupes with array_unique() php function.
I'm thinking of doing:
go through the array with foreach($newarray as $item) { ... }
check each $item for existence in the tags mySQL table
if item does not exists, insert into tags table
Is there a FASTER or MORE OPTIMUM way for doing this?

I'm hoping that people can comment on this method. Intuition tells me its probably not the best way to go about things, but what do you all think?
Instead of making an extra call to the DB to find duplicates, just add a unique constraint in the DB so that tags cannot be inserted twice. Then use INSERT IGNORE... when you add new tags. This method will ignore the errors caused by duplications while at the same time inserting the new items.

You can call implode( ',' $array ) then use the IN SQL construct to check if there are existing rows in the db.
Example:
<?php
$arr = ...
$sql = 'SELECT COUNT(*) FROM table WHERE field IN ( ' . implode( ',', $arr ) . ' );';
$result = $db->query( $sql );
?>

$tags = array('foo', 'bar');
// Escape each tag string
$tags = array_map('mysql_real_escape_string', $tags, array($sqlCon));
$tagList = '"' . implode('", "', $tags) . '"';
$qs = 'SELECT id FROM tag_list WHERE tag_name IN (' . $tagList . ')';

I don´t know if it's faster, but you can use mysql's IN. I guess you'd have to try.

You could build up a big query and do it in one shot, instead of many little queries:
SELECT DISTINCT tag
FROM my_tags
WHERE tag in ( INPUT_TAGS )
Just build up INPUT_TAGS as a comma-separated list, and make sure you properly escape each element to prevent SQL injection attacks.

Related

mysql REPLACE INTO table with hundreds of columns

I have a table with hundreds of columns. The table structure is out of my control (controlled by a third party). The table also has horrendous field names with spaces, single quotes, etc. and so do the table values. The table is updated once per hour via cron. The cron job truncates and rebuilds the table each time. I also keep an archive table of that table, that I use a REPLACE INTO statement to update or insert as required.
My challenge - I prefer not to have to explicitly define all 350 field names and values, and do so again in my REPLACE INTO statement as this will take a very long time and will require maintenance if the table changes. I would much rather use arrays. Here is what is not working but hopefully gives an idea of the goal (I realize this is deprecated MySQL but it is what it is for a variety of reasons):
$listings = mysql_query("SELECT * FROM current.table");
while ($listing = mysql_fetch_assoc($listings)){
//prepare variables
$fields = array_keys($listing);
$fields = implode('`, `', $fields);
$fields = "`$fields`";
$values = array_values($listing);
$values = implode("`, `", $values);
$values = "`$values`";
mysql_query('REPLACE INTO archive.table ($fields) VALUES ($values)');
}
Posting as a community wiki, no rep should come from this since it did solve the OP's question (as per suggested in comments).
"Aha! Erroneous single quotes on the mysql_query statement was the culprit. I also did mysql_real_escape_string on $values and used single quotes instead of ticks. Worked like a charm. Thank you! Final answer: – Tavish"
Use mysql_error() on the queries. What you posted seems legit code, however values needs to be quoted ' and escaped for possible injection, not ticked.
While using double quotes " for the second query's encapsulation.
mysql_query("REPLACE INTO archive.table ($fields) VALUES ($values)");
As well as other suggestions given.
OP's final code (taken from comments):
while ($listing = mysql_fetch_assoc($listings)){
$fields = array_keys($listing);
$fields = implode(', ', $fields);
$fields = "$fields";
$values = array_values($listing);
$values = implode(", ", $values);
$values = mysql_real_escape_string($values);
$values = str_replace("`","'",$values);
$values = "'$values'";
mysql_query("REPLACE INTO archive.table ($fields) VALUES ($values)");
}

Looping an entire form into a MySQL db row?

I'm hoping someone can give me a suggestion on a challenge I am facing. I am not sure that I'm able to do this the way I envision, so looking for advice from those more experienced.
I have a database table with around 20 columns. It's a lot of columns and unfortunately I cannot change that. The goal is to take a form submission and insert it into this table. So what I have is, the field names are identical to the column names in the database.
To try and keep the code cleaner, I would like to just pull the entire form (key and value) in, instead of doing the traditional $varWhatever = $_POST['whatever']; 20 times. Using something like this: foreach ($_POST as $key => $value)
Now my question is, if at all possible, how can I run that foreach loop in a way that will let me put the keys and values into a single SQL statement?
"INSERT INTO table_name (Loop all keys here) VALUES (Loop related values here)"
Is this even possible, or should I just go back to the more traditional way I mentioned above?
One way I am thinking is, before starting the loop, I could create the empty row and grab it's ID, then within the loop, I could run an update query on the row matching the ID. Sounds sloppy though.
Here is a solution I came up with. You first have to define an array of field names that acts as a whitelist of expected inputs. Then you just loop through that array to build a parameters array to bind the submitted values. And implode the array with a comma when building the query.
$fields = array('field1','field2','field3');
$binds = array();
foreach ($fields as $field) {
$binds[":$field"] = $_POST[$field];
}
$sql = "INSERT INTO table_name (" . implode(',',$fields) . ") VALUES (" . implode(',',array_keys($binds)) . ")";
$db->prepare($sql);
$db->execute($binds);
This assumes you are using PDO.
Yes, you can loop for all keys (eg. do an array_keys), but I don't recommend blindly taking any submission parameter and putting it into a SQL query.
Instead, I would keep a list of all valid columns of the form and work with that, remembering that each value needs sanitization, too.
For example:
<?php
$columns = array('column1', 'column2', 'column3', …);
foreach ($columns as $column) {
if (!isset($_POST[$column])) {
die("No data for column $column\n");
}
}
if (!check_csrf($_POST['csrt_token'])) { … }
# (setup database connection)
$SQL = "INSERT INTO table_name (" . implode(", ", $columns) . ") VALUES (";
foreach ($column as $column) {
$SQL .= "'" . $mysqli->real_escape_string($_POST[$column]) . "',";
}
$SQL[strlen($SQL)-1] = ')';
$mysqli->query($SQL);

concatenate string in php foreach

I should be able to figure this out, but I keep going in circles. I have a form with checkboxes. When the checkboxes are selected they tagged to be deleted.
I process those selected checkboxes with a foreach:
foreach (array_keys($_POST['checkboxselect']) as $k) {
$str .= $k.',';
}
This is my lame attempt to put the list of videos together. The sql delete I'm using is:
DELETE FROM `videos` WHERE `video_name` IN ($str)
So I'm missing lots of things here. The $str needs to have:
Only one checkbox is returned so return string with quotes (i.e. "myvideo.mp4")
Multiple checkboxes are selected so build a string with quotes and commas (i.e. "myvideo.mp4","myvideo2.mp4","myvideo3.mp4")
Thanks for the help.
Try using PHP's implode:
// Init links
$mysqliLink = mysqli_connect('host', 'user', 'password') or die('Could not connect: ' . mysqli_error());
$mysqlLink = mysql_connect('host', 'user', 'password') or die('Could not connect: ' . mysql_error());
//-----
// Prep values for query
//-----
// Only pick one of the following depending upon the mysql library you are using
// If using mysql, passing the 2nd argument is not necessary, but for
// clarity, adding it
$deleteNames = array_map('mysql_real_escape_string', array_keys($_POST['checkboxselect']), array_fill(0, count($_POST['checkboxselect']), $mysqlLink));
// If using mysqli
// This will ensure that $mysqliLink will be passed in as first arg for
// every iteration satisfying the function signature
$deleteNames = array_map('mysqli_real_escape_string', array_fill(0, count($_POST['checkboxselect']), $mysqliLink), array_keys($_POST['checkboxselect']));
//-----
// Because you are passing strings, we need to ensure each is wrapped in quotes
$deleteNames = "'" . implode("','", $deleteNames) . "'";
// Construct query using implode
$sql = sprintf('DELETE FROM `videos` WHERE `video_name` IN (%s)', $deleteNames);
-- Update --
Using Joomla APIs:
$db = &JFactory::getDBO();
// Localize and sanitize each individual value
foreach (array_keys($_POST['checkboxselect']) as $element) {
$deleteNames[] = $db->quote($element);
}
// Because you are passing strings, we need to ensure each is wrapped in quotes
// No longer true because $db->quote taking care of quoting out each name for us
$deleteNames = implode(',', $deleteNames);
// Construct query using implode
$sql = sprintf('DELETE FROM `videos` WHERE `video_name` IN (%s)', $deleteNames);
Use implode() like this:
$str = '"' . implode('","', array_keys($_POST['checkboxselect'])) . '"';
implode() will take an array and join each value in the array with the "glue" string. In this case the "glue" is "," and the array is composed of the keys in $_POST['checkboxselect']. Finally, the resulting string is wrapped in ".
This will result in your desired example string "myvideo.mp4","myvideo2.mp4","myvideo3.mp4".

Proper format for PDO and MySQL IN/NOT IN queries

For reasons that should be obvious, this is murder to search for...
How do I do this in PDO:
SELECT thing FROM things WHERE thing_uid IN ( ... )
My particular use case is a string built by exploding an array taken from a form with several dozen checkboxes. In standard MySQL this is very easy...
$thingString = implode("', '", $thingArray);
$q = "SELECT thing FROM things WHERE thing_uid IN ('$thingString')";
but I want that to benefit from PDO's anti-injection protection... bound params and all that. So how can I do it?
Create an array of as many ? as you have values, and throw that into the query.
$placeholders = array_fill(0, count($thingArray), '?');
$sql = "SELECT thing FROM things WHERE thing_uid IN (" . implode(',', $placeholders) . ")";

How do you output contents of an array as a comma separated string?

I would like to convert an array if IDs, into a string of comma separated values, to use in a MySQL UPDATE query. How would I do this?
Remember to escape values:
'"' . implode('","', array_map('mysql_real_escape_string', $data)) . '"'
implode(',', $array);
Make sure you pass the results through mysql_real_escape_string() before executing your query. This should prevent sql injection if you use implode() as others suggest.
And as nickf mentions, always check to make sure the array isn't empty or null first, and handle those cases. Since you are only dealing with int's, it wouldn't hurt to put some type checking in your assignments, otherwise you'll get sql errors if a string slips in somehow.
Often this type of situation is people building an array from another table for use in a 2nd query.. If this is the case you can use a subquery to accomplish this.
Eg.
UPDATE Table SET Column = Value WHERE ID IN ( SELECT ID FROM Table2 WHERE CONDITIONS )
This is probably better if all ids should be numerical.
Check that it consists of at least one integer with
$ids = array_filter($ids, 'is_int');
if (!$ids) {
//no valid ids returned.
die('or something');
}
$sql .= '(' . implode(',', $ids) . ')';

Categories