DISTINCT Comma Separated SQL Table Rows - php

I have a b_topics table with tags column in multiple rows
id | tags
1 | Joshua, Janet, Hannah
2 | Glory, Jon, Celina, Johanna
3 | Bridge,Terry, Sterling
4 | Daniel, Florence, Joanne
I want to check for the related tags with the input Jo so i have the below sql select
$query = Jo;
$sql = mysql_query("SELECT DISTINCT tags FROM b_topics WHERE tags LIKE '%{$query}%'");
while ($row = mysql_fetch_array($sql)) {
$array[] = array ( 'label' => $row['tags'], 'value' => $row['tags'], );
}
echo json_encode ($array);
This is the output:
[{"label":"Joshua, Janet, Hannah","value":"Joshua, Janet, Hannah"},{"label":"Glory, Jon, Glory","value":"Glory, Jon, Glory"},{"label":"Daniel, Florence, Joanne","value":"Daniel, Florence, Joanne"}]
I want the matched words to be on foreach();
Expected output:
[{"label":"Joshua","value":"Joshua"},{"label":"Jon","value":"Jon"},{"label":"Johanna","value":"Johanna"},{"label":"Joanne","value":"Joanne"}]

Fix your data structure! You should not be storing multiple values in a string delimited list. This is simply not the right way to store the data. SQL has this great data structure for storing lists. It is not called "string". It is called "table". You want a table called TopicTags with one row per topic and per tag.
Although you can do nasty string functions to get what you want, this would be much simpler with the right data structure:
select topic_id, tag
from TopicTags tt
where tag like '%Jo%';
You can aggregate if you want the results in a particular format.

set your tables because when your query return a row then you can store that row not only a single tag.
$array[] = array ( 'label' => $row['tags'], 'value' => $row['tags'], );
where $row['tags']=Joshua, Janet, Hannah so its gives result like
'label' ="Joshua, Janet, Hannah"

$sql = "select * from test where tags like '%Jo%'";
$res = mysqli_query($con,$sql);
$string = '';
while($row=mysqli_fetch_array($res)){
$string .= ','.$row['tags'];
}
$array = explode(',', $string);
$search = preg_quote('Jo', '~'); // don't forget to quote input string!
$result = preg_grep('~' . $search . '~', $array);
print_r($result);

Related

php query array in array

I am using php phalcon5 framework. I have 2 columns on a product table. Product title and keyword. The product title contains 1 complete sentence and the keyword contains multiple comma separated words. I have done different queries but am getting unexpected results. How do I get the right results?
[Table]
table -> products
column -> product_title, keywords
-----------------------------------------------
product_title | keywords
-----------------------------------------------
three piece | women,cloth,three,piece
shari | women,cloth
Kurti | cloth,women
-----------------------------------------------
[Query Results]
Query1: women cloth
Result: three piece, shari
Expected Result: three piece, shari, Kurti
Query2: cloth women
Result: Kurti
Expected Result: three piece, shari, Kurti
Query3: women/cloth
Result: Three piece, shari, Kurti
Expected Result: three piece, shari, Kurti
Query4: cloths
Result: No Data Found
Expected Result: three piece, shari, Kurti
[Controller]
$data = trim($this->request->getPost('q'));
$query = Products::find(["keywords LIKE :key: OR product_title LIKE :data:",
"bind" => ["data" => '%'. $data .'%',"key" => '%'.$data.'%'],
]);
if($query->count() > 0)
{
$this->view->setVar('pro',$query);
$this->view->pick('index/query');
}
One thing to take in account is that you can use only one bind param, ex. data, and reemploy it in either keywords and product_title comparison:
$query = Products::find(["keywords LIKE :data: OR product_title LIKE :data:",
"bind" => ["data" => '%'. $data .'%'],
]);
In the other hand, you should to explode your query string to prevent it from affecting the words order, and repeat your keywords/product_title comparison with each of them:
$words = array_map('trim', preg_split('|\s+|', $this->request->getPost('q')));
$params = [];
foreach ($words as $key => $word) {
$params['conditions'] .= isset($params['conditions']) ? ' AND ' : '';
$params['conditions'] .= "(keywords LIKE :word".$key.": OR product_title LIKE :word".$key.":)";
$params['bind']['word'.$key] = '%'. $word .'%';
}
$query = Products::find($params);

How do I convert a one to many relationship to a flat array using mysql and php

How can I format a one to many relationship to one 'row' using mysql or php?
Main Table
movie_id name
1 Portland
2 Blazers
Many Table
movie_id actor
1 Brandon
2 Greg
Below is the psudo code I have mapped out. Not sure if there is any thing native to PHP or MYSQl that would be more efficient before I continue. I'm thinking I have two queries. One with the movie information. One with the actor information. Once they are arrays I will loop through to get a single record for each movie with the actors.
movieInfo = = []
actorInfo = = []
Foreach(movieActorId)
If(actorId = movieActorId)
arrayPush(movieInfo[actorArray],actorInfo[name]);
The final array would look like this
movieInfo = array('movie_id' => '1',
'movie_name' => 'porland',
'actor' => array(Brandon,Greg)
);
Maybe this is what you need:
SELECT m.movie_id, m.name AS movie_name, GROUP_CONCAT(a.actor SEPARATOR ',') AS actor
FROM movies m
JOIN actors a ON m.movie_id = a.movie_id
GROUP BY m.movie_id;
// Run query
$movieInfo = array();
while ($array = mysqli_fetch_assoc($result)) {
$array['actor'] = explode(',', $array['actor']);
$movieInfo[] = $array;
}

MySql NOT IN many values from the mysql fetched result

I have 2 MySql queries which are interdependent.
My 'table1'
----------
id
----------
1
2
3
4
5
6
7
My First query
$sql1 = "SELECT * FROM table1 WHERE";// some condition which gives me id's 1,2,3
$res1=$obj->_executeQuery($sql1);
$res1=$obj->getAll($res1);
The result of this is giving me array
Array
(
[0] => Array
(
[id] => 1
..
..
)
[1] => Array
(
[id] => 2
..
..
)
[2] => Array
(
[id] => 3
..
..
)
)
I want to run another query on same 'table1', where not equal to list of ID's which i am getting from the first query.
My Second Query
$sql2 = "SELECT * FROM table1 WHERE id NOT IN (" . implode(',', $res1) . ")";
This is not showing me only one id i,e first. In above case i should get id's 4,5,6,7
Since implode will not give the desired value on multidimensional array so first you need to get the array of all id's to form one-dimensional array then use implode on the array of id's:
$id=array();
foreach ($res1 as $key=>$inner_array){
$id[]= $inner_array['id'];
}
you can use array_walk also here like this:
array_walk($res1,function($c) use (&$id) {$id[] = $c['id'];});
but i think the best one is array_map :
$id = array_map(function($i) {
return $i['id'];
}, $res1);
$sql2 = "SELECT * FROM table1 WHERE id NOT IN (" . implode(',', $id) . ")";
Note: when ever you are doing select please specify your column if you need few to select,unnecessarily selecting all will slow your sql processing.
suppose here:SELECT * FROM table1 WHERE, if you need only id then please select id only.
You have to change $res1, which is a two-dimensional array, into a one-dimensional array of IDs to be able to use implode:
$ids_from_first_query = array();
foreach($res1 as $result_row) {
$ids_from_first_query[] = $result_row['id'];
}
$ids_as_string = implode(',', $ids_from_first_query);
$sql2 = 'SELECT * FROM table1 WHERE id NOT IN(' . $ids_as_string . ')';
In the above code, $ids_as_string will look like this:
1,2,3
Thus it can be used in your MySQL query
Here you are getting two dimensional array so that's reason it is not working
while($each=mysql_fetch_array($res1))
{
$array[]=$each[id];
}
$imp=implode(",",$array);
$sql2 = "SELECT * FROM table1 WHERE id NOT IN (".$imp.")";
Try this it will works

A general single sql query

I have a table like this:
id | roll_no | name
---------------------
1 | 111 | Naveed
2 | 222 | Adil
3 | 333 | Ali
If I have data like this:
$fields = array( "id" , "roll_no" ) and $values = array( "1,111", "2,222" );
It means I have to write a sql query to get records from table where (id != 1 and roll_no != 111) and (id != 2 and roll_no != 222). It means 3rd record will be fetched.
If I have data like this:
$fields = array( "id" ) and $values = array( "2", "3" );
It means I have to write a sql query to get records from table where (id != 2) and (id != 3). It means 1st record will be fetched.
Q: How to write a general single query using php to get data from table using above two data arrays.
Thanks
select * from dummy where concat_ws (',', id, roll_no) not in ('1,111', '2,222')
Complete solution:
$tableName = "test";
$fields = array( "id" , "roll_no" );
$values = array( "1,111", "2,222" );
$fieldsStr = implode(',', $fields);
$valuesStr = implode("','", $values);
$sql = "SELECT *
FROM $tableName
WHERE concat_ws(',', $fieldsStr ) NOT IN ( '$valuesStr' )";
You will probably always have to explode the Array in PHP and pass the values as a string into the query (sprintf) so you probably can, and should, do all in PHP.
One thing that catches my eye is that you are always using ID's. Are the ID's a unique or primary field? If so just forget about the roll_no as your query will be faster using just ID's.
Complete solution with the help of accepted answer.
$tableName = "test";
$fields = array( "id" , "roll_no" );
$values = array( "1,111", "2,222" );
$fieldsStr = implode(',', $fields);
$valuesStr = implode("','", $values);
// Get all records from remote table
$sql = "SELECT * FROM $tableName WHERE concat_ws(',', $fieldsStr ) NOT IN ( '$valuesStr' )";

MySQL (exploding/matching array)

Question1:
MySQL table
id | array
1 | 1,2,3
2 | 2
3 | 2,3
4 | 4,5,6
$_GET['id'] = 2;
$a = mysql_query("SELECT * FROM `table` WHERE `array` ??? '$_GET[id]'");
In this step, I want to run through the entire array and see if it matches with the $_GET['id'], so it should output:
ids: 1,2,3
Question2:
MySQL table
id | array
1 | 4,5,6
2 | 3,4,7
$_GET['id'] = 4;
$a = mysql_query("SELECT * FROM `table` WHERE `array` ??? '$_GET[id]'");
In this step, I only want to match against the first element in the array, so it should output:
id: 4
I can only think of using PHP to do this, but I'd rather do all that just within the MySQL query, if that is even possible.
$a = mysql_query("SELECT * FROM `table`");
while($b = mysql_fetch_assoc($a))
{
$elements = explode(',', $b['array']);
foreach($elements as $element)
{
if($element == $_GET['id'])
{
echo $b['id'].'<br />';
}
}
}
or
$a = mysql_query("SELECT * FROM `table`");
while($b = mysql_fetch_assoc($a))
{
$array = $b['array'];
if(in_array($_GET['id'], $array))
{
echo $b['id'].'<br />';
}
}
that would look just awful.
That you can/should structure your database differently has already been mentioned (see http://en.wikipedia.org/wiki/Database_normalization). But....
See FIND_IN_SET()
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2
e.g.
<?php
$mysql = init();
bar($mysql, 1);
bar($mysql, 2);
bar($mysql, 3);
bar($mysql, 4);
function bar($mysql, $x) {
$sql_x = mysql_real_escape_string($x, $mysql);
$result = mysql_query("SELECT id, foo FROM soTest WHERE FIND_IN_SET('$sql_x', foo)", $mysql) or die(mysql_error());
echo "$x:\n";
while( false!==($row=mysql_fetch_array($result, MYSQL_ASSOC)) ) {
echo $row['id'], ' ', $row['foo'], "\n";
}
echo "----\n";
}
function init() {
$mysql = mysql_connect('localhost', 'localonly', 'localonly') or die(mysql_error());
mysql_select_db('test', $mysql) or die(mysql_error());
mysql_query('CREATE TEMPORARY TABLE soTest (id int auto_increment, foo varchar(64), primary key(id))', $mysql) or die(__LINE__.' '.mysql_error());
mysql_query("INSERT INTO soTest (foo) VALUES ('1,2,3'), ('2,4'), ('3'), ('2,3'), ('1,2')", $mysql) or die(__LINE__.' '.mysql_error());
return $mysql;
}
prints
1:
1 1,2,3
5 1,2
----
2:
1 1,2,3
2 2,4
4 2,3
5 1,2
----
3:
1 1,2,3
3 3
4 2,3
----
4:
2 2,4
----
MySQL can't use indices to perform this search, i.e. the query results in a full table scan, see Optimizing Queries with EXPLAIN
edit:
For your second question you only have to change the WHERE-clause to
WHERE FIND_IN_SET('$sql_x', foo)=1
Your data structure in the DB is not optimal for querying the way you want it.
For the first question:
mysql_query("SELECT * FROM table WHERE array LIKE '%,$_GET[id],%' OR array LIKE '$_GET[id],%' OR array LIKE '%,$_GET[id]' OR array = '$_GET[id]'");
For the second:
mysql_query("SELECT id, SUBSTR(array, 1, POSITION(',' IN array) - 1) AS array FROM table WHERE array LIKE '$_GET[id],%' OR array = '$_GET[id]'");
As you can see, these queries aren't pretty, but they'll do what you want.
Untested, but you should be able to use:
Question 1:
SELECT * FROM table WHERE array REGEXP '(^|,)?(,|$)';
// Match either the start of the string, or a , then the query value, then either a , or the end of the string
Question 2:
SELECT * FROM table WHERE array REGEXP '^?(,|$)';
// Match the start of the string, then the query value, then either a , or the end of the string
Where ? is replaced with your $_GET value.
No idea on the performance of this.
I'd recommend you to bring your database to the first normal form, e. g.
CREATE TABLE t_master (
id INT PRIMARY KEY AUTO_INCREMENT
);
CREATE TABLE t_array (
id INT PRIMARY KEY AUTO_INCREMENT,
master_id INT NOT NULL,
value INT,
CONSTRAINT fk_array_master_id FOREIGN KEY (master_id) REFERENCES t_master (id)
);
Then you can find records in t_master that have a specific value with
$q = 'SELECT m.* ' .
'FROM t_master AS m INNER JOIN t_array AS a ON a.master_id = m.id ' .
"WHERE a.value = '" . mysql_real_escape_string($_GET['id'], $db) . "' " .
'GROUP BY m.id';
The most important advantage is that if you have a lot of values, you can add an index to find them much faster:
ALTER TABLE t_array ADD INDEX idx_value (value);
A less evident, but not the last advantage is that your queries become more logical and structured.
If you can't normalise your schema (which is the best option:
SELECT *
FROM table
WHERE ','+array+',' LIKE '%,$_GET[id],%'
But if you need to access the records by id, then you really should normalise
First One:
SELECT * FROM table WHERE array LIKE '$_GET[id],%' OR array LIKE '%,$_GET[id],%' OR array LIKE '%,$_GET[id]' OR array = '$_GET[id]
Second One:
SELECT * FROM table WHERE array LIKE '$_GET[id],%' OR array = '$_GET[id]
Explanation:
'$_GET[id],%' will match, if array is start with $_GET[id]
'%,$_GET[id],%' will match, if $_GET[id] is between any two of array items
'%,$_GET[id]' will match, if array is end with $_GET[id]
array = '$_GET[id]' match, if the array contains only one item equal to $_GET[id]

Categories