Match Where Multiple Columns are... PHP PDO - php

I have a search query which works, but not the way I had hope. Here:
try{
/*
Create Search Query.
*/
//Include database config file.
include('config.php');
//Get values from Angular.
$valuesPost=$_POST;
$values=array();
foreach ($valuesPost as $rows) {
$decodedValues=json_decode($rows, TRUE);
$values[]=$decodedValues;
}
//Get table from post values.
$table=$values[0]["table"];
//Get limit from post values.
$limit=$values[0]["limit"];
//Get offset from post values.
$offset=$values[0]["offset"];
//Get orderBy from post values.
$orderBy=$values[0]["orderBy"];
//Unset Whole array
unset($values[0]);
//Create cats fields
$cats=array_keys($values[1]);
if(empty($cats)) {
$cats = null;
} else {
foreach($cats as &$val){
$val="cat_id = :".$val;
}
$cats=implode(" OR ", $cats);
}
//Create subCats fields
$subCats=array_keys($values[2]);
if(empty($values[2])) {
$subCats[0] = null;
} else {
foreach($subCats as &$val){
$val="sub_cat_id = :".$val;
}
$subCats=implode(" OR ", $subCats);
}
//Create colourCats fields
$colourCats=array_keys($values[3]);
if(empty($colourCats)) {
$colourCats[0] = null;
} else {
foreach($colourCats as &$val){
$val="colour_id = :".$val;
}
$colourCats=implode(" OR ", $colourCats);
}
$where = "";
//Create Where Statement
if(empty($cats[0]) && empty($subCats[0])){
$where = $colourCats;
}
if(empty($cats[0]) && empty($colourCats[0])){
$where = $subCats;
}
if(empty($subCats[0]) && empty($colourCats[0])){
$where = $cats;
}
if(empty($colourCats[0]) && !empty($cats[0]) && !empty($subCats[0])){
$where = $cats." AND ".$subCats;
}
if(empty($subCats[0]) && !empty($cats[0]) && !empty($colourCats[0])){
$where = $cats." AND ".$colourCats;
}
if(empty($cats[0]) && !empty($subCats[0]) && !empty($colourCats[0])){
$where = $subCats." AND ".$colourCats;
}
if(!empty($cats[0]) && !empty($subCats[0]) && !empty($colourCats[0])){
$where = $cats." AND ".$subCats." AND ".$colourCats;
}
//Search query.
$search="SELECT * FROM $table WHERE $where ORDER BY $orderBy LIMIT $limit OFFSET $offset";
/*
Database Connection.
*/
//Crate a database connection variable: $conn and error checking attributes.
$conn = new PDO($DB_SETTINGS, $DB_USER, $DB_PASS);
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
/*
PDO WORK.
*/
//SEARCH_LIKE_ALL QUERY!
//Prepare query.
$search_qry=$conn->prepare($search);
//For each array index create array $rows.
foreach ($values as $rows) {
//Bind each value to $value_fields from $rows array.
foreach ($rows as $key => &$value) {
switch(gettype($value)) {
case 'integer':
case 'double':
$search_qry->bindParam(':' . $key, $value, PDO::PARAM_INT);
break;
default:
$search_qry->bindParam(':' . $key, $value, PDO::PARAM_STR);
}
}
}
$search_qry->execute();
$rows = $search_qry->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($rows);
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
It builds the query fine. In my angularjs script I build a multidimensional array where index [0] contains data such as table, orderby and limits. Index [1] contains an array of Categories. Index[2] an array of Sub Categories and Index[3] an array of Colour Categories. My problem is that the search query is bring back data I am not expecting. So lets say I send this type of array:
values[0]={table: table, order_by: prod_code, limit: 10, offset 0}
values[1]={cat_id0: 1};
values[2]={sub_cat_id0: 1, sub_cat_id1: 3}
values[3]={colour_id0: 1, colour_id1: 2}
I want only products in cat 1 with sub cats 2 & 3 and a colour id 1 & 2. It is changing order when I remove a sub cat id from the array but is still in search, I assume this product is later being picked up by a colour id. Is the query wrong or the way in which I am searching.
From the array above:
$search="SELECT * FROM table WHERE cat_id=:cat_id0 AND sub_cat_id=:sub_cat_id0 OR sub_cat_id=:sub_cat_id1 AND colour_id=:colour_id0 OR colour_id=:colour_id1 ORDER BY prod_code LIMIT 10 OFFSET 0"

I think the big problem you have is the order of precedence between AND and OR.
SELECT a
, b
, c
, a OR b AND c
, (a OR b) AND c
FROM ( SELECT 1 AS a UNION SELECT 0 ) a
CROSS
JOIN ( SELECT 1 AS b UNION SELECT 0 ) b
CROSS
JOIN ( SELECT 1 AS c UNION SELECT 0 ) c
WHERE NOT (a.a = b.b AND a.a = c.c)
ORDER BY c, b, a
a b c a OR b AND c (a OR b) AND c
------ ------ ------ ------------ --------------
1 0 0 1 0
0 1 0 0 0
1 1 0 1 0
0 0 1 0 0
1 0 1 1 1
0 1 1 1 1
That is to say
foo = a OR foo = b AND bar = c
is evaluated as:
( foo = a ) OR ( foo = b AND bar = c )
when what you probably want is:
(foo = a OR foo = b ) AND ( bar = c )
I'm having trouble deciphering what the code is doing.
For example, why do you need all those permutations of conditions in building the WHERE clause? Why all those checks? Why not something simpler, like:
$where = " 1=1";
if (!empty($cats[0]) {
$where .= " AND (" . $cats . ")";
}
if (!empty($subCats[0]) {
$where .= " AND (" . $subCats . ")";
}
if (!empty($colourCats[0]) {
$where .= " AND (" . $colourCats . ")";
}
Also, all those OR conditions testing equality
foo = a OR foo = b OR foo = c OR foo = d
Can be expressed much more elegantly using an IN
foo IN (a,b,c,d)
//Create cats fields
$cats=array_keys($values[1]);
if(empty($cats)) {
$cats = null;
} else {
$cats=" cat_id IN (". implode(",",$cats) . ");
}

Related

PHP - Form fields that create MYSQL Queries on submit

can anyone help me with how can I achieve form fields that create MySQL queries? below are the details
Table name: books
id
user_id
cateory_id
name
1
2
1
Book name 1
1
4
4
Book name 2
and in my HTML view this is how the form looks like
https://imgur.com/Sy0NSf7
The Column field drop down values are the table field names: id, user_id, category_id, name
The Comparison drop down values are: '=', '<', '>', 'contains'
'contains' value is like 'LIKE' this must be change on the backend
and the Value input field is just a text field where you put what you are looking for
This form is dynamic where you can add filter with the radio button condition value of 'or' or 'and'
so my goal is when I click the submit button it will create mysql query with LEFT JOIN?I'm not really sure what I'm supposed to do.
Sample output:
id
user_id
cateory_id
name
1
James
Fiction
Book name 1
1
John
Non-Fiction
Book name 2
below is my code to create mysql query but I think this is wrong because I have a user_id and category_id which I need to append the JOIN
$data = $request->getParsedBody();
$query = "SELECT * FROM books WHERE ";
foreach ($data['column'] as $key => $value) {
if ($data['comparison'][$key] == 'contains')
{
$data['comparison'][$key] = 'LIKE';
$data['value'][$key] = "'%" . $data['value'][$key] . "%'";
}
if ($key <= 0)
{
$query .= "{$value} {$data['comparison'][$key]} '{$data['value'][$key]}'";
}
if ($key > 0) {
if (!$value == "" && !$data['comparison'][$key] == "" && !$data['value'] == "")
{
$query .= " {$data['and_or'][$key-1]} {$value} {$data['comparison'][$key]} '{$data['value'][$key]}'";
}
}
}
$query .= " ORDER BY id DESC";
sorry for my bad english.
As there are only 2 additional tablex we can hardcode them in the query, using a LEFT JOIN so that we will always get any matching data from books.
The following example uses one value for each possible search field. You could construct your query string dynamically using the same logic
<?php
/* We are not prepared prepared to take any risks with sql injection */
if ("$data['comparison1'][$key]" == "AND") { andor[1] = AND } else { andor[1] = "OR"};
if ("$data['comparison2'][$key]" == "AND") { andor[2] = AND } else { andor[2] = "OR"};
if ("$data['comparison3'][$key]" == "AND" ){ andor[3] = AND } else { andor[3] = "OR"};
/* prepare the query using string concatenation with the the values in andor[] */
$query = "SELECT b.id, u.user_id, c.category_id, b.name
FROM books b
LEFT JOIN categories c ON b.id = c.id
LEFT JOIN users u ON b.id = u.id
WHERE
( b.id = COALESCE(:book_id , b.id)"
.andor[1].
"( b.name = COALESCE(:book_name , b.name)"
.andor[2].
"( c.category_id = COALESCE(:category , c.category_id)"
.andor[3].
"( u.user_id = COALESCE(:user_id , u.user_id);";
/* Here you need to prepare the database connecter $dbh */
$host = 'localhost';
$db = 'bookdb';
$user = 'root';
$password = 'S#cr#t1!';
$dsn = "mysql:host=$host;dbname=$db;charset=UTF8";
/* and connect to the database */
try {
$pdo = new PDO($dsn, $user, $password);
if ($pdo) {
echo "Connected to the $db database successfully!";
}
} catch (PDOException $e) {
echo $e->getMessage();
}
/* we prepare the query and load the variables */
$sth = $pdo->prepare($query);
$sth->bindValue(':bookname', "$data['book_id'][$key]");
$sth->bindValue(':book_name', "$data['book_name'][$key]"); ;
$sth->bindValue(':category', "$data['category'][$key]");
$sth->bindValue(':user_id', "$data['user_id'][$key]");
/* here we execute the query and extract the results*/
$sth->execute();
$book_results = $sth->fetchAll();
/* now you can exploit the table $book_results
here we simply print it so that you can check */
print_r( $book_results);
?>

check for multiple values count

i need to check if in an exam that contains 5 students
exist 3 students from the same class.
here is what i tried
<?
//this array contains all student id's that are in an exam
$exam = array('s1' => $s1, 's2' => $s2, 's3' => $s3, 's4' => $s4, 's5' => $s5);
$values = implode(", ", $exam);
$sql = "SELECT class FROM students WHERE students.id IN (" . $values . ")";
try{
$db = new db();
$db = $db->connect();
$stmt = $db->query($sql);
$studs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$db = null;
if(!empty($studs)) {
//check if 3 students from the same class are taking the exam
$i = 0; $s = 0;
foreach($exam as $e )
{
if( !in_array( $e, $studs[$i] ) )
{
$exist = FALSE;
}
else {$s++;}
$i++;
}
if ($s<=3) {
#do sth
}
else {
echo "more than 3 students";
}
} else {
echo "error";
}
} catch(PDOException $e) {}
?>
Problem
what i am not sure about is how to count that 3 students have the same class id in this exam array.
i know there is something i need to fix in my foreach just trying with no success.
You can ask your database to return all classes with 3 or more students from your id list by applying an an aggregate snd grouping your results by the class ánd using a HAVING clause:
SELECT class, COUNT(id) as num_students_in_class FROM students WHERE id IN (1,2,3,4) GROUP BY class HAVING COUNT(id) >= 3
More info:
How to use group by in SQL Server query?
What's the difference between HAVING and WHERE?
If you dont want to query as suggested in https://stackoverflow.com/a/46517195/100809 you’ll need to keep an assoc array of the classes and the number of times you’ve seen it:
$seenClasses = array_count_values($studs);
foreach($seenClasses as $class => $numStudents) {
if ($numStudents > 4)
echo “class $class has $numStudents”;
}

Using the && / AND operator for an array

This code checks if $value (array) is set in the database as 1. The foreach loop saves all the founding matches into the array $products. Now I have all the fields in the database which have the value 1 but that's not really what I want, because if the field 'dog' equals 1 and the field 'cat' equals NULL, I still have the value of 'dog' in my array. It only should get to the array when BOTH equal 1.
For simple variables you can use the && or AND operator to check if all keys have the same value but how do I do this with an array?
$products = array();
foreach($_POST['selected_checkboxes'] as $value) {
var_dump($value);
if($result = $db->query("SELECT * FROM produkte WHERE `$value` = 1")){
while($row = $result->fetch_object()) {
if (!in_array($row->name, $products)) {
array_push( $products, array('name'=>$row->name, 'image'=>$row->image, 'link'=>$row->link) );
}
}
}
else {
array_push($products, 'error');
}
}
Change your SQL statement such that all checked values are equal to 1.
You can append a " = 1 AND " to each value then use them in your query.
<?php
$arr = ['field1', 'field2', 'field3'];
$condition = join(' = 1 AND ', $arr) . ' = 1';
The output would be "field1 = 1 AND field2 = 1 AND field3 = 1". You can then use $condition in your query.
$db->query("SELECT * FROM produkte WHERE $condition")
UPDATE:
To cope with fieldname containing spaces you would need to wrap each fieldname with backticks so change this line
$condition = '`' . join('` = 1 AND `', $arr) . '` = 1';

Mysql result to limit output

I have a problem realizing some output to echo a list of results that comes from an Array.
I would like to create a Live search engine that runs a query by the help of keyup-function by using AJAX.
Everything works fine when the output will be echoed for every match that is listed in the table.
Now I would like to to combine all entries that are duplicates.
The code is like:
$search_term = $_POST['search_term'];
$where = "";
$search_term = preg_split('/[\s]+/', $search_term, -1, PREG_SPLIT_NO_EMPTY);
$total_search_terms = count($search_term);
$total_search_term = 0;
foreach ($search_term as $key=>$value) {
$total_search_term = $total_search_term + 1;
if ($total_search_term === 1){
if (is_numeric($value) ){
$where .= "(`a` LIKE '%$value%')";
} else {
$where .= "(`b` LIKE '%$value%')";
}
}else if ($total_search_term > 1){
$where .= " AND ";
if (is_numeric($value) ){
$where .= "(`a` LIKE '%$value%')";
} else {
$where .= "(`b` LIKE '%$value%')";
}
}
}
$duplicate = $db->query("SELECT a, b, COUNT(*) counter
FROM `table`
GROUP BY a, b
HAVING COUNT(*) > 1
");
$check = $duplicate->fetch_assoc();
$query = $db->query("SELECT a, b FROM table WHERE $where");
$result = $query->num_rows;
if ($result !== 0 ){
echo '<li id="hit">There are $result results!</li>';
while ($row = $query->fetch_assoc() ) {
echo '<li>',
$row["a"],
' ',
$row["b"],
'</li>';
}
} else {
echo '<li id="hit">no result!</li>';
}
To give an example of the output:
There are 3 results!
12345 New
12345 New
56789 Chicago
And thats how it should be:
There are 3 results!
12345 New (2x)
56789 Chicago
So the table is:
a | b
12345 New
12345 New
56789 Chicago
Thanks alot.
I thought of something like this:
$query = $db->query("SELECT a, b, COUNT(*) counter FROM `table` WHERE ".$where." GROUP BY a, b");
$result = $query->num_rows;
if ($result !== 0 ){
$resultSizeQuery = $db->query("SELECT COUNT(*) counter FROM `table` WHERE ".$where);
$resultSize = $resultSizeQuery->fetch_assoc();
echo '<li id="hit">There are '.$resultSize["counter"].' results!</li>';
while ($row = $query->fetch_assoc() ) {
echo '<li>'.$row["a"].' '.$row["b"];
echo ($row["counter"] > 1 ? " (".$row["counter"]."x)" : "");
echo '</li>';
}
} else {
echo '<li id="hit">no result!</li>';
}
Replacing all lines from "$duplicates = ..." to the end it should do it's work. Just give it a try because sometimes the step before the problem should be thought over.
Regards
parascus
first of all, your statement will return just 1 line with New York and the column counter will have 2. Chicago is missing because counter is just 1.
So I think your result looks like:
Ther are 1 results!
12345 New York
If you want to have "3 results" jsut do 2 queries, one for the number of rows (just leave out the group and having clause, also don't ask for a and b).
So you get the output:
There are 3 results!
Next you have to omit the having clause for getting all rows (also those without duplicates). You could write something like:
echo ($row["a"].' '.$row["b"].($row["counter"] > 1 ? " (".$row["counter"]."x)" : "")
I hope this helps.
Regards
Parascus

Rank points based off the highest number in a row

I have a table called "member_points", which is set up like this:
|----|-------|----------|------------------------|
| id | uid | points | last_loggedin |
|----|-------|----------|------------------------|
| 1 | 1 | 5075 | 2012-08-02 02:04:00 |
|----|-------|----------|------------------------|
| 2 | 2 | 2026 | 2012-08-04 02:15:02 |
|----|-------|----------|------------------------|
I have a function below. I want the function to echo or return the rank like "Ranked #1" or "Ranked #2", based on the number in the row "points".
function getTopRanks($id) {
$sql = "SELECT MAX(points) AS toppoints FROM member_points";
$r = mysql_query($sql);
if ( $r !== false && mysql_num_rows($r) > 0 ) {
while ( $a = mysql_fetch_assoc($r) ) {
$points = stripslashes($a['toppoints']);
return ''.$points.'';
}
}
}
Can someone help me make this possible?
I think you are going to ranking user on the basis of points.
For such type of problem, i suggest you to first rank user on DESC order. Then pickup desire value form row.
function getTopRanks($id) {
$sql = "SELECT uid FROM member_points ORDER BY points DESC ";
$r = mysql_query($sql);
if ( $r !== false && mysql_num_rows($r) > 0 ) {
while ( $a = mysql_fetch_assoc($r) ) {
$points = stripslashes($a['toppoints']);
return ''.$points.'';
}
}
}
this will solve your problem.:)
UPDATE AS YOUR REQUIREMENTS:
function getTopRanksUser($id) {
$userPos ="Ranked position is:";
$count = 0;
$sql = "SELECT uid FROM member_points ORDER BY points DESC ";
$r = mysql_query($sql);
if ( $r !== false && mysql_num_rows($r) > 0 ) {
while ( $a = mysql_fetch_assoc($r) ) {
$count = $count+1;
$userPosCount = $userPos+$count;
return $userPosCount;
}
}
}
there should be return userPos+count. because count is increases up number of rows in table and the string ranked position is always remains same.
this will give result. Your can change return string according to your requirements. :)
thanks.
In your mysql query use ORDER BY (http://dev.mysql.com/doc/refman/5.0/en/sorting-rows.html)
so your query would become
SELECT points FROM member_points ORDER BY points DESC
this will sort the results from the query by the amount of points. (DESC will make them descending and ASC will make the result ascending).
Here's what I use for my rankings ... this code also takes into account ties and displays correctly. Just change "username" and "points" to your appropriate column names in your db and set the db connection vars: $hostName $databaseNanme $username and $password for you connection.
Hope this helps!
$sql1 = "SET #rnk=0; SET #rank=0; SET #scount=0;";
$sql2 = "SELECT username, points, rank FROM
(
SELECT AA.*,BB.username, points
(#rnk:=#rnk+1) rnk,
(#rank:=IF(#scount=points,#rank,#rnk)) rank,
(#scount:=points) newscount
FROM
(
SELECT * FROM
(SELECT COUNT(1) scorecount,points
FROM users GROUP BY points
) AAA
ORDER BY points DESC
) AA LEFT JOIN users BB USING (points)) A;";
try {
$conn = new PDO('mysql:host='.$hostName.';dbname='.$databaseName, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->query($sql1);
$data = $conn->query($sql2);
$standings = $data->fetchAll(PDO::FETCH_ASSOC);
} catch(PDOException $e) {
error_log( 'ERROR: ' . $e->getMessage() );
}
print_r($standings);

Categories