query MySQL database with variables from an array - php

I have my database field named tag_info in it I will have data that appears like so:
arm leg head forearm foot
Tags are separated by spaces. (not sure if that is the best way so I am open to suggestions)
Now I need to write a query that will return all the rows that have specific tags in the tag_info field. But the tags I am looking for do not all need to be present just at least one of them does.
$find_tags = array("arm,"leg");
$query = "SELECT * FROM mydatabase WHERE tag_info = '$find_tags'
Is this possible?

You should probably change your database schema around first. Instead of storing tags separated by spaces.
Could you show me the schema (column names) for your table. I think this is a case where you should be having two tables. A separate table just for tags.

That's not a good way to do tagging. You should have a tags table (id, name), yourdatabase_tags (yourdatabaserecord_id, tag_id) so you can associate a record in yourdatabase table to a tag in tags table.
So when you have a list of tags, do a query to get their ids, then query yourdatabase_tags to get the record_ids that have that tag_ids, then query yourdatabase for the records. Or you can combine them into one query (That's an exercise left for the readers :)) )

Mysql syntax does not work that way
here what will work.
$query = "SELECT * FROM mydatabase
WHERE tag_info = '".$find_tags[0]."' OR tag_info = '".$find_tags[1]."'";
In your case since the tags are all in one field you can use a wild card: %
$query = "SELECT * FROM mydatabase
WHERE tag_info LIKE '%".$find_tags[0]."%' OR tag_info LIKE '%".$find_tags[1]."%'";

Query with where tag_info like '%arm%' or tag_info like '%leg%'
but this will be quite inefficient, better to store tags seperately one tag per row, have an index on it.

It's not possible to write a query like that because you will have to dynamically change the amount of OR statements in WHERE blah OR blah. One way to do this would be to grab all the data from the database into a PHP Array. Once in the array you can programatically search through the results and eliminate the ones that do not match.
That is if you want to search for different tags, if you only need a specific query for
$find_tags = array("arm,"leg"); the other answers will work.

you can try below query.
$query = "SELECT * FROM mydatabase WHERE tag_info IN ('$find_tags');
if you are using a table name and field name correct then this may be needful to you.
Thanks.

$keywords = array("arm", "leg");
$length = count($keywords);
$searchCriteria = '';
for($i = 0; $i < $length; $i++)
{
$searchCriteria = $searchCriteria . "tag_info like '%" . $keywords[$i] . "%'";
if( ($i+1) < $length)
$searchCriteria = $searchCriteria . " OR ";
}
$query = "SELECT * FROM mydatabase
WHERE $searchCriteria";
You may need to add some additional checking to ensure that you don't run into error if the array containing the tags is empty.

The more correct approach would be to have a tag table with the availeble tags and a link table between tag_info and tag_table.
But here is a solution with a query
$query = "SELECT * FROM mydatabase WHERE ";
foreach($find_tags as $tag)
{
$query .= "tag_field LIKE %$tag% OR ";
}
$query = substr($query, 0, strlen($query) - 3);
Not tested!!

Sure, use a LIKE in the WHERE clause instead of =.
SELECT * FROM mydatabase WHERE tag_info LIKE '% foot %' or tag_info LIKE '% leg %'

your query should look something like
SELECT * FROM mydatabase WHERE tag_info = 'arm' OR tag_info = 'leg'
You could also use the MySQL IN clause:
SELECT * FROM mydatabase WHERE tag_info IN ('arm', 'leg')
This way is easier to build the query string
Edit
I just saw that you are holding the tags serialized in the DB. Now, that is definitely not good because you are destroying the concept of the structured data.
You should create a table called tags and hold each tag on its own row. Than create another pivot table between the table you wish to tag (the one that holds the serialized tags now) and the tags table.
The pivot table can hold just 2 fields - main_table_id and tag_id - this way, you will be able to query your DB with a single join like:
SELECT t.* FROM tags t
INNER JOIN pivot_table pt ON pt.tag_id = t.id
INNER JOIN main_table mt ON mt.id = pt.main_table_id
WHERE tag IN ('arm', 'leg')

Related

PHP: MYSQL select inside another MYSQL select?

I'm trying to place one mysql select inside another one and combine the results to be displayed.
this is my code:
$allattrs = "";
$sql69 = "SELECT * FROM product_details";
$query69 = mysqli_query($db_conx, $sql69);
$login_check69 = mysqli_num_rows($query69);
if($login_check69 > 0){
while($row69 = mysqli_fetch_array($query69, MYSQLI_ASSOC)){
$FID = $row69["id"];
$sql2s = "SELECT * FROM ATTRIBUTES WHERE id='$FID'";
$query2s = mysqli_query($db_conx, $sql2s);
$login_check2s = mysqli_num_rows($query2s);
if($login_check2s > 0){
while($row2s = mysqli_fetch_array($query2s, MYSQLI_ASSOC)){
// Get member ID into a session variable
$Sid = $row2s["id"];
$attr = $row2s["attr"];
$allattrs .= ''.$attr.', ';
}
}
$product_list .= '<tr>
<td>'.$allattrs.'</td>
</tr>';
}
}
The problem i'm having is that the $allattrs returns the values but it will put everthing together.
for example:
if one attr column in mysql database has apples, and another one has oranges, when i see the results of $allattrs on my PHP page i see this:
id 1 - apples
id 2 - apples, oranges
id 3 - apples, oranges, apples, oranges
etc etc
this is in fact wrong as each attribute value needs to stay true to their own id and product_details id field!
I'm not sure what I am doing wrong to cause this.
could someone please advise on this issue?
any help would be appreciated.
The right way to write your query is using JOIN, EXISTS, or IN. I think you would find this most natural:
SELECT a.id, GROUP_CONCAT(a.attr) as attrs
FROM ATTRIBUTES a
WHERE a.id IN (SELECT id FROM product_details)
GROUP BY a.id;
This replaces a bunch of your code.
Looks like you are only interested in the the attributes then try this out instead of the first sql:
SELECT * FROM ATTRIBUTES where id IN (SELECT id FROM product_details)
You need to set $allattrs to an empty string inside your first while loop, instead of only once before.
Apart from that, you should look into the following two topics: Normalization and JOINs.

Using Replace in SELECT * statement

How can I work this REPLACE statement I'm using in the following, into my SELECT statement below ? The update works perfectly, but need the same code in the SELECT.
$sql1 = ("UPDATE $table SET notes=replace(REPLACE(notes,CHAR(13),' '),CHAR(10),' ') WHERE year='$year'");
$sql2 = "SELECT * FROM $table WHERE SUBSTR(week_start_date,-4)=$startDate AND week_num = '$week' AND archived!='yes' ORDER BY fn,ln";
Thanks for any assistance.
If you have data in your table that has CRLF that you want to REPLACE with ' ', you can't use the SELECT * notation - you have to tell it which column you want to replace the characters in. You'll have to do something like this:
$sql2 = "SELECT REPLACE(REPLACE(notes, CHAR(13), ' '), CHAR(10), ' ') as fixed_notes FROM $table ...`
In general, it's best to avoid using SELECT *, and always to specify the columns you want (as well as any scalar functions you want to run on said columns) explicitly. This way, if the table definition adds more columns, you don't start getting data you never intended to process; and if a column gets dropped, you'll know that was the cause rather than something else in your presentation layer. It also means that it'll be possible to manipulate the individual columns in the statement where appropriate.
If you can live with these limitations, and you don't mind having the column appear twice, you could do this:
$sql2 = "SELECT *, REPLACE(REPLACE(notes, CHAR(13), ' '), CHAR(10), ' ') as fixed_notes FROM $table ...`

PHP PDO result from query

I am trying to do a query in PHP PDO where it will grab a simple result. So like in my query I need it to find the row where the column group is 'Admin' and show what ever is in the group column. I know that we already know what it should be [Should be admin] but just need to get the query to work. Its only grabbing 1 row from my table, so will I need forsearch?
If I change WHERE group = 'Admin' to WHERE id = '1' it works fine. But I need it so it can be where group = 'admin'
$sql2 = "SELECT * FROM groups WHERE group = 'Admin'";
$stm2 = $dbh->prepare($sql2);
$stm2->execute();
$users2 = $stm2->fetchAll();
foreach ($users2 as $row2) {
print ' '. $row2["group"] .' ';
}
Thanks
group is a reserved word in MySQL, that's why it's not working. In general it's a bad idea to use reserved words for your column and table names.
Try using backticks around group in your query to get around this, so:
$sql2 = "SELECT * FROM groups WHERE `group` = 'Admin'";
Also you should really use placeholders for values, because you're already using prepared statement it's a small change.
Edit: just to clarify my last remark about the placeholders. I mean something like this:
$sql2 = "SELECT * FROM groups WHERE `group` = ?";
$stm2->execute(array('Admin'));
try to use wildcard in your WHERE Clause:
$sql2 = "SELECT * FROM groups WHERE group LIKE '%Admin%'";
Since the value in your table is not really Admin but Administrator then using LIKE and wildcard would search the records which contains admin.

Text Search using Mysql

I am trying to create a search blog system where blogs are displayed based on user suggestions. Suggestions are basic hashtags which is stored in user_information table. While blogs will also have some hashtags input by blogger which will be stored in blog table.
Now as I have to match hashtags from user_information table to blog table, I am not finding a way to do this.
I have tried mysql LIKE CLAUSE but I am not able to get even single result.
My Query was [just representation]
SELECT * FROM blog WHERE hashtags LIKE $hashtags_from_user
You are not correctly escaping the value, you require the hash tags to be enclosed in quotations
"LIKE '". $hashtags_from_user ."'";
Additionally you will pay the price in the SELECT statement when you have denormalized database (hash-tags should have their own row in a hash_tags table and the blog would references these via a many-to-many or many-to-one association)
Currently the output of the query is:
SELECT * FROM blog WHERE hashtags LIKE '#hashtag1,#hashtag2,#hashtag'
This isn't going to give you the results you want if you need to match on the individual tags.
The only soultion would be to read the users has tag string, break it up into each tag (using $tags = explode(',', $hash_tags_from_user); and then build up a query to include all of them
$where = array();
$select = 'SELECT * FROM foo';
foreach($tags as $tag) {
$where[] = sprintf('tag LIKE \'%s\'', $tag);
}
if (! empty($where)) $select .= 'WHERE ' . implode(' OR ', $where);
Please don't use the above as it's just and example (and open to simple SQL injection) consider using prepared statements, with PDO/MySQLi.
Try this:
$sql = 'SELECT * FROM blog WHERE hashtags LIKE "'.$hashtags_from_user.'";';
You have to put the string you are searching for between ' or " you can do it without only with numbers.

How to SELECT one field form one table which is part of string contained in one field of another table?

use PHP and MYsql. I have 2 tables like
table 'tag': has field 'id' and 'tag' ('tag' is varchar)
table 'entry': has fields 'tags' and other fields ('tags' is varchar that stores many words as tag, each word is separated by comma. Then I use function 'explode' to extract tags from string to store in array '$tags'. All tags from table 'entry' are stored in table 'tag'.)
What I want is to select all 'tag's from table 'tag' which are tags in the same string of 'tags' of table 'entry' that has a given tag (variable $tag). But no idea, I just try to SELECT tag from table 'entry' which string in 'tags' contains a specified tag (variable $tag).My current code is
$count=0;
$sql="SELECT tags FROM entry WHERE tag LIKE '%$tag%'";
$result = mysql_query($sql);
while($data=mysql_fetch_array($result)){
if(mysql_num_rows($result)!=0){
$tags1=explode(',',$data['tags']);
for($loop=0; $loop<=count($tags1)-1; $loop++){
if(!in_array($tags1[$loop], $tags2)){
$count+=1;
$tags2[$count]=$tags1[$loop];
}
}
}
}
The difficulties you are having solving this problem all stem from the fact that you're storing a list of tags in a single column.
You really need to add an intersection table, to contain the data for the many-to-many relationship between tags and entries. This is the proper way to design a relational database, and many SQL queries will become more efficient if you do that.
For example, to get all the tags from an entry that matches tag $tag:
<?php
$sql = "SELECT t1.tag FROM tag AS t1
INNER JOIN entries_tags AS e1 ON e1.tag = t1.id
INNER JOIN entries_tags AS e2 ON e2.entry = e1.entry
INNER JOIN tag AS t2 ON t2.id = e2.tag
WHERE t2.tag = ?";
$stmt = $pdo->prepare($sql);
$result = $stmt->execute( array($tag) );
$data = $result->fetchAll();
$count = count($data);
I wrote a chapter to detail the practical problems caused by this design in my book, SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
The super inefficient, SQL injection attack prone way of doing it as one statement would be:
$sql="SELECT tags FROM entry WHERE 1=1 ";
Then add each tag to the query before running it:
$sql.="AND tag LIKE '" . $tags1[$loop] . "' ";
If you want to match any tag instead of all tags replace AND with OR.
I'd really suggest normalizing the tags and looking into not building SQL queries that way though.

Categories