LEFT OUTER JOIN get max() and include NULL values - mysql

I've managed to get the data out and include NULL values by using left outer join. This is my current query:
select s.user, a.id, a.datetime as date, a.total_time
from steam_accounts s
left outer join activity a on a.steam_id = s.id
where s.user_id = 1
This returns this:
Which is almost perfect. But now I need to filter the results with max(a.id) and include null values if there are no matches from the outer join.
Here's what I've tried:
select s.id, s.user, max(a.id), a.datetime as date, a.total_time
from steam_accounts s
left outer join activity a on a.steam_id = s.id
where s.user_id = "1"
Result:
All the null values disappeared. I only wanted to filter out the first two results from the previous query.
This is my desired result:
Any much is much appreciated. Thanks

Alas, MySQL doesn't have OUTER APPLY or LATERAL JOIN, so it will be less efficient, than it could have been. It seems that something like this should produce what you want:
SELECT
s.id
,s.user
,ActivityIDs.MaxActivityID
,activity.datetime as date
,activity.total_time
FROM
steam_accounts s
LEFT JOIN
(
SELECT
a.steam_id
,max(a.id) AS MaxActivityID
FROM activity a
GROUP BY a.steam_id
) AS ActivityIDs
ON ActivityIDs.steam_id = s.id
LEFT JOIN activity ON
activity.id = ActivityIDs.MaxActivityID
WHERE
s.user_id = 1
For each steam_account we find one activity with max ID in the first LEFT JOIN. Then we fetch the rest of activity details using found ID in the second LEFT JOIN.

Use max(coalesce(a.id, 0))
Any aggregation done on results with null will always return null

What I can think of would be using ROW_NUMBER() with partitioning functionality as in SQL Server or PostgreSQL. There's an example how to do this in MySQL here:
http://blog.sqlauthority.com/2014/03/09/mysql-reset-row-number-for-each-group-partition-by-row-number/.
What comes next, I'd partition your result set by user and sort it by date DESCENDING and then take records where ROW_NUMBER is equals to 1.
I've given similar answer here using SQL Server functionality: https://stackoverflow.com/a/30952154/3680098
It should produce you result set as follows:

Use aggregation to calculate the maximum value. Then join in that record using another left join:
select s.user, a.id, a.datetime as date, a.total_time
from steam_accounts s left outer join
activity a
on a.steam_id = s.id left outer join
(select a.steam_id, max(a.id) as maxid
from activity a
group by a.steam_id
) amax
on amax.steam_id = a.steam_id and amax.maxid = a.id
where s.user_id = 1;

Related

How to get orders count sub-queries

I have some difficuties to get orders count with the following SQL query:
select
d.id,
d.title,
count(distinct o.id)
from store s
left join `order` o on o.store_id = s.id
left join order_product op on op.order_id=o.id
left join store_product sp on sp.id = op.product_id
left join product p on p.id = sp.product_id
left join department_category_to_entity dce1 on dce1.entity_type IN ('Product') and dce1.entity_id = p.id
left join department_category_to_entity dce2 on op.status != 'replaced' and
op.replacement_id is null and
dce2.entity_type IN ('StoreProduct') and
dce2.entity_id = sp.id
left join department_category_to_entity dce3 on op.status = 'replaced' and
op.replacement_id is not null and
dce3.entity_type IN ('StoreProduct') and
dce3.entity_id = op.replacement_id
left join department_category dc on dc.id = p.department_category_id or
dc.id = dce1.category_id or
dc.id = dce2.category_id or
dc.id = dce3.category_id
left join department d on d.id = dc.department_id
where d.id is not null
group by d.id;
Is it possible to get orders count without sub-queries or to get correct count of orders? Please, help... Thank you!
You have LEFT JOIN, which says to keep looking even if there is no row in the 'right' table. But, on the other hand, you are GROUPing BY a column in the last of a chain of LEFT JOINs! Perhaps you meant JOIN instead of LEFT JOIN??
Saying where d.id is not null is roughly equivalent to saying "Oops, all those LEFT JOINs could have been JOINs.
With GROUP BY and JOINs (LEFT or otherwise), you are doing an "inflate-deflate". What logically happens is all the JOINing is done to build a huge intermediate table with all the valid combinations. Then the COUNT(*) and GROUP BY are done. This tends to make the COUNTs (and SUMs, etc) have bigger values than expected.
What's the most direct route to get from department to order? It does not seem to involve store, so get rid of that table.
Are other tables irrelevant?
Even after addressing those issue, you still may be getting the wrong value. Please provide, for starters, `SHOW CREATE TABLE for each table.

Improve MySql query left outer joins with subquery

We are maintaining a history of Content. We want to get the updated entry of each content, with create Time and update Time should be of the first entry of the Content. The query contains multiple selects and where clauses with so many left joins. The dataset is very huge, thereby query is taking more than 60 seconds to execute. Kindly help in improving the same. Query:
select * from (select * from (
SELECT c.*, initCMS.initcreatetime, initCMS.initupdatetime, user.name as partnerName, r.name as rightsName, r1.name as copyRightsName, a.name as agelimitName, ct.type as contenttypename, cat.name as categoryname, lang.name as languagename FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
left join Age a on c.ageLimit = a.id
left outer join (
SELECT contentId, createTime as initcreatetime, updateTime as initupdatetime from ContentCMS cms where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId WHERE c.deleted='0' order by c.id DESC
) as temp group by contentId) as c where c.editedBy='0'
Any help would be highly appreciated. Thank you.
Just a partial eval and suggestion because your query seems non properly formed
This left join seems unuseful
FROM ContentCMS c
......
left join (
SELECT contentId
, createTime as initcreatetime
, updateTime as initupdatetime
from ContentCMS cms
where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId
same table
the order by (without limit) in a subquery in join is unuseful because join ordered values or unordered value produce the same result
the group by contentId is strange beacuse there aren't aggregation function and the sue of group by without aggregation function is deprecated is sql
and in the most recente version for mysql is not allowed (by deafult) if you need distinct value or just a rows for each contentId you should use distinct or retrive the value in a not casual manner (the use of group by without aggregation function retrive casual value for not aggregated column .
for a partial eval your query should be refactored as
SELECT c.*
, c.initcreatetime
, c.initupdatetime
, user.name as partnerName
, r.name as rightsName
, r1.name as copyRightsName
, a.name as agelimitName
, ct.type as contenttypename
, cat.name as categoryname
, lang.name as languagename
FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
WHERE c.deleted='0'
) as temp
for the rest you should expiclitally select the column you effectively need add proper aggregation function for the others
Also the nested subquery just for improperly reduce the rows don't help performance ... you should also re-eval you data modelling and design.

Sub Query Not IN With Left Join Alternative?

I have Query Like This
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
where f.ACCOUNT_ID NOT IN(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
)
group by f.ACCOUNT_ID;
That Sub Query work but To Slow so I change it with Left Join, it work faster
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
left join(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
)subb on subb.ACCOUNT_ID = f.ACCOUNT_ID
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
group by f.ACCOUNT_ID;
But, My issue is Still Contain wrong Result,
With Not IN, query 1 select where NOT IN query 2.
How can I get like NOT IN query with left join.
Can Anyone Help Me?
thanks.
You have to add a WHERE clause to filter by the results of your LEFT JOIN. If you add an appropriate WHERE clause WHERE subb.ACCOUNT_ID IS NULL, it should work as expected (since you used a GROUP BY in your subquery, there's no danger of duplicate rows):
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
left join(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
) subb on subb.ACCOUNT_ID = f.ACCOUNT_ID
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
WHERE sub.ACCOUNT_ID IS NULL
group by f.ACCOUNT_ID;
Update
The goal of our LEFT JOIN is to find all rows in our forecast table that don't have a matching row in the subquery. Therefore, we need a WHERE clause that removes all rows with a matching row - WHERE sub.ACCOUNT_ID IS NULL fits quite nicely.
SO user #quassnoi has written a wonderful comparison of different methods to achieve this goal.

Left join latest inspection date to establishments using MySQL

I have a table of establishments and I want to return a result set with the latest inspection date from the inspections table. Right now I have:
SELECT business_table.business_name, business_table.address, inspection_table.date
FROM business_table
LEFT JOIN inspection_table ON business_table.id = inspection_table.business_id
WHERE inspection_table.date = (
SELECT MAX(date)
FROM inspection_table)
The problem is I get only one result with the latest inspection date. I need all of the establishments returned. The query will need to be efficient because I have about 600K establishments and 3Million inspections.
You are using an outer join. When a business record has no matching inspection record, then an empty inspection record is created an joined instead. So that outer-joined inspection record will have all columns NULL. Then you have WHERE inspection_table.date = (...). This dismisses all outer-joined records again, because NULL will never match.
Use AND instead, to make the condition part of the WHERE clause:
SELECT
b.business_name,
b.address,
i.date
FROM business_table b
LEFT JOIN inspection_table i
ON i.business_id = b.id
AND i.date = (SELECT MAX(date) FROM inspection_table);
Ah, you are not looking for the latest inspection date at all. You are looking for the latest inspection date per business.
In your solution you are still using an outer join that doesn't work. Don't do that. Either use an outer join and use it properly or use an inner join if you are fine with that.
Here is how to get the latest inspection per business with an outer join (so you also show businesses that have had no inspection, yet):
SELECT
b.business_name,
b.address,
i.date,
...
FROM business_table b
LEFT JOIN inspection_table i
ON i.business_id = b.id
AND (i.business_id, i.date) IN
(
SELECT business_id, MAX(date)
FROM inspection_table
GROUP BY business_id
);
Same with joins:
SELECT
b.business_name,
b.address,
i.date,
...
FROM business_table b
LEFT JOIN
(
SELECT business_id, MAX(date) AS date
FROM inspection_table
GROUP BY business_id
) latest ON latest.business_id = b.id
LEFT JOIN inspection_table i
ON i.business_id = latest.business_id
AND i.date = latest.date;
I ended using the following:
SELECT business_table.business_name AS Name, business_table.address AS Address, business_table.city AS City, business_table.state AS Province, inspection_table.rating AS Rating, inspection_table.date AS "Inspected"
FROM business_table
LEFT JOIN inspection_table ON business_table.id = inspection_table.business_id
WHERE inspection_table.date = (
SELECT MAX(inspection_table.date)
FROM inspection_table
WHERE business_table.id = inspection_table.business_id)
ORDER BY inspection_table.date DESC

Sql query within an inner join

i have this mysql statement :
SELECT ca.*, MAX(ca.id), v.*,a.submit_dt from callback_holding ca
inner join valuations v on v.Ref = ca.ref
inner join answer a on a.title = ca.ref
where v.Consultant = '$user' and ca.isholding = 2
GROUP BY ca.ref DESC order by ca.reccomendeddate asc
But the problem is if there is not an entry in "answer" then it doesn't show up in the list. What is the correct way to bring back everything and just "null" if there is nothing in the "answer" table?
Thanks
Your query has several problems. First, you are grouping by the ref column from the callback_holding table, but are selecting non aggregate columns not only from this table, but from other tables. To get around this, you should do the aggregation to find maximum IDs in callback_holding in a subquery, and then join it to the other tables.
Next, you mentioned that if no answer be found, you get back no records. This is the nature of an INNER JOIN, but if you switch the join to answer to use a LEFT JOIN, then no records up to that point in the query will be lost. Note that I used COALESCE(a.submit_dt, 'NA') to display NA in the event that this column from the answer table be NULL. If this column be datetime, then you should use a suitable default value, e.g. NOW().
SELECT ca.*,
v.*,
COALESCE(a.submit_dt, 'NA') AS submit_dt, -- display 'NA' if no answer
t.max_id
FROM callback_holding ca
INNER JOIN
(
SELECT ref, MAX(id) AS max_id
FROM callback_holding
GROUP BY ref
) t
ON t.ref = ca.ref AND
t.max_id = ca.id
INNER JOIN valuations v
ON v.Ref = ca.ref
LEFT JOIN answer a
ON a.title = ca.ref
WHERE v.Consultant = '$user' AND
ca.isholding = 2
ORDER BY ca.reccomendeddate
try with:
SELECT ca.*, MAX(ca.id), v.*,a.submit_dt from callback_holding ca
INNER join valuations v on v.Ref = ca.ref
LEFT join answer a on a.title = ca.ref
WHERE v.Consultant = '$user' and ca.isholding = 2
GROUP BY ca.ref DESC order by ca.reccomendeddate asc