PHP/PDO Select from columns multiple conditions - mysql

I am trying to obtain results for a given member where status is pending or accepted doing the below:
$status1 = "Pending";
$status2 = "Attended";
$query = $conn->prepare('SELECT * FROM members WHERE member_id=:mID AND status=:status1 OR status=:status2');
$query->execute(array(':mID' => $mID,':status1' => $status1, ':status2' => $status2));
if ($query->rowCount() > 0) {
//start to create my table
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
//create variable, loop through and fill the table etc
}
}else{
echo "something";
}
This displays data - however, it even obtains results not specific to the member id (mID). Meaning other members data too!
I'm clearly missing something and or my query is wrong but struggling to find anything..
Any help appreciated.

You need to look at operator precedence for your database. You're doing this:
SELECT * FROM members WHERE member_id = :mID AND status = :status1 OR status = :status2;
Which most likely results in this:
SELECT * FROM members WHERE (member_id = :mID AND status = :status1) OR status = :status2;
But that's not what you want, so you will have to explicitly use parens like this:
SELECT * FROM members WHERE member_id = :mID AND (status = :status1 OR status = :status2);
Alternatively, you can use IN so that there's no OR:
SELECT * FROM members WHERE member_id = :mID AND status IN (:status1, :status2);

Related

MySQL optional filters for search query

I am working on a query that has an optional filter, so lets assume the table name is products and the filter is the id (primary key)
If the filter is not present I would do something like this:
SELECT * FROM products;
If the filter is present I would need to do something like this:
SELECT * FROM products WHERE id = ?;
I have found some potential solutions that can mix the 2 in sql rather than doing conditions in the back-end code itself
SELECT * FROM products WHERE id = IF(? = '', id, ?);
OR
SELECT * FROM products WHERE IF(? = '',1, id = ?);
I was just wondering which one would be faster (In the case of multiple filters or a very big table) Or is there a better solution to handle this kind of situation?
A better approach is to construct the WHERE clause from the parameters available. This allows the Optimizer to do a much better job.
$wheres = array();
// Add on each filter that the user specified:
if (! empty($col)) { $s = $db->db_res->real_escape_string($col);
$wheres[] = "collection = '$s'"; }
if (! empty($theme)) { $s = $db->db_res->real_escape_string($theme);
$wheres[] = "theme = '$s'"; }
if (! empty($city)) { $s = $db->db_res->real_escape_string($city);
$wheres[] = "city = '$s'"; }
if (! empty($tripday)) { $s = $db->db_res->real_escape_string($tripday);
$wheres[] = "tripday = '$s'"; }
// Prefix with WHERE (unless nothing specified):
$where = empty($wheres) ? '' :
'WHERE ' . implode(' AND ', $wheres);
// Use the WHERE clause in the query:
$sql = "SELECT ...
$where
...";
Simplest approach is OR:
SELECT *
FROM products
WHERE (? IS NULL OR id = ?);
Please note that as you will add more and more conditions with AND, generated plan will be at least poor. There is no fit-them-all solution. If possible you should build your query using conditional logic.
More info: The “Kitchen Sink” Procedure (SQL Server - but idea is the same)

How to convert this query to doctrine DQL

SELECT apntoken,deviceid,created
FROM `distribution_mobiletokens` as dm
WHERE userid='20'
and not exists (
select 1
from `distribution_mobiletokens`
where userid = '20'
and deviceid = dm.deviceid
and created > dm.created
)
What this query does is selects all mobiletokens where the user id is equal to 20 and the deviceid is the same but chooses the newest apntoken for the device.
My database looks like below.
For more information on this query, I got this answer from another question I asked here(How to group by in SQL by largest date (Order By a Group By))
Things I've Tried
$mobiletokens = $em->createQueryBuilder()
->select('u.id,company.id as companyid,user.id as userid,u.apntoken')
->from('AppBundle:MobileTokens', 'u')
->leftJoin('u.companyId', 'company')
->leftJoin('u.userId', 'user')
->where('u.status = 1 and user.id = :userid')
->setParameter('userid',(int)$jsondata['userid'])
->groupby('u.apntoken')
->getQuery()
->getResult();
//#JA - Get the list of all the apn tokens we need to send the message to.
foreach($mobiletokens as $tokenobject){
$deviceTokens[] = $tokenobject["apntoken"];
echo $tokenobject["apntoken"]."\n";
}
die();
This gives me the incorrect response of
63416A61F2FD47CC7B579CAEACB002CB00FACC3786A8991F329BB41B1208C4BA
9B25BBCC3F3D2232934D86A7BC72967A5546B250281FB750FFE645C8EB105AF6
latestone
Any help here is appreciated!
Other Information
Data with SELECT * FROM
Data after using the SQL I provided up top.
You could use a subselect created with the querybuilder as example:
public function selectNewAppToken($userId)
{
// get an ExpressionBuilder instance, so that you
$expr = $this->_em->getExpressionBuilder();
// create a subquery in order to take all address records for a specified user id
$sub = $this->_em->createQueryBuilder()
->select('a')
->from('AppBundle:MobileTokens', 'a')
->where('a.user = dm.id')
->andWhere('a.deviceid = dm.deviceid')
->andWhere($expr->gte('a.created','dm.created'));
$qb = $this->_em->createQueryBuilder()
->select('dm')
->from('AppBundle:MobileTokens', 'dm')
->where($expr->not($expr->exists($sub->getDQL())))
->andWhere('dm.user = :user_id')
->setParameter('user_id', $userId);
return $qb->getQuery()->getResult();
}
I did this for now as a temporary fix, not sure if this is best answer though.
$em = $this->em;
$connection = $em->getConnection();
$statement = $connection->prepare("
SELECT apntoken,deviceid,created
FROM `distribution_mobiletokens` as dm
WHERE userid=:userid
and not exists (
select 1
from `distribution_mobiletokens`
where userid = :userid
and deviceid = dm.deviceid
and created > dm.created
)");
$statement->bindValue('userid', $jsondata['userid']);
$statement->execute();
$mobiletokens = $statement->fetchAll();
//#JA - Get the list of all the apn tokens we need to send the message to.
foreach($mobiletokens as $tokenobject){
$deviceTokens[] = $tokenobject["apntoken"];
echo $tokenobject["apntoken"]."\n";
}

php codeigniter MySQL search query

I want to create a search query on MySQL database that will consist of 5 different strings typed in from user. I want to query 5 different table columns with these strings.
When I for example have input fields like:
first name, last name, address, post number, city.
How should I query the database that I dont always get all the rows.
My query is something like this:
SELECT user_id, username
from users
where
a like %?% AND
b like %?% AND
c like %?% AND
d like %?% AND
e like %?%;
When I exchange the AND for OR I always get all the results which makes sense, and when I use AND I get only the exact matches...
Is there any function or statement that would help me with this?
EDIT
The code I use is:
$sql = "select users.user_id, first_name
from users
inner join user_normal_aos
on users.user_id = user_normal_aos.user_id
inner join normal_areas_of_expertise
on user_normal_aos.normal_areas_of_expertise_id = normal_areas_of_expertise.normal_areas_of_expertise_id
where
users.first_name like ? AND
users.kanzlei like ? AND
normal_areas_of_expertise.normal_aoe like ? AND
users.postcode like ? AND
users.city like ?";
$query = $this->db->query($sql,
array(
'%'.$lawyer_name.'%',
'%'.$kanzlei.'%',
'%'.$area_of_expertise.'%',
'%'.$post_code.'%',
'%'.$city.'%')
);
For example use PHP to adjust your query based on what fields you have entered.
$where = array();
$replacements = array();
/* you can also compare if string is not null or not empty...
this is just example using isset */
if (isset($lawyer_name)) {
$where[] = 'users.first_name like ?';
$replacements[] = '%'.$lawyer_name.'%';
}
/* repeat this if again for all your fields .... */
$sql = "..... where ".implode(' AND ', $where);
$query = $this->db->query($sql,
$replacements
);

how can I connect these two tables in sql?

i need to take only the items from the table "__sobi2_item" that are in the same country of the user.And use this results for the rest of the function Showupdatelisting. This is my php script:
<?php
function showUpdatedListing()
{
//i found the user country field value...
global $database;
$user =& JFactory::getUser();
$userId = $user->get( 'id' );
$sql = "SELECT (id) FROM #__community_fields WHERE fieldcode= 'FIELD_COUNTRY'";
$database->setQuery( $sql );
$fieldID = $database->loadResult();
$sql = "SELECT (value) FROM #__community_fields_values WHERE field_id= {$fieldID} && user_id= {$userId}";
$database->setQuery( $sql );
$usercountry = $database->loadResult();
// From all the entries i take only ones that have country field like the user has...
$query = "SELECT `data_txt`, `itemid`, `fieldid` FROM `#__sobi2_fields_data` WHERE (`fieldid` = 6) AND ('data_txt' = {$usercountry})";
$database->setQuery($query);
$ResultsArray = $database->loadObjectList();
// We need something here like a Query to load only the entries from $ResultsArray... ??
//....instead of this...
$config =& sobi2Config::getInstance();
$database = $config->getDb();
$now = $config->getTimeAndDate();
$query = "SELECT itemid FROM #__sobi2_item WHERE (published = 1 AND publish_down > '{$now}' OR publish_down = '{$config->nullDate}') ORDER BY last_update DESC LIMIT 0, 30";
$database->setQuery($query);
$sids = $database->loadResultArray();
// ......... show update function goes on...
?>
can anyone help me to connect and adjust these query? thanks.
NB:with the last query (4) i need to filter items of the $ResultsArray taking only ones published and ordering them by last_update. i know it is wrong and now there is no connection with the query before. This is how i have tables in mysql:
_sobi2_fields_data:
itemid
fieldid
data_txt --->(is a description column for each field)
_sobi2_item:
itemid
published --->( 1 if true, 0 if false )
last_update --->(date of the last update for the item, also equal to the publication date if there are no changes)
thanks.
I don't know what you are trying to ask as well. Your last query (number 4) doesn't make sense to me, how is it linked to the above queries?
[EDIT] I've linked your 4th table above assuming itemid is the primary key for the items in sobi2_item table and that the value is linked to the sobi_fields_data table via itemid.
SELECT
cf.id,
sfd.data_txt,
sfd.itemid,
sfd.fieldid
FROM #__community_fields cf
INNER JOIN #__community_fields_values cfv ON cf.id=cfv.field_id
INNER JOIN #__sobi2_fields_data sfd ON cfv.value=sfd.data_txt
INNER JOIN #__sobi2_item si ON sfd.itemid=si.itemid
WHERE cf.fieldcode='FIELD_COUNTRY'
AND cfv.user_id=$userId
AND sfd.fieldid=6
AND si.published=1
AND (si.publish_down > '{$now}' OR si.publish_down = '{$config->nullDate}')
ORDER BY si.last_update DESC LIMIT 0, 30
Good luck!

How to combine sql select queries for this situation?

i'm writing a chat app with php/mysql
i have 3 tables: user, room and room_participant with these structures:
user: id, username
room: id, title
room_participant: room_id, user_id
Now i want to get list of all rooms along with list of all participants in each room.
Until now i just select all rooms from room table and iterate through all rooms and select users information out of each entry, which is very inefficient.
Is there any way to combine all these select into only 1 select query?
Not certain about this without testing, but give it a try:
SELECT
room.*,
user.*
FROM room
JOIN room_participant ON room_id = room_participant.id
JOIN user ON room_participant.user_id = user.id
ORDER BY room.id
To deduplicate rooms, use GROUP_CONCAT()
UPDATE GROUP_CONCAT() modified to return id|username
SELECT
room.id, room.name
GROUP_CONCAT(CONCAT(user.id,'|',user.username)) AS userlist
FROM room
JOIN room_participant ON room_id = room_participant.id
JOIN user ON room_participant.user_id = user.id
GROUP BY room.id, room.name
ORDER BY room.id
With the userlist generated by GROUP_CONCAT as id|name,id|name,id|name you can use PHP explode() to separate them.
// Split the list on the commas
$users = explode(",", $userlist);
$final_users = array();
// Then split each on the `|`
foreach ($users as $user) {
$split_user = explode("|", $user);
// Append each as a new associative array to $final_users
$final_users[] = array('id' => $split_user[0], 'username' => $split_user[1]);
}
// Now you have an array of users as id, username
var_dump($final_users);
You may wish to do two queries, and then match things up in whatever is using MySQL, e.g. PHP.
These two:
SELECT id, title
FROM room;
SELECT rp.room_id, rp.user_id, u.username
FROM room_participant AS rp
INNER JOIN user as u ON rp.user_id = u.id;
Or these two:
SELECT id, username
FROM user;
SELECT rp.room_id, rp.user_id, r.title
FROM room_participant AS rp
INNER JOIN room as r ON rp.user_id = r.id;
Which two queries make sense depends on what you're doing with the info really.
You could go a step further and select all three separately:
SELECT *
FROM room;
SELECT *
FROM user;
SELECT *
FROM room_participant;
Note: It's probably better to state the columns, rather than using '*', just in case in the future new columns are added to the table that you're not really interested in for these queries.
Obviously, you'd then have to match everything up in whatever is using MySQL, e.g. PHP. You could create a list of rooms and users from the selected info, and then match them up with something like:
// Use MySQL to populate $roomList from database, then do...
foreach ($roomList as $room)
{
$id = $room['id'];
$title = $room['title'];
$this->roomList[$id] = new Room($id, $title);
}
// Use MySQL to populate $userList from database, then do...
foreach ($userList as $user)
{
$id = $user['id'];
$username = $user['username'];
$this->userList[$id] = new User($id, $username);
}
// Use MySQL to populate $roomParticipantList from database, then do...
foreach ($roomParticipantList as $roomParticipant)
{
$room = $this->roomList[$roomParticipant['room_id']];
$user = $this->userList[$roomParticipant['user_id']];
// You could do one/both of these, depending on requirements.
$room->enterUser($user);
$user->joinRoom($room);
}