Using mysql LEFT Join DESC order is not working - mysql

Here i have two table,i have to join these two table and i have to get the plan details, i tried but is not happening, here is my code
user_info
id fullName
1 Arun
2 Sarvan
user_active_plan
id userId planName
1 1 Free Plan
2 1 Cool Plan
3 2 Free Plan
contact_property
id userId contactProperty
1 1 A
2 1 B
3 2 C
Here user_info(tablename) id (column name) i am using foreign key of user_active_plan(table name) userId(column name)
I want get the latest plan based on userId,So i am using desc order , but it is not coming expected results:
$sql = "SELECT a.fullName,b.*FROM user_info a LEFT JOIN user_active_plan b ON a.id = b.userId GROUP BY b.userId ORDER BY id DESC";
$result = $this->GetJoinRecord($sql);
print_r($result);
I am getting the following incorrect results:
Array
(
[0] => Array
(
[fullName] => Sarvan
[id] => 3
[userId] => 2
[planName] => Free Plan
)
[1] => Array
(
[fullName] => Arun
[id] => 1
[userId] => 1
[planName] => Free Plan
)
)
)
I was expecting the following:
Array
(
[0] => Array
(
[fullName] => Sarvan
[id] => 3
[userId] => 2
[planName] => Free Plan
)
[1] => Array
(
[fullName] => Arun
[id] => 2
[userId] => 1
[planName] => Coll Plan
)
)
)
Updated Expected Answer
Array
(
[0] => Array
(
[userId] => 1
[fullName] => Arun
[planId] => 2
[planName] => Cool Plan
[contactCount] => 2
)
[1] => Array
(
[userId] => 2
[fullName] => Sarvan
[planId] => 3
[planName] => Free Pla1
[contactCount] => 1
)
)

You can get the latest plan with a simple subquery, no need for grouping. The count of contacts can be done with a simple grouping:
SELECT u.id AS userId, u.fullName, p.id AS planId, p.planName, COUNT(c.userId) AS contactCount
FROM user_info u
LEFT JOIN user_active_plan p ON u.id = p.userId
LEFT JOIN contact_property c ON u.id = c.userId
WHERE p.id = (SELECT id
FROM user_active_plan
WHERE userId = u.id
ORDER BY id DESC
LIMIT 1)
GROUP BY c.userId;
You can also move the condition from the WHERE clause to the join:
SELECT u.id AS userId, u.fullName, p.id AS planId, p.planName, COUNT(c.userId) AS contactCount
FROM user_info u
LEFT JOIN user_active_plan p ON u.id = p.userId
AND p.id = (SELECT id
FROM user_active_plan
WHERE userId = u.id
ORDER BY id DESC
LIMIT 1)
LEFT JOIN contact_property c ON u.id = c.userId
GROUP BY c.userId;

Related

ORM query builder for complex condition in CakePHP

I have user_messages table with columns id, sender_id, receiver_id, message, deleted
I have to retrieve all messages like this
SELECT *
FROM
user_messages UserMessages
WHERE (
UserMessages.deleted = false
AND (
(sender_id = $loggedin_user_id AND receiver_id = $user_id)
OR
(sender_id = $user_id AND receiver_id = $loggedin_user_id)
)
ORDER BY
created DESC
Currently, I'm using this query builder
$message_by_list = $this->UserMessages->find()
->where(['UserMessages.deleted' => false])
->andWhere(function ($exp) {
return $exp->or_([
'sender_id' => $this->Auth->user('id'),
'receiver_id' => $this->Auth->user('id')
]);
})
which is generating sql query as
FROM
user_messages UserMessages
WHERE
(UserMessages.deleted = false AND (sender_id = $loggedin_user_id OR receiver_id = $loggedin_user_id))
ORDER BY
created DESC
How to write optimized ORM Query to retrieve data as above?
Edit 2 : Updated query for arilia's answer
WHERE (
(
UserMessages.deleted = false
)
AND
(
(
UserMessages.sender_id = $loggedin_user_id
AND
UserMessages.receiver_id = $user_id
)
OR
UserMessages.sender_id = $user_id
OR /// doubt here
UserMessages.receiver_id = $loggedin_user_id
)
)
try this
$message_by_list = $this->UserMessages->find()
->where(['sender_id' => $user_id, 'receiver_id' => $logged_user_id])
->orWhere(['sender_id' => $logged_user_id, 'receiver_id' => $user_id])
->andWhere(['UserMessages.deleted' => false]);

Unique Left Join Gives Skewed Results

I'm very perplexed! I have 3 tables that separately give me the results I'm looking for. When I join them (tried join, union, left join, sub query) I get skewed results.
Table 1:
SELECT DISTINCT JB.job_id, AR.job_id as jid, SUM(AR.ar_payment_amount) AS sum,
JB.marketing_campaign FROM job_tbl JB LEFT JOIN ar_payment_tbl AR ON JB.job_id =
AR.job_id WHERE JB.marketing_campaign != '' AND FROM_UNIXTIME(AR.ar_payment_date,'%Y') =
YEAR(NOW()) GROUP BY JB.marketing_campaign ORDER BY sum DESC LIMIT 10
Which gives me the result I'm looking for (just showing one result for this example)
[job_id] => 551
[jid] => 551
[sum] => 124440.97024536133
[marketing_campaign] => Roto Rooter
Table 2:
SELECT DISTINCT JB.job_id, AP.job_id, SUM(price) AS price, AP.vendor FROM job_tbl JB
LEFT JOIN ap_tbl AP ON JB.job_id = AP.job_id WHERE AP.marked_as_paid = 1 AND
AP.activity = 'Commission' AND FROM_UNIXTIME(AP.payment_date,'%Y') = YEAR(NOW()) GROUP
BY vendor ORDER BY price DESC LIMIT 10
Which gives me the results I'm looking for...
[job_id] => 551
[price] => 5700
[vendor] => 436
Now when I join them I get a different result
SELECT DISTINCT JB.job_id, AR.job_id as arid, AP.job_id as apid,
SUM(AR.ar_payment_amount) AS sum, SUM(AP.price) AS price, JB.marketing_campaign FROM
job_tbl JB LEFT JOIN ap_tbl AP ON JB.job_id = AP.job_id AND AP.marked_as_paid = 1 AND
AP.activity = 'Commission' LEFT JOIN ar_payment_tbl AR ON JB.job_id = AR.job_id AND
FROM_UNIXTIME(AR.ar_payment_date,'%Y') = YEAR(NOW()) WHERE AP.price != '' AND
AR.ar_payment_amount != '' AND JB.marketing_campaign != '' GROUP BY
JB.marketing_campaign ORDER BY sum DESC LIMIT 10
Here is the result I get
[job_id] => 551
[arid] => 551
[apid] => 551
[sum] => 130507.02011108398
[price] => 8200
[marketing_campaign] => Roto Rooter
and here is what the result should be
[job_id] => 551
[arid] => 551
[apid] => 551
[sum] => 124440.97024536133
[price] => 5700
[marketing_campaign] => Roto Rooter
Any help would be appreciated and this project was due last Friday! ;-)
Try this - I think there were a Problem with the JOIN Condition and the GROUP BY:
SELECT
JB.job_id,
AR.job_id as arid,
AP.job_id as apid,
JB.marketing_campaign,
SUM(AR.ar_payment_amount) AS `sum`,
SUM(AP.price) AS price
FROM job_tbl JB
LEFT JOIN ap_tbl AP ON JB.job_id = AP.job_id
LEFT JOIN ar_payment_tbl AR ON JB.job_id = AR.job_id
WHERE
AP.marked_as_paid = 1
AND AP.activity = 'Commission'
AND FROM_UNIXTIME(AR.ar_payment_date,'%Y') = YEAR(NOW())
AND AP.price != ''
AND AR.ar_payment_amount != ''
AND JB.marketing_campaign != ''
GROUP BY 1,2,3,4
ORDER BY `sum` DESC LIMIT 10

getting row counts from multiple tables

I am trying to get row counts returned for different tables based on user_id value. users is a table of all users with a unique column of user_id. All other tables have a corresponding user_id column to join on it with.
I would think this would be fairly easy, but for some reason I cannot get the counts to return right.
What I want to accomplish is alerts = ? and locations = ? where ? is the total number of rows in that table where user_id = 1,2,3,4,5,6,7, or 8.
$stmt = $db->prepare("
SELECT
count(t_alerts.user_id) as alerts,
count(t_locations.user_id) as locations
FROM users
LEFT JOIN
(SELECT user_id
FROM alert_logs
WHERE alert_logs.event_title LIKE '%blocked%'
) as t_alerts
on t_alerts.user_id = users.user_id
LEFT JOIN
(SELECT user_id
FROM location_logs
) as t_locations
on t_locations.user_id = users.user_id
WHERE users.user_id IN(1,2,3,4,5,6,7,8)
");
$stmt->execute();
//get results
$results = $stmt->fetch(PDO::FETCH_ASSOC);
EDIT :
A bit of a modification to eliminate the need of supplying the IN values... I use this in some other queries to only get results for 'active' users...
$stmt = $db->prepare("
SELECT
(SELECT COUNT(*)
FROM alert_logs al
WHERE event_title LIKE '%blocked%' AND al.user_id = u.user_id
) as alerts,
(SELECT COUNT(*)
FROM location_logs ll
WHERE ll.user_id = u.user_id
) as locations
FROM
( SELECT account_id, computer_id
FROM computers
WHERE account_id = :account_id
ORDER BY computer_id ASC LIMIT 0, :licenses
) as c
INNER JOIN users as u
on u.computer_id = c.computer_id
");
$binding = array(
'account_id' => $_SESSION['user']['account_id'],
'licenses' => $_SESSION['user']['licenses']
);
$stmt->execute($binding);
I am running into the problem mentioned below with this statement... it is returning an array of counts per user rather than all counts combined into one result.
Array
(
[0] => Array
(
[alerts] => 6
[locations] => 4
)
[1] => Array
(
[alerts] => 3
[locations] => 5
)
[2] => Array
(
[alerts] => 1
[locations] => 4
)
[3] => Array
(
[alerts] => 0
[locations] => 0
)
[4] => Array
(
[alerts] => 0
[locations] => 0
)
[5] => Array
(
[alerts] => 0
[locations] => 0
)
[6] => Array
(
[alerts] => 0
[locations] => 0
)
[7] => Array
(
[alerts] => 0
[locations] => 0
)
)
What can I do to 'combine' results?
The problem is that the alerts are multiplying with the locations. So, if there are 10 alerts and 5 locations, the result is 50 rows. That is what gets counted.
The easy solution is to use count(distinct):
SELECT
count(distinct t_alerts.user_id) as alerts,
count(distinct t_locations.user_id) as locations
. . .
The better solution is often to use a subquery to do the counting along each dimension, and then join the results together.
EDIT:
In your case, nested subqueries in the select might be the best approach, because the query filters on users:
SELECT (SELECT COUNT(*)
FROM alert_logs al
WHERE event_title LIKE '%blocked%' AND
al.user_id = u.user_id
) as alerts,
(SELECT COUNT(*)
FROM location_logs ll
WHERE ll.user_id = u.user_id
) as locations
FROM users u
WHERE u.user_id IN (1,2,3,4,5,6,7,8)
EDIT II:
I see, there is no group by at the end of your query. In that case, you might as well do:
SELECT (SELECT COUNT(*)
FROM alert_logs al
WHERE event_title LIKE '%blocked%' AND
al.user_id IN (1,2,3,4,5,6,7,8)
) as alerts,
(SELECT COUNT(*)
FROM location_logs ll
WHERE ll.user_id IN (1,2,3,4,5,6,7,8)
) as locations;
You don't need the users table at all.

How to perform left outer join with distinct clause

I have the following data:
Users:
Id UserId Name
----------------
1 1 Him
2 10 Her
3 2 Other
Groups:
Id GroupId UserId
-------------------
1 1 1
2 2 2
3 3 10
In SQL I can do something like this to determine if a user is in any group
select *
from Users as u
left outer join (select distinct(UserId) from Groups) as g on u.UserId = g.UserId
The result should be something like this.
Id UserId Name UserId
------------------------
1 1 Him 1
2 10 Her 10
3 2 Other Null
But how can I do this in LINQ?
I think that this one is helpful for u,
var data = (from u in Users
join g in Groups.Where(a => a.UserId == (from gp in Groups.Select(r=>r.UserId).Distinct() ) )
on u.UserId equals g.UserId into outer
from x in outer.DefaultIfEmpty()
select u);
In function syntax, it would look like
var result = Users.Join(Groups,
U => U.UserId, G => G.GroupId,
(U,R) => R.Select(
G => new { Id = U.Id, UserId = U.UserId, Name = U.Name, UserId = G.UserId }
).DefaultIfEmpty(new { Id = U.Id, UserId = U.UserId, Name = U.Name, UserId = null })
);

Mysql_fetch_array( $result, MYSQL_BOTH ) is not exact

I have a weird problem with mysql_fetch_array( $result , MYSQL_BOTH ). This is how the problem is reproduced:
Do a query string that uses LEFT JOIN, GROUP BY
select *,
(A1.total - A2.borrow_total) as remain_total
from ( select Min(sample_created_date) as created_date,
sample_model,
sample_ncc,
count(*) as total ,
sample_sort
from tb_sample
where 1=1
AND sample_kho IN ( 'hanoi','tm_hanoi' )
group by sample_model, sample_sort, sample_ncc
) A1 left join
( select sample_model as sample_model1,
count(*) as borrow_total,
sample_ncc
from tb_sample B1 left join tb_sample_ticket B2
on B1.sample_borrow_status = B2.ticket_id
where 1=1
and ticket_bo_phan_duyet = 'finish'
and ticket_status_duyet = '1'
AND sample_kho IN ( 'hanoi','tm_hanoi' )
group by sample_model, sample_ncc
) A2 on A1.sample_model = A2.sample_model1
AND A1.sample_ncc = A2.sample_ncc left join tb_product_sort A3
on A1.sample_sort = A3.sort_code
where 1=1
order by created_date DESC
LIMIT 0, 30
Implement mysql_fetch_array( $result , MYSQL_BOTH ):
$rw = mysql_fetch_array( $result , MYSQL_BOTH );
Display value of $rw:
echo $rw['sample_cc'];
But there is nothing. Then I try print_r( $rw ), see below for the result:
Array
(
[0] => 2013-06-13 04:10:39
[created_date] => 2013-06-13 04:10:39
[1] => 3G
[sample_model] => 3G
**[2] => Gmobile
[sample_ncc] =>**
[3] => 1
[total] => 1
[4] => SIM
[sample_sort] => SIM
);
I see that $Rw[2] = 3G but $rw['sample_ncc'] is null.
This is a weird problem. Please help me solve it.
Try to give explicit aliases for sample_ncc columns in subqueries A1 and A2 (e.g. sample_ncc_a1 and sample_ncc_a2) or return only one that is on the left side of LEFT JOIN (meaning in A1).