Insert one array value to a mysql insert statement - php

People enter values into a form and on one entry of the form I have multiple values.
Eg:
One Entry for Name
and Multiple entries for hobbies.
I could enter into the db by running a for loop but then that would be multiple entries for the same name for each different hobby.
How can I enter one name and all hobbies with 'space' into one DB field 'TWIG'. I could use arrays but it shows up as ARRAY but then its back to FOR loop.
for ($t=0; $t<=$_POST['tc']-1; $t++) {
echo "<BR> ts:".$_POST[$t]."<BR>";
$ths[]=$_POST[$t];
}
print_r ($ths);
$statement = $link->prepare("INSERT INTO quest(cnos, dte, twig)
VALUES(:q, :d, :t )");
$statement->execute(array(
":q" => htmlspecialchars ($_POST['iht']),
":d" => $_SERVER['REQUEST_TIME'],
":t"=> $ths
));

One possibility is to implode your hobbies / concatinate your string into one...
for ($t=0; $t<=$_POST['tc']-1; $t++) {
$ths = $_POST[$t] . " "; //Concatinate string, do no use array!
}
//Cut off last character " " to avoid ugly space at the end:
$ths = substr($ths, 0, strlen($ths) - 1);
However a more clean solution is to make a more clear database structure if you want for atomic values.
This is an 1:n relation (Each of your entries in table A relates to n instances in table B).
Here is an example that can be adapted into your schema very easy:
Table User(id[PK], name, age);
Table User_Hobbies: (user_id, hobby_descr);
--
INSERT INTO User(2, "Chris", 19);
INSERT INTO USER_User_Hobbies(2, "videogames");
INSERT INTO USER_User_Hobbies(2, "music");
--
Example query:
SELECT u.name, u.age, GROUP_CONCAT(uh.hobby_descr) AS hobbies
FROM User u
LEFT JOIN User_Hobbies uh ON u.id = uh.user_id
WHERE u.id = 123 /*Where is optional, if you want to filter for a specific user*/
GROUP BY u.id;
Possible result:
| name | age | hobbies |
chris 18 videogames, music
steve 22 computer, programming, php

Use the implode function for this as follows:
":t"=> implode(" ", $ths);

I am not sure, if I understand your question right, but if you want to insert an array as a comma-seperated string (or separated by whatever), why don't use the php implode function: http://php.net/manual/de/function.implode.php
For example:
$myHobbies = array("football", "basketball", "running");
$statement = $link->prepare("INSERT INTO quest(cnos, dte, twig)
VALUES(:q, :d, :t )");
$statement->execute(array(
":q" => htmlspecialchars ($_POST['iht']),
":d" => $_SERVER['REQUEST_TIME'],
":t"=> implode(" ", $myHobbies)
));
I think to use comma's or semicolons as separator, is better than whitespaces, since some of the hobbies could consists of two words (for example "pc gaming").

I think that better solution is to use json_encode and not implode, since it provides more robust structure.
":t" => json_encode($myHobbies)

You can use join function to store multiple value in same field in database may be it will works:-
":t"=>join(",", $ths);//if you want ,(coma) between two string
or
":t"=>join(" ", $ths);//if you want space between two string

Related

PHP - search using 2 concatenated fields

I've concatenated 2 fields in a form's drop down list, with the 2 fields being First Name and Last Name; This allowed me to display a person's full name.
The issue is that in the database a person's full name is stored in 2 seperate fields; First Name and Last Name.
I need to match the person's full name (from the dropdown list) to the First Name and Last Name in the DB, however I am struggling to achieve this.
The php code for the concatenated form list is:
<td>
<select name='IndivSurname'>";
while($FullName_row = odbc_fetch_array($sql_run_FullName)){
$IndivFirstName=$FullName_row['FirstName'];
$IndivLastName=$FullName_row['LastName'];
echo"<option value='$IndivIndivId' . '$IndivTenantNdx'> $IndivFirstName $IndivLastName</option>";
}
</select>
</td>
While the SQL statement is:
SELECT EventId, EventTime, Individual, Tenant, TenantName, DeviceName, Comment,
InetDb.dbo.Individuals.FirstName, InetDb.dbo.Individuals.LastName
FROM taclogdata.dbo.Event
LEFT JOIN InetDb.dbo.Tenants
ON taclogdata.dbo.Event.Tenant = InetDb.dbo.Tenants.TenantId
LEFT JOIN InetDb.dbo.Individuals
ON taclogdata.dbo.Event.Individual = InetDb.dbo.Individuals.IndivId
AND taclogdata.dbo.Event.Tenant = InetDb.dbo.Individuals.TenantNdx
WHERE (taclogdata.dbo.Event.EventTime BETWEEN '00:00:00 05/26/2015'
AND '09:00:00 05/26/2015'
AND (taclogdata.dbo.Event.Comment ='Reader entry'
OR taclogdata.dbo.Event.Comment='Reader exit')
AND (InetDb.dbo.Individuals.FirstName = '$IndivFirstName'
AND InetDb.dbo.Individuals.LastName = '$IndivLastName')";
Many thanks in advance
The easiest way to do this, is by splitting the string on the space. However, that introduces a bug if the person has more than one first- or surname. This means that in the cases where we have 3 or more elements in the result of the split, we'll have to add the middle elements to both matches; Seeing as we don't know whether it's a surname or middle name.
That is why I recommend using a different character for the glue in the value attribute, one which is never used in a name. # is one such character.
This leaves you with the following code for the form-generation:
// CF: Added HTML-escaping to prevent XSS-attacks.
$IndivFirstName=htmlspecialchars ($FullName_row['FirstName']);
$IndivLastName=htmlspecialchars ($FullName_row['LastName']);
echo"<option value='{$IndivFirstName}#{$IndivLastName}'> $IndivFirstName $IndivLastName</option>";
In the SQL-statement you can do the following (using prepared statements):
$stmt = $db->prepare ("SELECT EventId, EventTime, Individual, Tenant, TenantName, DeviceName, Comment, InetDb.dbo.Individuals.FirstName, InetDb.dbo.Individuals.LastName
FROM taclogdata.dbo.Event
LEFT JOIN InetDb.dbo.Tenants
ON taclogdata.dbo.Event.Tenant = InetDb.dbo.Tenants.TenantId
LEFT JOIN InetDb.dbo.Individuals
ON taclogdata.dbo.Event.Individual = InetDb.dbo.Individuals.IndivId
AND taclogdata.dbo.Event.Tenant = InetDb.dbo.Individuals.TenantNdx
WHERE (taclogdata.dbo.Event.EventTime BETWEEN '00:00:00 05/26/2015' AND '09:00:00 05/26/2015'
AND (taclogdata.dbo.Event.Comment ='Reader entry' OR taclogdata.dbo.Event.Comment='Reader exit')
AND (InetDb.dbo.Individuals.FirstName = :firstname AND InetDb.dbo.Individuals.LastName = :lastname)");
$name = explode ("#", $_POST['IndivSurname']);
$stmt->exec (array (":firstname" => $name[0], ":lastname" => $name[1]));
Splitting on spaces is not going to be reliable given the number of De Aches and Mc Pains in this world.
The really sure way of doing this is to save the first name and last name in a hidden table within your form (in the same order as your pulldown).
A possible alternative is to concatenate using a not a space but one of the other white space characters like NO BREAK SPACE "\u00A0" which you can then reliably split later.
Ok I solved my problem by POSTing the user's PK rather than his First Name and Last Name individually, and search using those characteristics.
However I introduced the explode function as per the below to separate the concatenated values as per the below:
SQL query selects the IndivId, TenntNdx, individual's first name and last name, however only the first name and last name of the user are displayed in the form's drop down list. The matching IndivId and TenantNdx are posted using the code below:
$sql_FullName = "SELECT IndivId, TenantNdx, FirstName, LastName FROM InetDb.dbo.Individuals
ORDER BY FirstName ASC";
$sql_run_FullName = odbc_exec($conn_general, $sql_FullName);
while($FullName_row = odbc_fetch_array($sql_run_FullName)){
$IndivIndivId = $FullName_row['IndivId'];
$IndivTenantNdx = $FullName_row['TenantNdx'];
$IndivFirstName=$FullName_row['FirstName'];
$IndivLastName=$FullName_row['LastName'];
echo"<option value='$IndivIndivId $IndivTenantNdx'> $IndivFirstName $IndivLastName</option>";
The code above would POST the IndivId and TenantNdx (which form a composite primary key for the individuals table) to the page containing the code below which would then separate the 2 fields and search by the PK rather than the individual's name:
$IndivSurname = $_POST['IndivSurname'];
$explode_sql = explode(" ", $IndivSurname, 2);
$ListIndivId = $explode_sql['0'];
$ListTenantId = $explode_sql['1'];

Detect duplicate values in a comma separated field in mysql

Is it possible somehow to return all the rows in a mysql table where one field has duplicate values in a comma separated string?
EG if we had fields
ID VALUE
'1', '123,123,678'
'2', '23,24,25'
I only want to return row 1 in this instance?
There is no way to do this from within SQL that's both practical and efficient.
As indicated by John Conde, you should normalize your database so that it looks like this:
ID VALUE
1 123
1 123
1 678
2 23
2 24
2 25
Then you can easily prevent the situation from arising by disallowing duplicate rows
(eg., by defining a UNIQUE index on the ID and VALUE columns). And if you can't / don't want to prevent it from happening, you can at least detect it much easier in pure SQL.
As it currently stands, I would get all rows into a PHP array and detect duplicate values from there. This isn't really efficient either, but at least it'll be more practical than trying to reach this in pure SQL.
$result = mysql_result('select ID, VALUE from table');
while($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
$values_array = explode( ',' , $row['VALUE'] );
if(count($values_array) != count(array_unique($values_array)))
{
// There must be duplicate values in the array
echo 'Row with id ' .$row['ID'] .' has duplicate values';
}
}
You can do it like this, but normalizing your database would be much better.
(Oh, and don't use these mysql_... functions; you should use mysqli or PDO. This is just to show you how it works).
Assuming you just have just 3 values comma separated . then you can use this via mysql and get rid of php:
select a.id,a.v1 , a.v2,a.v3,a.value from (
SELECT id,value,SUBSTRING_INDEX(value, ',', 1)as v1,
SUBSTRING_INDEX(SUBSTRING_INDEX(value, ',', 2), ',', -1) as v2,
SUBSTRING_INDEX(value, ',', -1) as v3
FROM table1 )a
where v1 = v2 or v1 = v3 or v2 = v3
DEMO HERE
OTHER DEMO with extra values
OBS: have make extra v1 ,v2,v3 separated ,so you may use them in your work

Select values that are NOT in table

Let's say I have three values in PHP: "a", "b", "c". Doesn't matter whether in an array or comma separated string.
There is a table in database:
id | value
1 | a
2 | b
3 | d
My purpose is to find the values that are in php array but not in database table.
The given example will give "c".
Can I do it with only one query?
UPDATE
Received several good suggestions in answers about array_diff(), though in my case the DB table is really large and the array has not more than 5-6 items. So it would be better to perform 5-6 queries, I think.
If the PHP array is short, you can build a UNION ALL query to build your small table, then use NOT IN or LEFT JOIN query (whichever is faster) against the large table:
SELECT value
FROM (
SELECT 'a' AS value
UNION ALL
SELECT 'b'
UNION ALL
SELECT 'c'
) AS php_array_values
WHERE value NOT IN (
SELECT value
FROM that_large_table
);
Alternately, you can insert the php array values in a temporary table and use the IN or JOIN queries. Of course, this means you end up writing three extra queries:
CREATE TEMPORARY TABLE IF NOT EXISTS php_array_values (value VARCHAR(100));
DELETE FROM php_array_values;
INSERT INTO php_array_values VALUES ('a'), ('b'), ('c');
SELECT php_array_values.value
FROM php_array_values
LEFT JOIN that_large_table ON php_array_values.value = that_large_table.value
WHERE that_large_table.value IS NULL
how about this?
<?php
$a = array('a', 'b', 'c');
$values = implode("','", $a);
$sql = "SELECT DISTINCT `value` FROM `mytable` WHERE `value` IN ('$values')";
echo $sql;
perform the sql query. the result will be those 0 to 3 elements you already have. next, do an array_diff (which will not be heavy at all, since you'll have your initial small array, and the array of those in the db, which is even smaller).
$not_in_db = array_diff($a, $array_from_sql_result);
if what you have is a string with comma separated values, then you'll need to "explode" it first:
$s = "a,b,c";
$a = explode(",", $s);
You could select all the entries in the table and then perform an array_diff().
But this isn't one query, is one query and some post processing.
For this I would pull values from the table into an array and use array_diff
REF: http://www.php.net/manual/en/function.array-diff.php
Instead of pulling all the elements from the DB you can try using 'LIKE' statement, this will reduce the number of entries pulled from DB.
something like this :
PHP array values : a,b,c
DB values : a,b,d
select value from your_table_name where (value LIKE '%a%') OR (value LIKE '%b%') OR (value LIKE '%c%');
o/p of this will be : {a,b}
now use array_diff of php.
select max(*) as count from tablename
where field1 != $arr[0] && field2 != $arr[1] && field3 != $arr[2];
You can use and or or operators if you want. if the return count for this query is 0 then the array values are not exist in the database already.

PHP MySQL extract field then split at double spaces

I have a table which contains rows, within each row there is a field which contains peoples names seperated by a double space.
I need to extract this field from each row, then split the field into separate names and dump into another table.
What's the best way to do this? Do I split it within the sql query or extract the fields then split using PHP?
New info:
OK, the field contains full names e.g. john doe, tom smith etc etc the double spaces separate the full names e.g.
JOHN DOE TOM SMITH JOHN WOO
There might be one name in the field but there could also be potentialy up to 10
Thanks
Darren
you can do it in sql. should be something like this:
INSERT INTO tbl_dest (first_name, last_name) VALUES
(SELECT
LEFT(ts.name, LOCATE(' ',ts.name)) AS first_name,
SUBSTRING(ts.name, LOCATE(' ',ts.name)+2) AS last_name
FROM tbl_source AS ts)
as #Marc B stated, it will only work if the data is consistent (ie each row's name field contains a double space and the first double space separates the name and password.
you can ignore other rows (without the double space) by simply adding
INSERT ...
(SELECT ...
FROM tbl_source AS ts WHERE LOCATE(' ', ts.name) > 0)
if the field starts with a double space (ie first name is empty) it will also be ignored
EDIT:
since you said that each row can contain multiple pairs of first/last name. here's a code example:
$query = "SELECT `name` FROM `tbl_source`";
$res = mysql_query($query, $link);
$names = array();
while ($row = mysql_fetch_assoc($res)) {
$nmlist = explode(' ',$row['name']);
for($i=0, $n=count($nmlist); $i<$n-1; $i+=2){
$names[] = "('" . mysql_real_escape_string(stripslashes($nmlist[$i])) ."',"
."'" . mysql_real_escape_string(stripslashes($nmlist[$i+1])) ."')";
}
}
$insert_query = "INSERT INTO `tbl_dest` (`first_name`,`last_name`) VALUES "
.implode(',', $names);
mysql_query($insert_query, $link);
in this example, double spaces are between first name and last name, and between each pair
I'm not familiar with a MySQL solution for this.
I suggest grabbing the information you need, using explode(' ', $info); and then inserting back into the database.

filter mySQL database with multiple words in any order in Concatenated field

I have Concatenated in mySQL to produce a field that I can search with. This contains animal as well as owner names and addresses for a client database. I need to be able to search by animal name, owner names and postcode in any order.
Thus if if my name is john smith and i own a dog called Fido and live in postcode AB1 2CD, I want the search to be yield results if any of the following are entered:
"john fido AB1"
"john AB1"
"AB1 john"
"AB1 fido"
"fido 2CD"
"2CD"
"fido"
etc... i.e any of the fields in any order, and also not complete words either so "john fido AB1" should yield the same result as "jo fi AB1"
I currently have this PHP code, taking a single text field on my search page, exploding then imploding it to add % between the search terms:
$list = explode(' ',$_GET["q"]);
$q = implode('%%', $list);
if (!$q) return;
$sql = "SELECT DISTINCT owner.AddressPrim, owner.PostcodePrim,
owner.OwnerSurnamePrim,owner.OwnerForenamesPrim,owner.OwnerID
FROM owner
Inner Join patient ON owner.OwnerID = patient.OwnerID
WHERE CONCAT_WS(' ',owner.AddressPrim, owner.PostcodePrim,
owner.OwnerForenamesPrim,owner.OwnerSurnamePrim,patient.AnimalName) LIKE '%$q%'";
This works for "AB1 john" and "john fido" but not "fido john" as it is out of order in the concatenated field.
Any help greatly appreciated
I think you're going to have to split the keywords and add a query for each keyword in the string of keywords.
So first (in PHP), split the query string and dynamically generate your SQL query, then send it to the database. Here's some pseudocode to show you what I mean:
$keywords = explode(' ', $q);
$sql = "SELECT DISTINCT owner.AddressPrim, owner.PostcodePrim,
owner.OwnerSurnamePrim,owner.OwnerForenamesPrim,
owner.OwnerID
FROM owner
Inner Join patient ON owner.OwnerID = patient.OwnerID";
$first = true;
foreach($keyword in $keywords):
if($first):
$sql += " WHERE ";
$first = false;
else:
$sql += " AND ";
$escaped = mysql_real_escape_string($keyword);
$sql += " CONCAT_WS(' ',owner.AddressPrim, owner.PostcodePrim,
owner.OwnerForenamesPrim,owner.OwnerSurnamePrim,patient.AnimalName)
LIKE '%$escaped%'";
But do beware, this is not going to be anywhere near fast for the size of tables you'll probably encounter in daily operation. You may want to look into a better way of doing fulltext search, whether it means using a library or making a cross-reference table of keywords maintained by triggers.
MySQL's fulltext search (MyISAM tables only!) could be useful to you.
http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html
You can try this http://www.sphinxsearch.com/
You need dedicated server to run this thing, but if you have one, sphinx will easily solve your problem and your queries won't load database.

Categories