How can I use IF(bool, a, b) clause in CakePHP's order parameter? - sql

I'm writing a SQL query to order products by price asc, but putting products with 0 price at last.
As I know, I can use ORDER BY IF(price=0,1,0) ASC, price ASC to get it done. But when I pass 'order' => array('IF(Product.price=0, 1, 0) ASC', 'Product.price' => 'ASC') to the paginate component in CakePHP, the IF part is dismissed automatically.
I'm wondering how to get it done in CakePHP with CakePHP style param array?

i think you want this...
if($condition) ? /**want do you want if true/ : /** do what you want if false/;
hope it help

Related

Laravel Collection Sorting Desc/Asc Based On Condition

I have laravel elqueont collection and i want to sort it based on condition on status value, if status == 3 the ordering will be id desc else the ordering will be id asc, help please.
trying to sort laravel collection based on condition on anthor column value
You can read all about collection sorting on the documentation. So lets say you have 2 conditions your code will look like this
$collection = Model::all();
if($condition == 1) {
$collection->sortBy('created_at');
} else {
$collection->sortByDesc('created_at);
}
This is just a general idea, you can share your code for further help.

Phalcon Model order by item popularity (number of appearances)

I'm sure I have done something like this before, but can't find it and google not being helpful.
Using Phalcon model if possible, I want to select the items from a table whose ID appears the most - i.e. 10 most popular items ordered by popularity. Is this possible using Model::find("conditions")? I do I have to use PHQL for this?
using model::find
Model::find([
'columns' => 'id,count(id) as counter',
'group' => 'id',
'order' => 'counter DESC'
]);
PHQL:
$this->modelsManager->executeQuery('SELECT count(id) AS counter,id FROM ModelName GROUP BY id ORDER BY counter DESC');
find() does have a group clause, but I don't think it's possible to do what you want because you also need to do a count.
Talal's answer is close, but won't work if you want a list of model objects.
Something like this should work:
$Results = $this->modelsManager->executeQuery('SELECT * FROM ModelName GROUP BY id ORDER BY count(id) DESC LIMIT 10');
$Results->setHydrationMode(\Phalcon\Mvc\Model\Resultset::HYDRATE_RECORDS);
Setting the hydration mode may not be necessary, as Phalcon may default to that mode based on the fact the query is asking for *.

ZF2 how to avoid sql query limit to add quotes in subquery

I'm trying to set up a subquery in ZendFramework 2 and I got an issue with the limit function for a Select object. Whatever I do, numeric value is put between quotes and makes my query fails : I should get LIMIT 1 and instead I get LIMIT '1'.
Seems this is not the first time this issue has been encountered, I saw some have asked about this issue before (like 8 months ago) but without getting any proper answer.
I also saw this issue has been marker as resolved in 2012 (https://github.com/zendframework/zf2/pull/2775) so I really don't understand what's happening there.
Here's my code in ZF2 :
$resultSet = $this->tableGateway->select( function (Select $select) use ($params) {
$sub = new Select();
$sub->from(array('temp' => 'scores'))
->columns(array(new \Zend\Db\Sql\Expression("id AS id")))
->where(array('temp.glitch' => array('None', 'Glitch')))
->where('temp.zone=scores.zone')
->order('temp.multi DESC, temp.score DESC')
->limit(1);
$select->join('players', 'player=players.id', array('player_name' => 'name', 'player_url' => 'name_url'))
->join('countries', 'players.country=countries.id', array('country_name' => 'name', 'country_iso' => 'iso'))
->join('cars', 'car=cars.id', array('car_name' => 'name'), 'left')
->join('zones', 'zone=zones.id', array('zone_name' => 'name'));
$select->where(array('scores.id' => $sub));
$select->order('scores.zone ASC');
print_r($select->getSqlString());
});
This should render the following query (which I get right except LIMIT '1' instead of LIMIT 1) :
SELECT "scores".*, "players"."name" AS "player_name", "players"."name_url" AS "player_url", "countries"."name" AS "country_name", "countries"."iso" AS "country_iso", "cars"."name" AS "car_name", "zones"."name" AS "zone_name"
FROM "scores" INNER JOIN "players" ON "player"="players"."id"
INNER JOIN "countries" ON "players"."country"="countries"."id"
LEFT JOIN "cars" ON "car"="cars"."id"
INNER JOIN "zones" ON "zone"="zones"."id"
WHERE "scores"."id" = (SELECT id AS id FROM "scores" AS "temp" WHERE "temp"."glitch" IN ('None', 'Glitch')
AND temp.zone=scores.zone ORDER BY "temp"."multi" DESC, "temp"."score" DESC LIMIT 1)
ORDER BY "scores"."zone" ASC
Since this doesn't seem to work this way, is there another way I could proceed to get my limit (using Mysql 5 database) ?
EDIT :
Thanks for your help. Finally I figured out a way to get things done the way I want and to remove the quotes by simply remove the subquery construction and to write it directly in the where function :
$select->where('scores.id = (SELECT id FROM scores AS lookup WHERE lookup.zone = scores.zone ORDER BY multi DESC , score DESC LIMIT 1)');
Although I can continue my dev with this, I feel more like using a poor trick to get rid of this issue and so I will let this question unanswered until someone comes with a real solution there.
Anyway there might be no solution at all, since it might be an issue in ZF2 core itself.
Change the line -
$select->where(array('scores.id' => $sub));
with
$select->where(array('scores.id' => new \Zend\Db\Sql\Expression("({$sub->getSqlString($this->tableGateway->adapter->getPlatform())})"));
Try with just above change.
And if it still doesn't work then make changes to the core Select class file located at -
PROJECT_FOLDER/vendor/zendframework/zendframework/library/Zend/Db/Sql/Select.php
Line No. 921 -
Change $sql = $platform->quoteValue($limit); with $sql = $limit;
Line No. 940 -
Change return array($platform->quoteValue($offset)); with return array($offset);
I have come across the issue from github and wondered as why it is still not working with the latest ZF2 files. I know the solution given above doesn't look like the proper one but I had to somehow make it work. I have tried it and it works.
Its only a quick fix before the actual solution comes into picture.

CakePHP SQL-query count

I have a problem concerning CakePHP SQL-queries. I need to fetch products from the database where shop_id is given and then count the products. All I need is Product.url and its count.
This will do the trick in plain SQL:
SELECT url,COUNT(*) as count FROM products GROUP BY url ORDER BY count DESC;
This one I used to get all products relating to shops:
$this->Product->find('all', array('conditions' => array('Product.shop_id'=>$id)));
That works correctly, but I need to convert that SQL-query above to CakePHP.
I tried something like this:
$this->Product->find('count', array('conditions' => array('Product.shop_id'=>$id),
'fields'=>array('Product.url','Product.id'),
'order'=>'count DESC',
'group'=>'Product.url'));
That returns only an int. But if I run that SLQ-query presented above in mysql server, I get two columns: url and count. How do I get the same results with CakePHP?
You can try this:
$data = $this->Post->query(
"SELECT COUNT(id),MONTH(created) FROM posts GROUP BY YEAR(created), MONTH(created);"
);
The most easiest way to do this:
$this->Product->query("SELECT url,COUNT(*) as count FROM products GROUP BY url ORDER BY count DESC;");
...at least for me.
Try the following code:
$this->Product->virtualFields['CNT_PRODUCTS'] = 0;
$this->Product->find('count', array('conditions' => array('Product.shop_id' => $id),
'fields' => array('Product.id', 'Product.url', 'count(*) as CNT_PRODUCTS'),
'order' => array('CNT_PRODUCTS' => 'DESC'),
'group' => 'Product.url'));

Help optimizing ActiveRecord query (voting system)

I have a voting system with two models: Item(id, name) and Vote(id, item_id, user_id).
Here's the code I have so far:
class Item < ActiveRecord::Base
has_many :votes
def self.most_popular
items = Item.all #where can I optimize here?
items.sort {|x,y| x.votes.length <=> y.votes.length}.first #so I don't need to do anything here?
end
end
There's a few things wrong with this, mainly that I retrieve all the Item records, THEN use Ruby to compute popularity. I am almost certain there is a simple solution to this, but I can't quite put my finger on it.
I'd much rather gather records and run the calculations in the initial query. This way, I can add a simple :limit => 1 (or LIMIT 1) to the query.
Any help would be great--either rewrite in all ActiveRecord or even in raw SQl. The latter would actually give me a much clearer picture of the nature of the query I want to execute.
Group the votes by item id, order them by count and then take the item of the first one. In rails 3 the code for this is:
Vote.group(:item_id).order("count(*) DESC").first.item
In rails 2, this should work:
Vote.all(:order => "count(*) DESC", :group => :item_id).first.item
sepp2k has the right idea. In case you're not using Rails 3, the equivalent is:
Vote.first(:group => :item_id, :order => "count(*) DESC", :include => :item).item
Probably there's a better way to do this in ruby, but in SQL (mysql at least) you could try something like this to get a top 10 ranking:
SELECT i.id, i.name, COUNT( v.id ) AS total_votes
FROM Item i
LEFT JOIN Vote v ON ( i.id = v.item_id )
GROUP BY i.id
ORDER BY total_votes DESC
LIMIT 10
One easy way of handling this is to add a vote count field to the Item, and update that each time there is a vote. Rails used to do that automatically for you, but not sure if it's still the case in 2.x and 3.0. It's easy enough for you to do it in any case using an Observer pattern or else just by putting in a "after_save" in the Vote model.
Then your query is very easy, by simply adding a "VOTE_COUNT DESC" order to your query.