I have reached dead end with the brain o.O. In DB I have two tables:
store_module->caffe_id, module_id, position, order
module->id_module, name, description, image
I have query where I take all modules for set ID (store_module table), and I need to get all modules which appear in this query (module_id). What I need to do?
This is the code (I am awake for 30+ hours and my brain is refusing to communicate with me, deadline is almost here, and this on of the last things I need to do. So, please help :D):
function mar_get_modules($id){
$q = $this->db->get_where('store_module', array('caffe_id' => $id));
$modules = $q->result_array();
}
Start simple, by using a regular query (if I guess right, you need a JOIN there).
This query should work:
$sql = "SELECT m.*,sm.* FROM module m
LEFT JOIN store_module sm ON sm.id_module = m.module_id
WHERE sm.caffe_id = ?";
return $this->db->query($sql, array($id))->result_array();
Now, you can transform it into an AR query:
$query = $this->db->select('module.*,store_module.*')
->from('module')
->join('store_module', 'store_module.id_module = module.module_id','left')
->where('store_module.caffe_id',$id)
->get();
return $query->result_array();
While AR is quicker sometimes, I usually prefer writing my queries "by hand", taking advantage of the binding to prevent SQL injections; it's a lot easier to see how things are working if you have a query fully laid under your eyes
Sasha,
In the function above, you are not returning anything. You'll need to update the 3rd line something to the effect of return $q->result_array();
Related
I have an entity “coin” linked with a oneToMany on prices. Prices is updated every minute and has millions of entries.
What I’m trying to achieve is a DQL query that will grab the last price to date.
Right now I see 2 ways to do it and I’m wondering which one is best in term of performance:
I could look for prices in the DB with ‘prices’.’lastupdated’ equal to the last update.
OR I could grab the last 100 price ids (we update 100 coins every minute and add 100 new rows in the Database) with a LIMIT 100 and ORDER BY ‘id’ DESC on my left join.
I know it’s tricky to use LIMIT on a LEFT JOIN with doctrine but I found a solution here:
How to limit results of a left-join with Doctrine (DQL)
and here: https://www.colinodell.com/blog/201703/limiting-subqueries-doctrine-2-dql
I’m wondering what will take the least amount of ressources to execute that query.
Of course I’m using getArrayResults() and am using partials and doctrine cache.
What is your opinion on that?
Thanks!
I've been in similar situations. For example, I run a social commerce network and want to get all follower_ids from a business to update them that an action has been performed. It's the same if you want liker_ids, etc.
In this scenario, you are only interested in a value from one column (price for you) but based off queries involving different fields (coin_id, lastupdated). For this, I greatly advise using doctrine to send a native SQL query. It's orders of magnitude more efficient, evading costly doctrine hydration, etc.
I wrote a sample query in an entity repository for you.
<?php
namespace App\EntityRepository;
use Doctrine\ORM\EntityRepository;
use PDO;
class CoinPricesRepository extends EntityRepository
{
public function queryLatestPricesForCoinId(int $coin_id, int $limit)
{
$sql = 'SELECT price FROM coin_prices WHERE coin_id = :coin_id ORDER BY lastupdated DESC LIMIT = :limit;';
$params['coin_id'] = $coin_id;
$params['limit'] = $limit;
$stmt = $this->getEntityManager()->getConnection()->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
}
I have been working on optimizing a my Doctrine request and got some awesome perf improvement I’ll be sharing here if anyone is looking at a similar solution.
First, limit your left join with a where clause as much as possible
Second, use partial objets
Third, use Array results. This actually changes everything.
/**
* #return Coins[] Returns an array of Crypto objects
*/
public function findOneByTickerRelationnal($ticker)
{
$em = $this->getEntityManager();
$updatesrepository = $em->getRepository(Updates::class);
$updates = $updatesrepository->findOneBy(['id'=> 1 ]);
// This is where I’ve been doing additional work to limit my left join as much as possible with a ‘with’ on left join
$recentMarkets = $updates->getMarket();
$recentPrices = $updates->getPrice();
$recentSources = $updates->getSources();
$cryptos = $this->createQueryBuilder('c')
->select('partial c.{id, name, ticker}’) //<= use of partial is a plus but you need to know exactly which fields you want
->leftJoin('c.prices', 'p','WITH', 'p.last_updated >= :recentPrices')
->addSelect('partial p.{id, price_usd, daily_volume_usd, change_1h, change_1d, change_7d, rank}')
->leftJoin('c.markets', 'm','WITH', 'm.last_updated >= :recentMarkets')
->addSelect('partial m.{id, cur_supply, market_cap, max_supply}')
->leftJoin('c.sources', 's','WITH', 's.last_updated >= :recentSources')
->addSelect('s')
->where('c.ticker = :ticker')
->setParameter('recentPrices', $recentPrices)
->setParameter('recentMarkets', $recentMarkets)
->setParameter('recentSources', $recentSources)
->setParameter('ticker', $ticker)
->getQuery()
->getArrayResult(); //<=Changes everything
$results = $cryptos[0];
return $results;
}
So, I had this code working earlier today--and all of a sudden it decided to only start displaying the first result from the query. I cannot figure out what i've changed since then, I actually believe that I haven't changed anything... anyway... I've gone into the DB and altered the table so that all the "upgrades" meet the requirements to be displayed, and yet still only one result is being shown.
$sql = "SELECT id, name, cost, count(*) FROM upgrades
WHERE id NOT IN (Select upgrade_id FROM thehave8_site1.user_upgrades WHERE uid = :uid)
AND nullif NOT IN(SELECT upgrade_id FROM thehave8_site1.user_upgrades WHERE uid = :uid2)
AND prereq IN (SELECT upgrade_id FROM thehave8_site1.user_upgrades WHERE uid = :uid3)
;";
$que = $this->db->prepare($sql);
#$que->bindParam(':id', $id); //note the : before id
#$que->bindParam(':id2', $id);
$que->bindParam(':uid', $this->uid);
$que->bindParam(':uid2', $this->uid);
$que->bindParam(':uid3', $this->uid);
try {
$que->execute();
while($row = $que->fetch(PDO::FETCH_BOTH))
{
echo "<div class='upgrade' id={$row[0]}><p>{$row[1]}</p><p>{$row[2]}</p></div>";
}
} catch(PDOException $e) { echo $e->getMessage();}
Problem has been solved, though I'm not sure of the exact reason why, the count(*) being at the end of the query string was preventing the entire code from running properly
Aren't you missing some line of code that advances the result in the query to the next row? When I do loops through recordsets (slightly different than what you are doing but probably not much different) there is usually a MoveNext or something like that - I see nothing like that here.
I don't know this language you are using.
......
I am not being allowed to add a comment to your response so I will add it here..
....
Cool. You really looked like you knew what you were doing, far more advanced than anything I've written! New here also and didn't catch onto the tag system, thanks for pointing that out so I don't need to embarrass myself in future. Glad you've solved it. I think Count (*) would work if you included it as a subquery
SELECT FIELD1, FIELD2, (SELECT count (*) FROM ... ) AS FIELD3
FROM (ETC)
or if you just want its value in the recordset result, compute it ahead of time via query, and then include its value as a dummy/constant field in your select statement. Depending on the query plan in your query engine this may or may not be more efficient.
Or just wait to get Recordcount from your recordset.
I can't find a way to get it working. I have to tables, the first with orders and the second with trackings. I want to add the latest tracking row to left table row.
This is what I've tried so far (doesn't work):
$this->db->select('orders.*, trackings.id AS trackings_id, trackings.order_id AS trackings_order_id, MAX(trackings.status) AS trackings_status, trackings.created_at AS trackings_created_at, trackings.updated_at AS trackings_updated_at, trackings.ip_address AS trackings_ip_address');
$this->db->from('orders');
$this->db->join('trackings', 'orders.id = trackings.order_id', 'left');
$this->db->group_by('trackings.order_id');
$this->db->order_by('orders.created_at', 'DESC');
$query = $this->db->get();
return $query->result();
Woooh, I hate CI's active records, this is a real pain in the butt most of times...
First of all, I'd group by orders.id
Secondly, would be great if you'd define "not working" (gives wrong results? doesn't execute at all?)
P.S. Sorry for posting it as answer (I'd prefer comment) but my rep is still kinda low...
I have just started to convert to mysqli due to the added security benefits. The main reason for converting to mysqli was the mysqli_multi_query function though. I had several long, complicated query's with a lot of JOINS in and when I found out about this function I thought I could make it simpler by breaking everything down into separate queries.
However, I have been unable to get the query to work as I want it to and the PHP manual isn't helping me.
Here's what I have so far;
$qry = "SELECT T.T_ID, T.name, T.pic, T.timestamp AS T_ts,
(SELECT COUNT(*) FROM track_plays WHERE T_ID = T.T_ID) AS plays,
(SELECT COUNT(*) FROM track_downloads WHERE T.T_ID = T_ID) AS downloads
FROM tracks T WHERE ID = '$ID';
";
$qry .= "SELECT S_ID, status, timestamp AS S_ts FROM status WHERE ID = '$ID';";
$qry .= "SELECT G_ID, gig_name, date_time, lineup, price, ticket, venue, G_pic, timestamp AS G_ts FROM gigs WHERE ID = '$ID'";
// Execute multi query
if (mysqli_multi_query($con,$qry)){
do {
// Store first result set
if ($result=mysqli_store_result($con)){
while ($row=mysqli_fetch_array($result)){
print_r($row);
}
}
}
while (mysqli_next_result($con));
}
This works fine and the array is printed as expected. However, i want to set a lot of variables for use later on. I won't list all of the variables but I have a few like this;
$S_ID = htmlspecialchars($row['S_ID']);
$T_ID = htmlspecialchars($row['T_ID']);
$G_ID = htmlspecialchars($row['G_ID']);
The variables are retrieved but due to the loop nature of the function PHP errors are given as well saying the $S_ID or $G_ID are undefined. $T_ID does not get an undefined error as it is the first query as is not looped over.
If you need any more information or I have not explained well enough just ask!
A while back ago i have had the same issues, and also for the same reason i have migrated from mysql to mysqli.
Sadly, it wasnt the best sollution for many cases, as i have realized on the way.
While implementing stuff with mysqli and multi query i still have had some issues that i had with mysql.
The solution i recommend, is to use views on your complicated querys.
it makes your coding and querys far less complicated.
I know it is not the answer you have been looking for, but give it a thought.
So far, every time I use a mysqli_multi_query, my do-while condition is:
while(mysqli_more_results($connection) && mysqli_next_result($connection));
You may like to replace your while statement and see if that helps.
If not, are you differentiating between each subsequent query when you are assigning these $S_ID, $G_ID variables? Are you inadvertently overwriting a variable with an empty $row[] value?
I also didn't see mysqli_free_result($result) in your code.
I'll even offer some error checking as a garnish.
preg_match_all("/(?:SELECT\s(.*?),\s.*?(?:;|$))/",$qry,$first_columnname);
if (mysqli_multi_query($con,$qry)){
do {
if ($result=mysqli_store_result($con)){
$query_identifier=array_shift($first_columnname[1]);
while ($row=mysqli_fetch_array($result)){
if($query_identifier)=="T.T_ID"){
//do something...
}elseif($query_identifier)=="S_ID"){
//do something...
}elseif($query_identifier)=="G_ID"){
//do something...
}
}
mysqli_free_result($result);
}
}
while(mysqli_more_results($con) && mysqli_next_result($con));
}
if($error_mess=mysqli_error($con)){echo "Error = $error_mess";}
Let me know if any of these small changes do the trick.
I'm a bit new to OOP, but i've been playing with it for about a month now. Usually, i create a class called Mysql which has a __construct function that connects to a database directly. And after that i have lots of different functions that gets or inserts data into different tables.
On the bus home today, i began thinking and i came up with a brilliant idea that would make it less cluttered. My idea is to use one single function that selects data (and one for inserting), and depending on how the query that's passed in looks, it will select different data from different tables. Nice, right?
But i'm kind of stuck here. I'm not sure at all how to achieve this. I've got a small clue how it could work, but i don't know how i would bind the results, or fetch them into an array. The query will be created in another method, and then be passed into the select/insert function within the Mysql class.
I drew a "sketch" on how i think it may work. Here it is:
Of course, the function below will be placed in the Mysql class, and will already have connection to a database.
// This is an example query that could be passed in.
$query = "SELECT * FROM table WHERE id=150";
function select_data($query) {
if ( $smtp = $this->conn->prepare($query) ) {
$smtp->execute();
$smtp->bind_results(What happens here?);
if ( $smtp->fetch() ) {
foreach ( fetched_row? as $key => $value ) {
$return[] = $key => $value;
}
return $return;
}
else return NULL;
}
else return $this->conn->error;
}
Thanks a lot to anyone who can show me how this can be achieved.
You have more options to use in PHP and they has their own specifics.
I can recommend some ORM
like Doctrine because of ease of use, stability, community and most importantly efectivity.
You can use it as easy as:
$q = Doctrine_Query::create()
->select('u.username, p.phone')
->from('User u')
->leftJoin('u.Phonenumbers p');
$users = $q->fetchArray();
or:
// Delete phonenumbers for user id = 5
$deleted = Doctrine_Query::create()
->delete()
->from('Phonenumber')
->andWhere('user_id = 5')
->execute();
// Make all usernames lowercase
Doctrine_Query::create()
->update('User u')
->set('u.username', 'LOWER(u.username)')
->execute();
// 'like' condition
$q = Doctrine_Query::create()
->from('User u')
->where('u.username LIKE ?', '%jwage%');
$users = $q->fetchArray();
I think you are running into problems when you need related data. In other words, when an object of yours has a property which is another object that data should also be gathered and dynamically filled. I once came pretty far but when stuff like INNER, LEFT and RIGHT joins come accross you'll think twice about going further ;)
About bind_results:http://php.net/manual/en/mysqli-stmt.bind-result.php
(Maybe slightly off topic; SMTP? That's a mailprotocol, are you sure you don't mean MySQLi's STMT?)
For reference, PDO already does a lot of what you seem to want to do. It even has a fetchAll method that returns an array of rows, much like your function does. You don't need to bind anything in order to use it, unless you have parameters in your query string (and of course, values to bind to those parameters).
Check out the PDO documentation, and see if that doesn't fit your needs. Particularly PDOStatement->fetchAll().