Subqueries in Cakephp 3.0 - sql

How can I Convert Mysql Query :
SELECT * , (
SELECT COUNT( * )
FROM articles
WHERE `user_id` =1
) AS total_count
FROM articles
WHERE `user_id` =1
GROUP BY user_id
into cakephp subquery?
I have tried this,
$articlescount = $this->Articles->find('all')->where(['user_id' => 1])->count();
print_r($articlescount);
Which returns me only no of count.

I have doubts that the query you are using is the best way to do what you want to achieve since it seems that the query and the subquery are returning the same value
Anyway this is how you can obtain the same exact query you asked
$q = $this->Articles->find();
$q->select([$q->func()->count('*')])
->where(['user_id' => 1]);
$q2 = $this->Users->find()
->select(['total_count' => $q])
->autoFields(true)
->where(['user_id' => 1])
->group(['user_id'])
->all();
$q is the subquery while $q2 is the actual query you want.
By the way i think that you could simply do
$q = $this->Users->find()
->select(['total_count' => $q->func()->count('*')])
->autoFields(true)
->where(['user_id' => 1])
->group(['user_id']);

Ariala's comment almost good but i think this code is more flexible because the suquery is not contains user id fixed condition:
$q = $this->Articles->find();
$q->select([$q->func()->count('*')])
->where(['Articles.user_id = Users.id']);
$q2 = $this->Users->find()
->select(['id', 'first_name', 'total_count' => $q])
->where(['id' => 1])
->all();
But if you would like to list all users with article count you can do it if you leave the where condition from $q2.
It will results like this:
id | first_name | total_count
1 | John | 3
2 | Doe | 0

Related

Add multiple rows in Laravel DB Query (for migrating WordPress usermeta table into new Laravel table)

I'm migrating Users from WordPress to Laravel. I want to join the users and user_meta tables. Then I will import into a new table.
In my user_meta table I have multiple rows assigned to the user_id. How do I import multiple rows with their own unique identifier.
Eg.
umeta_id = 1, user_id = 1, meta_key = first_name, meta_value = Bob
umeta_id = 2, user_id = 1, meta_key = last_name, meta_value = Builder
In the above example, I'd like to add first_name and last_name to the query output.
Then import into a new column in the Laravel DB (and stop using the user_meta reference table approach WordPress uses).
Here is my current query code and the output I get:
$wp_users = DB::connection('wordpress_db')
// ->select(DB::raw('um.meta_value as first_name'))
->select('um.meta_value as first_name')
->table('wp_users')
->leftJoin('wp_usermeta as um', function ($q) {
$q->on('um.user_id', '=', 'wp_users.id')
->where('um.meta_key', '=', "first_name");
})
->orderBy('wp_users.id');
and if i dump the output:
+"ID": 1
+"user_login": "123"
+"user_pass": "123"
+"user_nicename": "123"
+"user_email": "b#x"
+"user_url": "https://x"
+"user_registered": "2016-1-1 13:47:32"
+"user_activation_key": ""
+"user_status": 0
+"display_name": "Bobby Builds"
+"umeta_id": 222
+"user_id": 1
+"meta_key": "first_name"
+"meta_value": "Bob"
Rather than meta_value and meta_key I just want 'first_name' => 'bob' and then also the ability to do this for multiple values in the user_meta reference table
If I was writing RAW sql then I think I would approach this by having a left_join per value I want to get. I would then create an alias like
SELECT um1.meta_value as first_name
SELECT um2.meta_value as last_name
LEFTJOIN wp_usermeta as um1
LEFTJOIN wp_usermeta as um2
I've noodled around without luck - any ideas appreciated.
Thanks!
I've written about some of the processes involved here for further reference: http://raison.co/migrating-wordpress-users-to-laravel/
I did this in the end by using select to choose all columns from wp_users and then selectively adding the new joins.
See the code fixed code below:
$wp_users = DB::connection('wp_db')
->table('wp_users')
->leftJoin('wp_usermeta as um', function ($q) {
$q->on('um.user_id', '=', 'wp_users.id')
->where('um.meta_key', '=', "wbp_user_mob_phone");
})
->leftJoin('wp_usermeta as um2', function ($q) {
$q->on('um2.user_id', '=', 'wp_users.id')
->where('um2.meta_key', '=', "first_name");
})
->select('wp_users.*', 'um.meta_value as wbp_user_mob_phone', 'um2.meta_value as first_name')
->orderBy('wp_users.id');

MySQL distinct count type query with RethinkDB

I'm having some problems implementing the following SQL query in rethinkdb, I wanna get the 5 most popular channels in a community, based on user_count.
SELECT
channels.*,
COUNT(distinct channel_users.user_id) as user_count
FROM channel_users
LEFT JOIN channels ON
channels.id = channel_users.channel_id
WHERE channels.community_id = "MY_COMMUNITY_ID" AND channels.type = 'public'
GROUP BY channel_id
ORDER BY user_count DESC
LIMIT 5
This is what I got this far in ReQL, which just gives me a list of the channels, I suspect some more map/reducing is required here?
r.db('my_db')
.table('channel_users')
.filter({ community_id : 'MY_community_id' })
.orderBy(r.desc('created_at'))
.eqJoin('channel_id', r.table('channels'))
.map(function(doc){
return doc.merge(function(){
return {
'left' : null,
'right': {'user_id': doc('left')('user_id')}
}
})
})
.zip()
.run(function(err, channels){
console.log(err, channels);
next();
});
And the table design looks like:
channel_users
id | channel_id | community_id | role | user_id
channels
id | community_id | name | user_id (creator)
Any help appreciated! Thanks
Does this do what you want?
r.table('channels').filter(
{community_id: 'MY_COMMUNITY_ID', type: 'public'}
).merge(function(channel) {
return {user_count: r.table('channel_users').filter({channel_id: channel('id')}).count()};
}).orderBy(r.desc('user_count')).limit(5)
(Note that you can speed this up by using getAll instead of filter inside the merge if you create a secondary index on channel_id.)

ActiveRecord how to correctly exclude records with conditions correctly?

Why does below not work?
Try to exclude all the is_reply = 1 values from the query but can't figure out how to.
I tried several combinations but all fail.
All records with is_reply = 1 still get returned with this below query, I think need to sub the or part but how?
#messages = Message.all(:conditions => ['is_reply = ? AND recipient_id
= ? OR user_id = ?', 0, current_user.id, current_user.id] )
You probably want to use parenthesis() and group your conditions together
['is_reply = ? AND (recipient_id = ? OR user_id = ?)', 0, current_user.id, current_user.id]

Convert SQL to Linq - Where, Groupby and Having

The proper syntax in Linq for this SQL query is eluding me. I'd appreciate any hand with this.
SQL:
SELECT TOP 1 posdate
FROM dailypos
GROUP BY posdate
HAVING Count(DISTINCT customernumber) = 3
ORDER BY posdate DESC)
Essentially, I get files from 3 different customers and I need a quick way to determine the most recent date for which I have data from all 3. I'm open to a different approach, but this SQL works for what I need.
I can do the group, but I don't know how to handle the 4th line (HAVING Count(Distinct...))
Thanks in advance.
Try something like this:
var result = context.dailypos
.GroupBy(x => x.posdate)
.Where(g => g.Select(x => x.customernumber).Distinct().Count() == 3)
.Select(g => g.Key)
.OrderByDescending(x => x)
.Take(1);

Pull back rows from multiple tables with a sub-select?

I have a script which generates queries in the following fashion (based on user input):
SELECT * FROM articles
WHERE (articles.skeywords_auto ilike '%pm2%')
AND spubid IN (
SELECT people.spubid FROM people
WHERE (people.slast ilike 'chow')
GROUP BY people.spubid)
LIMIT 1;
The resulting data set:
Array ( [0] =>
Array (
[spubid] => A00603
[bactive] => t
[bbatch_import] => t
[bincomplete] => t
[scitation_vis] => I,X
[dentered] => 2009-07-24 17:07:27.241975
[sentered_by] => pubs_batchadd.php
[drev] => 2009-07-24 17:07:27.241975
[srev_by] => pubs_batchadd.php
[bpeer_reviewed] => t
[sarticle] => Errata: PM2.5 and PM10 concentrations from the Qalabotjha low-smoke fuels macro-scale experiment in South Africa (vol 69, pg 1, 2001)
[spublication] => Environmental Monitoring and Assessment
[ipublisher] =>
[svolume] => 71
[sissue] =>
[spage_start] => 207
[spage_end] => 210
[bon_cover] => f
[scover_location] =>
[scover_vis] => I,X
[sabstract] =>
[sabstract_vis] => I,X
[sarticle_url] =>
[sdoi] =>
[sfile_location] =>
[sfile_name] =>
[sfile_vis] => I
[sscience_codes] =>
[skeywords_manual] =>
[skeywords_auto] => 1,5,69,2001,africa,assessment,concentrations,environmental,errata,experiment,fuels,low-smoke,macro-scale,monitoring,pg,pm10,pm2,qalabotjha,south,vol
[saward_number] =>
[snotes] =>
)
The problem is that I also need all the columns from the 'people' table (as referenced in the sub select) to come back as part of the data set. I haven't (obviously) done much with sub selects in the past so this approach is very new to me. How do I pull back all the matching rows/columns from the articles table AS WELL as the rows/column from the people table?
Are you familiar with joins? Using ANSI syntax:
SELECT DISTINCT *
FROM ARTICLES t
JOIN PEOPLE p ON p.spubid = t.spudid AND p.slast ILIKE 'chow'
WHERE t.skeywords_auto ILIKE'%pm2%'
LIMIT 1;
The DISTINCT saves from having to define a GROUP BY for every column returned from both tables. I included it because you had the GROUP BY on your subquery; I don't know if it was actually necessary.
Could you not use a join instead of a sub-select in this case?
SELECT a.*, p.*
FROM articles as a
INNER JOIN people as p ON a.spubid = p.spubid
WHERE a.skeywords_auto ilike '%pm2%'
AND p.slast ilike 'chow'
LIMIT 1;
Lets start from the beginning
You shouldn't need a group by. Use distinct instead (you aren't doing any aggregating in the inner query).
To see the contents of the inner table, you actually have to join it. The contents are not exposed unless it shows up in the from section. A left outer join from the people table to the articles table should be equivalent to an IN query :
SELECT *
FROM people
LEFT OUTER JOIN articles ON articles.spubid = people.spubid
WHERE people.slast ilike 'chow'
AND articles.skeywords_auto ilike '%pm2%'
LIMIT 1