Yii order by another table CActiveDataProvider - yii

I am a bit stumped on this. Basically I have two tables:
Page:
id
name
Points:
id-
pageid
points
I am looking to get the records from the Page table, and sort it by the amount of points it has in the Points table (the points field)
Currently I have:
$dataProvider=new CActiveDataProvider('Page',array(
'criteria'=>array(
'condition'=>"active = 1 AND userid IN (".$ids.")",
'order'=>"???",
),
'pagination'=>array(
'pageSize'=>30,
),
));
I just don't know how to sort it by the Points table value for the relevant record
I have set up a relation for the Page/Points tables like so:
(in the Page model)
'pagepoints' => array(self::HAS_ONE, 'Points', 'pageid'),
Thanks

You need to do two things:
Add the pagepoints relation to the with part of the query criteria
Reference the column you want to sort by in the order part of the criteria
I 've marked the lines where this happens in the code below:
$dataProvider = new CActiveDataProvider('Page', array(
'criteria'=>array(
'with' => array('pagepoints'), // #1
'condition' => 'active = 1 AND userid IN ('.$ids.')',
'order' => 'pagepoints.points', // #2
),
'pagination'=>array(
'pageSize'=>30,
),
));
What you need to know to understand how this works is that when Yii builds the SQL query (which is a LEFT OUTER JOIN to the Points table), it uses the name you gave to the relation in the Page model (you give the definition for this, it's pagepoints) to alias the joined table. In other words, the query looks like:
SELECT ... FROM Page ... LEFT OUTER JOIN `Points` `pagepoints` ...
It follows that the correct specification for the sort order is pagepoints.points: pagepoints is the table alias, and points is the column in that table.

Try the following
$dataProvider=new CActiveDataProvider('Page',array(
'criteria'=>array(
'with'=>array('pagepoints'),
'condition'=>"active = 1 AND userid IN (".$ids.")",
'order'=>"t.points DESC",
),
'pagination'=>array(
'pageSize'=>30,
),
));

this is the sql you want to generate:
select * from page inner join points
on page.id = points.page_id order by
points.points desc

Related

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 *.

Cakephp query to get last single field data from multi user

I have a table called Transaction with relation User, Transaction has a field called balance.
Data looks like:
id user_id balance
1 22 365
2 22 15
3 22 900
4 32 100
4 32 50
I need all users associative data and last insert balance field of User. For example here id=3 is last inserted data for user_id=22.
In raw SQL I have tried this:
select * from transactions where id in (select max(id) from transactions group by user_id)
If I add here a inner join I know I can also retrieve User data. But how can I do this in CakePHP?
IMHO, subqueries are ugly in CakePHP 2.x. You may as well hard code the SQL statement and execute it through query(), as suggested by #AgRizzo in the comments.
However, when it comes to retrieving the last (largest, oldest, etc.) item in a group, there is a more elegant solution.
In this SQL Fiddle, I've applied the technique described in
Retrieving the last record in each group
The CakePHP 2.x equivalent would be:
$this->Transaction->contains('User');
$options['fields'] = array("User.id", "User.name", "Transaction.balance");
$options['joins'] = array(
array('table' => 'transactions',
'alias' => 'Transaction2',
'type' => 'LEFT',
'conditions' => array(
'Transaction2.user_id = Transaction2.user_id',
'Transaction.id < Transaction2.id'
)
),
);
$options['conditions'] = array("Transaction2.id IS NULL");
$transactions=$this->Transaction->find('all', $options);

wordpress ajax relationship query

Using the relationship field from Advanced Custom Fields I link artists to events. Both these artist and events are custom post types. For each event, the post IDs of the related artists are stored in an array as a custom meta field (lineup_artists).
On each event page I list all the artists. When you click on an artist, I'd like to show all the events where you can find this artist (through an AJAX call which shows the results in a bootstrap modal). I've tested the AJAX call and it's working, but there's something wrong with the query (takes very long to complete).
In my function I have:
$ArtistID = $_POST['ArtistID']; // Gets the ArtistID from the AJAX call
$meta_query = array(
'key' => 'lineup_artists',
'value' => '"' . $ArtistID .'"',
'compare' => 'LIKE'
);
$args = array(
'post_type' => 'events',
'meta_query' => array($meta_query),
'posts_per_page' => 5,
'post_status' => 'publish',
);
If I dump the results of wp_query, I get the following sql query:
SELECT SQL_CALC_FOUND_ROWS yt_posts.ID FROM yt_posts
INNER JOIN yt_postmeta ON (yt_posts.ID = yt_postmeta.post_id)
WHERE 1=1
AND yt_posts.post_type = 'events'
AND (yt_posts.post_status = 'publish')
AND ( (yt_postmeta.meta_key = 'lineup_artists' AND CAST(yt_postmeta.meta_value AS CHAR) LIKE '%\"17497\"%') )
GROUP BY yt_posts.ID ORDER BY yt_posts.post_date DESC LIMIT 0, 5
When I paste this query in phpmyadmin it takes very long to complete (I've never seen it finish because it takes so long).
Is this because the artist IDs are stored as an array? Someone told me that this is not very efficient and that these relations should be stored in a separate table (which I don't know how to do). Is there something wrong with my query or is this a very inefficient way of querying relations?

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'));

Need help creating a linq select

I need some help creating an LINQ select, i have a table with some columns in it, but only 2 of interest to this problem.
userid, type
Now this table have many thousands entries, and I only want the top, let’s say 50. So far so good, but the hard part is that there a lot of rows in success that should only be counted as 1.
Example
Type UserId
============
Add 1
Add 1
Add 1
Add 2
I would like this to only be counted as 2 in the limit of rows I am taking out, but I would like all the rows to be outputted still.
Is this possible with a single SQL request, or should I find another way to do this?
Edit: I can add columns to the table, with values if this would solve the problem.
Edit2: Sotred procedures are also an solution
Example 2: This should be counted as 3 rows
Type UserId
============
Add 1
Add 1
Add 2
Add 1
Are you stuck on LINQ?
Add a PK identity.
Order by PK.
Use a DataReader and just count the changes.
Then just stop when the changes count is at your max.
If you are not in a .NET environment then same thing with a cursor.
Since LINQ is deferred you might be able to just order in LINQ and then on a ForEach just exit.
I'm not close to a computer right now so I'm not sure is 100% correct syntax wise, but I believe you're looking for something like this:
data.Select(x => new {x.Type, x.UserId})
.GroupBy(x => x.UserId)
.Take(50);
You could do it with Linq, but it may be a LOT slower than a traditional for loop. One way would be:
data.Where((s, i) => i == 0 ||
!(s.Type == data[i-1].Type && s.UserId == data[i-1].UserId))
That would skip any "duplicate" items that have the same Type and UserID as the "previous" item.
However this ONLY works if data has an indexer (an array or something that implements IList). An IEnumerable or IQueryable would not work. Also, it is almost certainly not translatable to SQL so you'd have to pull ALL of the results and filter in-memory.
If you want to do it in SQL I would try either scanning a cursor and filling a temp table if one of the values change or using a common table expression that included a ROW_NUMBER column, then doing a look-back sub-query similar to the Linq method above:
WITH base AS
(
SELECT
Type,
UserId,
ROW_NUMBER() OVER (ORDER BY ??? ) AS RowNum
FROM Table
)
SELECT b1.Type, b1.UserId
FROM base b1
LEFT JOIN base b2 ON b1.RowNum = b2.RowNum - 1
WHERE (b1.Type <> b2.Type OR b1.UserId <> b2.UserId)
ORDER BY b1.RowNum
You can do this with LINQ, but I think it might be easier to go the "for(each) loop" route...
data.Select((x, i) => new { x.Type, x.UserId, i })
.GroupBy(x => x.Type)
.Select(g => new
{
Type = g.Key,
Items = g
.Select((x, j) => new { x.UserId, i = x.i - j })
})
.SelectMany(g => g.Select(x => new { g.Type, x.UserId, x.i }))
.GroupBy(x => new { x.Type, x.i })
.Take(50);
.SelectMany(g => g.Select(x => new { x.Type, x.UserId }));