How to convert normal sql query to Zend_Db_Select? - php

Hi I want to convert my normal mysql query to zend.db.select;
I want to use this script:
$select = $db->select();
// Add a FROM clause
$select->from( ...specify table and columns... )
// Add a WHERE clause
$select->where( ...specify search criteria... )
// Add an ORDER BY clause
$select->order( ...specify sorting criteria... );
$select->limit(20, 10);
for my query below
SELECT
IF(derived_messages.toid = '$user', derived_messages.fromid,
derived_messages.toid) friend1,c.UserName,
derived_messages.message, derived_messages.fromid, derived_messages.toid,
derived_messages.is_read,derived_messages.type,derived_messages.id as mesid,
derived_messages.date,
(SELECT M.message_id FROM messagesmapped M where M.message_id= derived_messages.id AND M.user_id ='$user' AND M.important = 1) as MesMapid
FROM
(
SELECT *
FROM messages
WHERE messages.deleted_by NOT
IN ( $user )
ORDER BY Date DESC
) derived_messages
INNER JOIN Users c ON c.MemberID = IF(derived_messages.toid = '$user', derived_messages.fromid,
derived_messages.toid)
WHERE (derived_messages.id IN
(SELECT M.message_id FROM messagesmapped M where M.message_id= derived_messages.id AND M.user_id ='$user' AND M.important = 1)
AND
(derived_messages.toid='$user' OR derived_messages.fromid='$user'))
GROUP BY friend1 ASC
ORDER BY derived_messages.date DESC, derived_messages.id DESC LIMIT $limit $offset
I hope someone can help m on this.
Thank you.

It's possible but unlikely someone will write the query for you.
My recommendation on tackling such a query is to write each individual subquery as its own Zend_Db_Select object and then build the final query using the subqueries that you already have objects for.
Zend_Db_Select doesn't directly support the IF function, so for that you will need to use Zend_Db_Expr to add that statement into your select.
Here is a basic example of what I am talking about. Let's build the following query:
SELECT IF(msg.toId = 'drew010', msg.fromId, msg.toId), id, name, age, history.ip
FROM users
JOIN history ON users.id = history.userId
WHERE users.id = (
SELECT id FROM users WHERE loginCount > 1000
)
GROUP BY id,
ORDER BY age DESC
First build the subselect that select users where loginCount > 1000.
$subquery1 = $db->select()
->from('users', array('id'))
->where('loginCount > ?', 1000);
Next, build the outer query with the IF function:
$cols = array(
new Zend_Db_Expr('IF(' . $db->quoteInto('msg.toId = ?', 'drew010') . '), msg.fromId, msg.toId'),
'id', 'name', 'age'
);
$query = $db->select()
->from('users', $cols)
->join('history', 'users.id = history.userId', array('ip'))
->where('id = ?', $subquery1)
->group('id')
->order('age DESC');
echo $query;
The output:
SELECT
IF(msg.toId = 'drew010', msg.fromId, msg.toId),
`users`.`id`,
`users`.`name`,
`users`.`age`,
`history`.`ip`
FROM `users`
INNER JOIN `history`
ON users.id = history.userId
WHERE id = (
(SELECT `users`.`id`
FROM `users`
WHERE (loginCount > 1000))
)
GROUP BY `id`
ORDER BY `age` DESC
So the way to go is break the entire query into individual queries first, and then construct the outer query. Just have patience and take it slow. That and read over the Zend_Db_Select docs to get a full picture of what you have available to you.

Related

Codeigniter 2 multiple join and where statement

Updated code
$office = $this->session->userdata('department');
$query = "SELECT `doc`.`id`, `doc`.`barcode`, `doc`.`sub`, `doc`.`source_type`, `doc`.`sender`, `doc`.`address`, `doc`.`description`, `doc`.`receipient`, `doc`.`status`, DATE_FORMAT(`doc`.`datetime_added`, '%m/%d/%Y-%h:%i %p') as datetime_added,
(SELECT GROUP_CONCAT(`tag`) FROM `tags` WHERE `tags`.`documentId` = `doc`.`id` GROUP BY `tags`.`documentId`) as `tags`
FROM `documents` AS `doc`
JOIN `transactions` AS `trans` ON `doc`.`id` = `trans`.`document_id`
JOIN `trackers` AS `track` ON `doc`.`id` = `track`.`document_id`
WHERE `doc`.`status` = 'Processing'
AND `track`.`action` = '1')
AND `track`.`location` = '$office'
ORDER BY `doc`.`id` DESC";
$go = $this->db->query($query)->result_array();
var_dump($go); exit();
What I'm try to accomplish, is to display all documents located in our office that are Processing and has action 1. Documents may have tags like memo, request, finance, etc. The output is incorrect and not showing all the records that is in our office. I think there is a problem in WHERE clause? What could be the culprit in my code?
Update your query by removing the single quotes surrounding 1:
$query = "SELECT `doc`.`id`, `doc`.`barcode`, `doc`.`sub`, `doc`.`source_type`, `doc`.`sender`, `doc`.`address`, `doc`.`description`, `doc`.`receipient`, `doc`.`status`, DATE_FORMAT(`doc`.`datetime_added`, '%m/%d/%Y-%h:%i %p') as datetime_added,
(SELECT GROUP_CONCAT(`tag`) FROM `tags` WHERE `tags`.`documentId` = `doc`.`id` GROUP BY `tags`.`documentId`) as `tags`
FROM `documents` AS `doc`
JOIN `transactions` AS `trans` ON `doc`.`id` = `trans`.`document_id`
JOIN `trackers` AS `track` ON `doc`.`id` = `track`.`document_id`
WHERE `doc`.`status` = 'Processing'
**AND `track`.`action` = 1)**
AND `track`.`location` = '$office'
ORDER BY `doc`.`id` DESC";
$this->db->select("doc.id, doc.barcode, doc.sub, doc.source_type, doc.sender, doc.address, doc.description, doc.receipient, doc.status, DATE_FORMAT(doc.datetime_added, %m/%d/%Y-%h:%i %p) as datetime_added,
(SELECT GROUP_CONCAT(tag) FROM tags WHERE tags.documentId = doc.id GROUP BY tags.documentId) as tags")
->from("documents AS doc")
->join("transactions AS trans",'doc.id=trans.document_id')
->join("trackers AS track",'doc.id=track.document_id')
->where("doc.status","Processing")
->andWhere("track.action","1")
->andWhere("doc.id",$office)
->order_by("doc.id", "DESC");

Adding condition to mysql query and fetch again

I have 3 queries which I run which are nearly identical, the latter two have an AND condition.
Main query:
$mess = $mysqli->prepare("SELECT * from ( SELECT cm.id ,cm.userid,cm.message,cm.voteup,cm.votedown,cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ?
ORDER BY cm.date DESC LIMIT 30 ) ddd
ORDER BY date ASC ");
$mess->bind_param("i", $room);
$mess->execute();
$mess->store_result();
$mess->bind_result($chatid,$chat_userid,$message,$voteup,$votedown,$date);
while($row = $mess->fetch()){
//im fetching here in my <div class='div1' >
}
Then, in the second div I have to add an AND condition:
$mess2 = $mysqli->prepare("SELECT * from ( SELECT cm.id ,cm.userid,cm.message,cm.voteup,cm.votedown,cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ? AND voteup - votedown >= 5
ORDER BY cm.date DESC LIMIT 30 ) ddd
ORDER BY date ASC ");
$mess2->bind_param("i", $room);
$mess2->execute();
$mess2->store_result();
$mess2->bind_result($chatid,$chat_userid,$message,$voteup,$votedown,$date);
while($row2 = $mess2->fetch()){
//im fetching here in my <div class='div2' >
}
Lastly, in the third div I have a slightly different AND condition:
$mess3 = $mysqli->prepare("SELECT * from ( SELECT cm.id ,cm.userid,cm.message,cm.voteup,cm.votedown,cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ? AND votedown - voteup >= 5
ORDER BY cm.date DESC LIMIT 30 ) ddd
ORDER BY date ASC ");
$mess3->bind_param("i", $room);
$mess3->execute();
$mess3->store_result();
$mess3->bind_result($chatid,$chat_userid,$message,$voteup,$votedown,$date);
while($row3 = $mess3->fetch()){
//im fetching here in my <div class='div3' >
}
Everything works BUT doing this near-same query seems clumsy. Is it possible to construct the same thing with only one query? I have used $mess->data_seek(0); but its not helping because I didn't add my condition to the query.
Just go for PhP to filter your data instead of triple query your database. In this case you can figure out to go for this solution because you call 3 times your query with the same parameter :
$mess3 = $mysqli->prepare(" SELECT *
FROM ( SELECT cm.id ,
cm.userid,
cm.message,
cm.voteup,
cm.votedown,
cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ?
AND votedown - voteup >= 5
ORDER BY cm.date DESC LIMIT 30 ) ddd
ORDER BY date ASC ");
$mess3->bind_param("i", $room);
$mess3->execute();
$mess3->store_result();
$mess3->bind_result($chatid,$chat_userid ,$message,$voteup,$votedown ,$date);
while($row = $mess3->fetch()){
$voteup = $row['voteup'];
$votedown = $row['votedown'];
addToDiv1($row);
if( $voteup - $votedown >= 5 ) {
addToDiv2($row);
}
if( $votedown - $voteup >= 5 ) {
addToDiv3($row);
}
}
I will just give an answer based specifically on cleaning up your code. Technically you will still make the 3 calls in this scenario, but it will be cleaner because you include one function only, you don't see the script behind it.
As I mentioned, I am not an SQL aficionado so I can not give a good solution there (maybe you can use GROUP BY and perhaps an OR clause...I don't really know...). If I were to do this, I would do a function that can return all the options:
/core/functions/getChatMessages.php
function getChatMessages($settings,$mysqli)
{
$id = (!empty($settings['id']))? $settings['id'] : false;
$type = (!empty($settings['type']))? $settings['type'] : false;
$max = (!empty($settings['max']))? $settings['max'] : 30;
$mod = '';
// No id, just stop
if(!is_numeric($id))
return false;
// equation one
if($type == 'up')
$mod = ' AND voteup - votedown >= 5';
// equation two
elseif($type == 'down')
$mod = ' AND votedown - voteup >= 5';
$mess = $mysqli->prepare("SELECT * from ( SELECT cm.id ,cm.userid,cm.message,cm.voteup,cm.votedown,cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ? {$mod}
ORDER BY cm.date DESC LIMIT {$max} ) ddd
ORDER BY date ASC");
$mess->bind_param("i", $id);
$mess->execute();
$mess->store_result();
$mess->bind_result($chatid, $chat_userid, $message, $voteup, $votedown, $date);
while($mess->fetch()){
$result[] = array(
'chatid'=>$chatid,
'chat_userid'=>$chat_userid,
'message'=>$message,
'voteup'=>$voteup,
'votedown'=>$votedown
);
}
// Send back the data
return (!empty($result))? $result : array();
}
To use:
// Include our handy function
require_once('/core/functions/getChatMessages.php');
// Store our id for use
$settings['id'] = 100;
// Should get 30 from first select
$voteGen = getChatMessages($settings,$mysqli);
// Should get 30 from second select
$settings['type'] = 'up';
$voteUp = getChatMessages($settings,$mysqli);
// Should get 15 from third select
// Just for the heck of it, I added in a limit settings
$settings['max'] = 15;
$settings['type'] = 'down';
$voteDown = getChatMessages($settings,$mysqli);
Now that you have these stored, just use a foreach loop to place them into your view. The good side of this is that you can call this where ever and when ever since the function only returns data. It allows you to work with the data in a view or non-view situation. Side note, I use PDO, so if there is something ineffective with the way the mysqli is fetching, that will be why. It's probably just best to fetch an assoc array to return...

SQL query empty field, select ALL option like *

A php search page uses an SQL query to search for properties based on set criteria, in the example below the street "Harrison Road" is chosen:
search.php?city=1&rooms=3&rooms_upper=7&price_lower=55&price_upper=100&streetname=Harrison%20Road
I am looking for a way to leave the streetname blank and still SELECT ALL records as below:
search.php?city=1&rooms=3&rooms_upper=7&price_lower=55&price_upper=100&streetname=
I tried using the ' * ' symbol as with other SQL ocmmands to specify SELECT ALL but it does not work in this instance.
The problem is the search does not display any results while the street option is left blank.
The reason I am looking to run a search with an empty street criteria is because the search.php loads before the user selects a particular street using the drop down option on the page.
I would like it to SEARCH ALL records using the first criteria specified:
rooms, rooms_upper, price_lower, price_upper
The standard search page load has been left with the widest search criteria possible ( 3 < rooms < 7 ) and (£55 < rent < £75) in order to display ALL records before the user narrows the search criteria specifying a particular 'streetname' if desired.
Many Thanks!
Jeanclaude
The full SQL is here:
$sql=mysql_query("SELECT main_table.housenumber, main_table.housenumber, main_table.streetname,
max(main_table.rent) AS reviews_rent, main_table.rooms AS reviews_rooms,main_table.average,
houses.houseID, houses.path, houses.rooms AS houses_rooms,houses.rent AS houses_rent
FROM main_table
LEFT JOIN houses ON main_table.housenumber = houses.housenumber
AND main_table.streetname = houses.streetname
WHERE main_table.streetname='$page_streetname'
AND main_table.city=$city
AND main_table.verify='1'
AND main_table.rooms>='$rooms'
AND main_table.rooms<='$rooms_upper'
AND main_table.rent>=$price_lower
AND main_table.rent<=$price_upper
GROUP BY main_table.housenumber, main_table.streetname
ORDER BY average DESC, houseID DESC, reviewID DESC;");
I want to keep streetname in the WHERE clause, but I don't want to restrict the search if it is left blank.
How about creating the query dynamically.
Set up the base query without the streetname in the WHERE clause but include a sprintf string tag.
Then if $page_streetname has a value add the streetname selection to the query dynamically or if not just add nothing.
$q = "
SELECT main_table.housenumber,
main_table.streetname,
Max(main_table.rent) AS reviews_rent,
main_table.rooms AS reviews_rooms,
main_table.average,
houses.houseid,
houses.path,
houses.rooms AS houses_rooms,
houses.rent AS houses_rent
FROM main_table
LEFT JOIN houses
ON main_table.housenumber = houses.housenumber
AND main_table.streetname = houses.streetname
WHERE %s
main_table.city = $city
AND main_table.verify = '1'
AND main_table.rooms >= '$rooms'
AND main_table.rooms <= '$rooms_upper'
AND main_table.rent >=$ price_lower
AND main_table.rent <=$ price_upper
GROUP BY main_table.housenumber,
main_table.streetname
ORDER BY average DESC,
houseid DESC,
reviewid DESC";
$q = isset($page_streetname)
? sprintf( $q, "main_table.streetname = '$page_streetname' AND " )
: sprintf( $q, '');
That's easily solved with PHP (no need for sprintf()):
<?php
$page_streetname = empty($page_streetname)
? null
: "AND main_table.streetname = '{$page_streetname}'"
;
$result = mysql_query(
"SELECT
main_table.housenumber,
main_table.housenumber,
main_table.streetname,
max(main_table.rent) AS reviews_rent,
main_table.rooms AS reviews_rooms,
main_table.average,
houses.houseID,
houses.path,
houses.rooms AS houses_rooms,
houses.rent AS houses_rent
FROM main_table
LEFT JOIN houses
ON main_table.housenumber = houses.housenumber
AND main_table.streetname = houses.streetname
WHERE
main_table.city = '{$city}'
AND main_table.verify = 1
AND main_table.rooms >= '{$rooms}'
AND main_table.rooms <= '{$rooms_upper}'
AND main_table.rent >= {$price_lower}
AND main_table.rent <= {$price_upper}
{$page_streetname}
GROUP BY
main_table.housenumber,
main_table.streetname
ORDER BY
average DESC,
houseID DESC,
reviewID DESC"
);
?>

Convert complex JOIN query in codeigniter

I'm converting my existing website to CI, and I have been trying for several days to convert this query to CI-friendly code:
$result = mysql_query("
SELECT t1.mnumber, t1.mcontent, t1.mcontact
FROM sms t1
JOIN (
SELECT mContent,mcontact, mnumber, MAX(mID) mID
FROM sms
GROUP BY mContact
) t2 ON t1.mcontact = t2.mcontact AND t1.mid = t2.mid
GROUP BY t1.mContact
ORDER BY t1.mid DESC
");
But no matter what I try, I can't get the correct result on CI.
I hope you guys can help me out here!
The closest to a result that i did get, was when i used the subquery hack.
However, out of frustration i deleted the block of code and kept trying.
I decided to use a flat query, like the one posted above. This almost gives me results.
$query = $this->db->query("SELECT t1.mnumber, t1.mcontent, t1.mcontact FROM sms t1
JOIN (SELECT mContent,mcontact, mnumber, MAX(mID) mID FROM sms GROUP BY mContact) t2
ON t1.mcontact = t2.mcontact AND t1.mid = t2.mid GROUP BY t1.mContact ORDER BY t1.mid DESC");
$contacts = array();
//Add data to our array
foreach($query->result() as $row){
echo $row->mNumber;
}
return $contacts;
However, in my view i get the notice "Message: Undefined property: stdClass::$mNumber"
So still no results, plus i prefer the CI query method.
You can use it in codeigniter like this
$query = " SELECT t1.mnumber, t1.mcontent, t1.mcontact
FROM sms t1
JOIN (
SELECT mContent,mcontact, mnumber, MAX(mID) mID
FROM sms
GROUP BY mContact
) t2 ON t1.mcontact = t2.mcontact AND t1.mid = t2.mid
GROUP BY t1.mContact
ORDER BY t1.mid DESC";
$result = $this->db->query($query);
return $result->result();
2nd Method
You can use sub query way of codeigniter to do this for this purpose you
will have to hack codeigniter. like this. Go to system/database/DB_active_rec.php
Remove public or protected keyword from these functions
public function _compile_select($select_override = FALSE)
public function _reset_select()
Now subquery writing in available And now here is your query with active record
$select = array(
'mContent',
'mcontact',
'mnumber',
'MAX(mID) mID'
);
$this->db->select($select);
$this->db->from('sms');
$this->db->group_by('mContact');
$subquery = $this->db->_compile_select(); // get the query string
$this->db->_reset_select(); // reset so it can newly form the query
unset($select);
$select = array(
't1.mnumber',
't1.mcontent',
't1.mcontact'
);
$this->db->select($select);
$this->db->join('',"($subquery)");
$this->db->from('sms t1');
$this->db->group_by('t1.mContact');
$this->db->order_by('t1.mid','DESC');
$result = $this->db->get();
return $result->result();
And the thing is done. Cheers!!!
Note : While using sub queries you must use
$this->db->from('myTable')
instead of
$this->db->get('myTable')
which runs the query.
Er, shouldn't that be (at a guess)...
SELECT t1.mnumber
, t1.mcontent
, t1.mcontact
, t1.mid
FROM sms t1
JOIN
( SELECT mcontact
, MAX(mID) mID
FROM sms
GROUP BY mContact
) t2
ON t1.mcontact = t2.mcontact
AND t1.mid = t2.mid
ORDER
BY t1.mid DESC;
This may not fully answer your question, but for those of you who are Googling for a "JOIN AND" (so that you can AND some extra stuff on the Join, before the WHERE clause kicks in), here's a hacky way to do it:
$this->db->join('table_to_join', 'first_table.connect_id = table_to_join.id AND table_to_join.some_filter != 0');
Definitely not pretty, but that obviously forces the AND to occur immediately after the JOIN, before you get into the WHERE stuff for the rest of your query on first_table
Well, its easy to implement in CI version 3+
$subQuery = $this->db->select( 'table2.*' )
->join( 'table3', 'table3.orderId = table2.fkOrderId', 'INNER' )
->where()->get_compiled_select( 'table2' );
$this->db->select( 'table1.*' )
->join( '(' . $subQuery . ') AS b', 'b.`fkCouponId` = table1.couponId', 'LEFT', FALSE )
->get( 'table1' )
TIP: Make sure you don't use any query builder methods above the subquery part.

How to use OR multiple times in a Where statement?

I want to do something like "Where conditions = '$condition' OR conditions = '$condition2' OR conditions = '$condition3' but that returns all entries in the database versus the ones i am looking for.. what i can i do to make this work?
$sql = "SELECT STRAIGHT_JOIN DISTINCT table1.person_id, table1.full_name,
table2.stuff_id, table2.stuff_name, table3.whatever_why,
table3.whatever_comments, table3.whatever_test_id
from table1
join table4 on table1.person_id = table4.person_id
join table2 on table4.stuff_id = table2.stuff_id
join table3 on table1.person_id = table3.person_id
WHERE conditions = '$condition' and ( (
table3.whatever_why like '%$q%'
OR table3.whatever_comments like '%$q%'
OR table3.whatever_name like '%$q%'
))
order by table3.id DESC limit $startrow, 10";
$result = mysql_query($sql);
you may use parenthesis to group up your logic.
just continuing to list OR statements will match the rows if any of those are true.
Where conditions in ('$condition1', '$condition2', '$condition3')

Categories