Inner join except for one case - php

I have a following statistics table:
id | date | user_id | price | additional_price
My query looks like this:
SELECT month, sum(price), sum(additional_price)
FROM statistics s
INNER JOIN users u on s.user_id = u.id
AND u.confirmed = 1
GROUP BY MONTH(date);
The problem is that there are rows in this table where price is equal to 0, but the additional_price is greater than 0, where users are not confirmed, or don't exist anymore. I want to count them into the output sum. Something like:
SELECT t.month, sum(t.price), sum(t.additional_price)
FROM (
(SELECT month, sum(price), sum(additional_price)
FROM statistics s
INNER JOIN users u on s.user_id = u.id
WHERE u.confirmed = 1
AND s.price > 0
GROUP BY MONTH(date))
UNION
(SELECT month, sum(price), sum(additional_price)
FROM statistics s
WHERE price = 0
GROUP BY MONTH(date))
) AS t
GROUP BY MONTH(t.date);
It will work, that's the result I want to achieve. But the query is slow, it's big, and unmaintainable. The main issue I'm having with this is that I can't use relations on a table like that:
$statistics = Statistic::join('users');
$statisticsForZeroPrice = Statistic::forZeroPrice();
$result = DB::query()->fromSub($statistics->union($statisticsForZeroPrice), 't')
->with('user') // will not work
->groupBy('t.date')
->get();
Is there a simpler solution to this issue?

I am guessing that you want a left join. From the limited information in your question, something like this:
SELECT month, sum(price), sum(additional_price)
FROM statistics s LEFT JOIN
users u
ON s.user_id = u.id AND u.confirmed = 1
GROUP BY MONTH(date);
It is unclear which table should be first in the left join.

SELECT month, sum(price), sum(additional_price)
FROM statistics s
LEFT JOIN users u on s.user_id = u.id
WHERE u.confirmed = 1 OR price = 0
GROUP BY MONTH(date);

What about this?
SELECT month, sum(price), sum(additional_price)
FROM statistics s
INNER JOIN users u on s.user_id = u.id
WHERE (u.confirmed = 1 AND s.price > 0) OR (u.confirmed = 0 AND s.price = 0)
GROUP BY MONTH(date);

Your 2 unioned queries differ only in the conditions of the WHERE clause.
Why not combine them like this:
SELECT MONTH(date) month, SUM(price), SUM(additional_price)
FROM statistics s INNER JOIN users u
ON s.user_id = u.id
WHERE (u.confirmed = 1 AND s.price > 0) OR (s.price = 0)
GROUP BY MONTH(date);

Related

Get SUM in related table. If not found get 0 as sum

I have this query:
$sql = "SELECT m.id FROM members m
LEFT JOIN orders o ON o.user_id = m.id AND
YEAR(date) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH) AND
MONTH(date) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
LEFT JOIN transactions t ON t.user_id = o.user_id";
I need to get sum of transactions.points, so I do this:
$sql = "SELECT m.id, COALESCE(SUM(t.points), 0) AS total_points FROM members m
LEFT JOIN orders o ON o.user_id = m.id AND
YEAR(date) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH) AND
MONTH(date) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
LEFT JOIN transactions t ON t.user_id = o.user_id";
But when i execute this, I will get only rows with some transactions (and orders). What I must to do for getting 0 as total_points when no transactions found?
Text description: I want to get sum of points reward (from transactions table) of orders (orders table), where order was created in last month.
Thanks!
Get the sum of points per user and use it in the left join to get 0 when the user has no transactions.
SELECT m.id, COALESCE(t.total_points, 0) AS total_points
FROM members m
LEFT JOIN orders o ON o.user_id = m.id AND
/* use this instead of functions which prevent indexes on the date column from being used */
o.date >= date '2016-11-01' AND o.date <= date '2016-11-30'
LEFT JOIN (select user_id, SUM(points) total_points
from transactions group by user_id) t ON t.user_id = o.user_id
You need to add a GROUP BY clause to your query without which you are actually performing SUM() on the entire resultset. Not sure on which column but say on m.id
$sql = "SELECT m.id, COALESCE(SUM(t.points), 0) AS total_points FROM members m
LEFT JOIN orders o ON o.user_id = m.id AND
YEAR(date) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH) AND
MONTH(date) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)
LEFT JOIN transactions t ON t.user_id = o.user_id
GROUP BY m.id";

LEFT JOIN Query With Condition

I have a query that LEFT JOINS another table to group rows. I am trying to only select records from the first table "googleimage" where the user_id is a certain number. (user_id is a column in "googleimage" table.
SELECT g.*
FROM googleimage g
LEFT JOIN (SELECT image_id, COUNT(*) AS cnt
FROM googleimagefound WHERE status = 0
GROUP BY image_id) gf ON gf.image_id = g.id
ORDER BY COALESCE(cnt, 0) DESC");
I have tried adding a new ON statement beiside the
gf ON gf.image_id = g.id
I have also tried changing
SELECT g.*
FROM googleimage g
to
SELECT g.*
FROM googleimage WHERE user_id = 1 g
but none of them seem to work, any help will be appriciated
Try changing you query a bit like below
SELECT g.*
FROM googleimage g
LEFT JOIN (SELECT image_id, COUNT(*) AS cnt
FROM googleimagefound WHERE status = 0
GROUP BY image_id) gf ON gf.image_id = g.id
WHERE g.user_id = 1 <-- add this condition
ORDER BY COALESCE(gf.cnt, 0) DESC;

select count of rows from 2 tables and merge into one row (mysqli)

i create a web app like facebook by php and mysqli
in my app i have a table for posts , one table for likes , and one table for comments
i want to get the number of comments and likes of each post in one row with his post_id!!!
i try some querys likes this :
select `tblpost`.`post_id`, COALESCE(TCOMM.`comment_num`,0) as `c_num`, COALESCE(TLIKE.`like_num`,0) as `l_num`
from
(select `tblpost`.`post_id`, count(*) as `like_num` from `tblpost` join `tbllikes` on `tbllikes`.`post_id` = `tblpost`.`post_id` group by `tblpost`.`post_id`
) TLIKE
inner join
(select `tblpost`.`post_id`, count(*) as `comment_num` from `tblpost` join `tblcomments` on `tblcomments`.`post_id` = `tblpost`.`post_id` group by `tblpost`.`post_id`) TCOMM
on
TCOMM.`post_id` = TLIKE.`post_id`
but i don't know what's my problem
You can do count distincts with two left joins.
Something like this would work if there are fields like_id and comment_id in the tables tbllikes and tblcomments
SELECT
tblpost.post_id AS post_id,
COUNT(DISTINCT tbllikes.like_id) AS likes,
COUNT(DiSTINCT tblcomments.comment_id) AS comments
FROM tblpost
LEFT JOIN tbllikes ON tbllikes.post_id = tblpost.post_id
LEFT JOIN tblcomments on tblcomments.post_id = tblpost.post_id
GROUP BY tblpost.post_id
First, I think you can greatly simplify your query:
select l.post_id,
COALESCE(c.comment_num, 0) as c_num, COALESCE(l.like_num, 0) as l_num
from (select l.post_id, count(*) as like_num
from tbllikes l
group by l.post_id
) l inner join
(select c.post_id, count(*) as comment_num
from tblcomments c
group by c.post_id
) c
on l.post_id = c.post_id;
This will only get you posts that have both likes and comments. To get what you want, use a left join:
select p.post_id,
COALESCE(c.comment_num, 0) as c_num, COALESCE(l.like_num, 0) as l_num
from tblpost p left join
(select l.post_id, count(*) as like_num
from tbllikes l
group by l.post_id
) l
on l.post_id = p.post_id left join
(select c.post_id, count(*) as comment_num
from tblcomments c
group by c.post_id
) c
on c.post_id = p.post_id;

MySQL Query left join repeated records

With a Left Join i have this result.
Here the screen
http://f.cl.ly/items/373Y141r1K131d0n3f1q/Schermata%202013-04-01%20alle%2016.51.18.png
i want to show only record once time, without repeat it, but with a left join all my records are different.
what i have to do for show once all my records?
the query.
SELECT * FROM login_users
LEFT JOIN login_users_seguaci
ON login_users.user_id = login_users_seguaci.following
WHERE name LIKE ""
AND user_id != '1'
ORDER BY data DESC
SELECT x.*, y.*
FROM login_users x
LEFT JOIN
(
SELECT a.*
FROM login_users_seguaci a
INNER JOIN
(
SELECT following, MAX(DATA) max_data
FROM login_users_seguaci
GROUP BY following
) b ON a.following = b.following AND
a.DATA = b.max_date
) y ON x.user_id = y.following
// WHERE ... your condition here ...
ORDER BY t.data DESC

Newest loan per user for all users

// Edit:
All work fine with:
SELECT u.*,
l.cod AS loans_cod,
l.step AS loans_step
FROM users AS u
LEFT JOIN loans AS l
ON u.id = l.users_id
WHERE l.step < 12
OR NOT EXISTS
(SELECT l.id
FROM loans
WHERE l.users_id = u.id
)
GROUP BY u.id
Now, I can select all user, and his last loan. Thanks, and I think my solution will help future users.
if you want the last loan, you could append something like this
ORDER BY l.date DESC GROUP BY u.id
SELECT u.*,
l.cod AS loans_cod,
l.step AS loans_step
FROM users AS u
LEFT JOIN loans AS l
ON u.id = l.users_id
WHERE u.id != :id
AND l.timestamp = (SELECT MAX(l.timestamp)
FROM loans AS l2
WHERE l2.users_id = l.users_id)
Where timestamp is a column on your table that indicates when the loan was created or otherwise gives you a time that you can compare to.
Try this:
SELECT u.*, l.cod AS loans_cod,
l.step AS loans_step
FROM users AS u inner join
(
select max(l.loan_id) as loan_id, l.users_id , l.cod, l.step
from loans l
group by
l.users_id,l.cod,l.step
) as l
ON u.id = l.users_id
WHERE u.id != :id
Note: Avoid doing select * it's a very bad practice. List all the columns you need only.

Categories