MySQL Left Outer Join & Count - 'Where'? - mysql

I have a relatively simple query that returns a user profile, together with 2 counts related to that user (stream events & inventory);
SELECT u.*, r.regionName, COUNT(i.fmid) AS invcount, COUNT(s.fmid) AS streamcount
FROM fm_users u
JOIN fm_regions r
ON u.region=r.regionid
LEFT OUTER JOIN fm_inventory i
ON u.fmid=i.fmid
LEFT OUTER JOIN fm_stream s
ON u.fmid=s.fmid
WHERE u.username='sampleuser'
Both the inventory & stream values could be zero, hence using a left outer join.
However, the values for both currently return numbers for all users, not the specific user (always identified as integer 'fmid' in each table). This is obviously because the query doesn't specify a 'where' for either count - but I'm not sure where. If I change the last part to this;
WHERE u.username='sampleuser' AND s.fmid=u.fmid AND i.fmid=u.fmid
GROUP BY u.fmid
It still returns incorrect numbers, albeit 'different' numbers depending on the search criteria - and the name number for both invcount and streamcount.

Your query cross joins the fm_inventory and fm_stream tables for each user. For example if a specific user has 2 matching fm_inventory rows, and fm_stream also has two, the result will have 4 (=2 x 2) rows and each will be counted in both COUNT() functions.
The way to accomplish what you are doing is to use correlated subselects for the counts.
SELECT u.*, r.regionName,
(select COUNT(*) from fm_inventory i
where i.fmid = u.fmid) AS invcount,
(select COUNT(*) from fm_stream s
where s.fmid = u.fmid) AS streamcount
FROM fm_users u
JOIN fm_regions r
ON u.region=r.regionid
WHERE u.username='sampleuser'
This allows the counts to be independent of each other. This works even if more than just one user is selected. In that case you need a group by clause, but otherwise it is the same syntax.

When you mix normal columns with aggregates, include all normal columns in the GROUP BY. In this particular case, you are missing the r.regionName in the GROUP BY.
See longer explanation.

Related

SQL LEFT JOIN with possible join condition duplicate match

Here is my query so far:
SELECT
b.cs_bidding_id, b.cs_bidding_user_id,
floor(AVG(u.cs_rating)) AS cs_user_rating
FROM cs_biddings b LEFT JOIN
cs_user_ratings u ON u.cs_user_rated_id = b.cs_bidding_user_id
I would like to get the avg rating of the user's per bidding post.
However this does not work for multiple biddings because whenever the join condition is satisfied, it wont let me fetch other avg rating for other biddings that shares the same bidding_user_id
desired result:
Unsummarized Query:
SELECT
b.cs_bidding_id,
b.cs_bidding_title,
b.cs_bidding_details,
b.cs_bidding_user_id,
b.cs_bidding_permalink,
b.cs_bidding_added,
b.cs_bidding_picture,
b.cs_bidding_status,
b.cs_bidding_location,
floor(AVG(u.cs_rating)) AS cs_owner_rating
FROM cs_biddings b LEFT JOIN
cs_user_ratings u ON u.cs_user_rated_id = b.cs_bidding_user_id
Your query is malformed. It is an aggregation query (because of the AVG()) but the SELECT columns are inconsistent with the aggregation columns (well, there are none of those).
Fixing the group by might fix your problem:
SELECT b.cs_bidding_id, b.cs_bidding_user_id,
floor(AVG(u.cs_rating)) AS cs_user_rating
FROM cs_biddings b LEFT JOIN
cs_user_ratings u
ON u.cs_user_rated_id = b.cs_bidding_user_id
GROUP BY b.cs_bidding_id, b.cs_bidding_user_id;
I'm not sure if you want both columns in the GROUP BY (and the result set). However, without sample data and desired results, it is unclear what you actually intend.

i want to get all column of Accounts table with this query but it is giving error

select Accounts.name, Accounts.regno Accounts.model , Accounts.slacc, count (servicing.dt) as total
from Accounts l,eft
outer join servicing on Accounts.slacc = servicing.slacc
group by Accounts.slacc,Accounts.name
The error message is
Major Error 0x80040E14, Minor Error 25515
> select Accounts.name,Accounts .model , Accounts.regno, Accounts.slacc, count (servicing.dt) as total from Accounts left outer join servicing on Accounts.slacc = servicing.slacc group by Accounts.slacc,Accounts.name
In aggregate and grouping expressions, the SELECT clause can contain only
aggregates and grouping expressions. [ Select clause = Accounts,model ]
Your query has a group by clause. If you use a group by clause in the query, then every column in the select statement has to do one of two things - either it has to be part of the group by list, or it has to be an aggregate of some kind (Sum, Count, Avg, Max, etc). If you don't do this, SQL doesn't know what to do with the column. In your case Accounts.regno and Accounts.model are listed in the select, but they are not in the group by clause and they are not aggregates - hence your error.
Assume for the moment you have two account records with the same account name and slacc, but different Regno (or model). The group by clause says they have to be joined into one record for display, but you haven't told SQL how to do that. It doesn't matter if the data isn't like that, SQL looks for possible errors first.
In this case, you probably just want all the details grouped. The simplest way is just to make sure you add all the columns needed to the group by, like this
select Accounts.name, Accounts.regno, Accounts.model, Accounts.slacc, count(servicing.dt) as total
from Accounts
left outer join servicing on Accounts.slacc = servicing.slacc
group by Accounts.slacc, Accounts.name, Accounts.regno, Accounts.model
This will fix the error, but does extra grouping you don't need, and would get very cumbersome if you had a lot more columns you wanted from account, as you'd have to add them all. Another way to handle it is to use the minimum amount of columns for the group query, then join the result of that to your main query to get the other columns. This would probably look something like this
select Accounts.name, Accounts.regno, Accounts.model, Accounts.slacc, Totals.Total
from Accounts
left outer join
( Select slacc, count(dt) as total
from servicing
group by slacc
) Totals on Totals.slacc = Accounts.slacc

OUTER JOIN -> want to return something even if empty

I try to return a group_concat on 2 tables
One being my list of schools and the other, some numeric data.
For some dates, i have NO DATA at all in the table SimpleData and so my lEFT OUTER JOINS returns 10 results where i have 11 schools (i need 11 rows for javascript treatment in order too)
here is my query (tell me if i need to give more details about tables
SELECT A.nomEcole,
A.Compteur,
IFNULL(SUM(B.rendementJour), '0') AS TOTAL,
B.jourUS,
B.rendementJour
FROM ecoles A LEFT OUTER JOIN SimpleData B ON A.Compteur = B.compteur
WHERE jourUS LIKE '2013-07-%'
GROUP BY ecole
in this example, i have no data in SimpleData for this month( not data was recorded at all)
I have to show either NULL or '0' for this missing school and i'm starting to lose my head on something easy apparently :(
Thanks for any help !
olivier
As one way is mentioned by #Abhik Chakraborty where will filter out the records which doesn't match the criteria ,another is you can use CASE statement
SELECT A.nomEcole,
A.Compteur,
SUM(CASE WHEN jourUS LIKE '2013-07-%' THEN B.rendementJour ELSE 0 END) AS TOTAL,
B.jourUS,
B.rendementJour
FROM ecoles A
LEFT OUTER JOIN SimpleData B ON A.Compteur = B.compteur
GROUP BY ecole
I suspect you just need to move the where condition to the on clause:
SELECT A.nomEcole, A.Compteur, IFNULL(SUM(B.rendementJour), 0) AS TOTAL,
B.jourUS, B.rendementJour
FROM ecoles A LEFT OUTER JOIN
SimpleData B
ON A.Compteur = B.compteur and b.jourUS >= '2013-07-01' and b.jourUS < '2013-08-01'
GROUP BY A.ecole;
Some other changes:
Don't use single quotes for numeric constants. Single quotes should really only be used for date and string constants.
Don't use like for dates. like is an operation on strings, not dates, and the date has to be implicitly converted to a string. Instead, do direct comparisons on the date ranges you are interested in.
I would also recommend that the table aliases be abbreviations for the tables you are using. This makes the query easier to read. (So e instead of A for ecoles.)
Also note that the values that you are returning for JourUS and RendementJour are indeterminate. If there are multiple rows in the B table that match, then an arbitrary value will be returned. Perhaps you want max() or group_concat() for them.
Your WHERE clause turns the LEFT OUTER JOIN into an INNER JOIN, because outer-joined records values are NULL and NULL is never LIKE '2013-07-%'.
This is the reason you must move jourUS LIKE '2013-07-%' to the ON clause, because you only want to join records where jourUS LIKE '2013-07-%' and otherwise outer join a null record.

simple joins between 2 mysql tables returning all results every time.. Help!

I just imported a large amount of data into two tables. Let's call them shipments and returns.
When trying to do a simple join (left or inner) based on any criteria in these two tables. query looks like it tries to do a cross join or find every combination instead of what the query should be pulling.
each table has an PK id field, but there is not FK relationship between the two other than some shared field.
I'm currently just trying to related them on shipment_id.
I feel this is a simple answer. Am I missing a reference or something obvious that is causing this? Thanks!
here's an example. This should returned under 100 rows. This instead returns hundreds of thousands.
SELECT r.*
FROM returns as r
left outer join shipments as s
on r.shipment_id = s.shipment_id
where r.date = '2011-06-20'
Here is a query that should work:
SELECT T0.*, T1.*
FROM shipments AS T0 LEFT JOIN returns AS T1 ON T0.shipment_id = T1.shipment_id
ORDER BY T0.shipment_id;
This query join assumes 1:1 on the shipment_id
It would be nice if you included the query you were using
You need to specify what you are joining on, otherwise it will do a cartesian join:
SELECT r.*
FROM returns as r
LEFT JOIN shipments as s ON s.shipment_id = r.shipment_id
where r.date = '2011-06-20'
Josh,
I would be interested in seeing what would happen if you forced a join to a specific record or set of records instead of the whole table. Assuming there is a shipment with an id of 5 in your table, you could try:
SELECT r.* FROM returns as r
left join shipments as s
ON 5 = r.shipment_id
WHERE r.date = '2011-06-20'
While just a fancy where clause, it would at least prove that the join you are attempting will eventually work correctly. The issue is that your on clause is always returning true, no matter what the value is. This could be because it's not interpreting the shipment_id as an integer, but instead as a true/false variable where any value evaluates to true.
Original Rejected Solution:
No Foreign Key relationship should be needed in order to make the joins happen. The PK id fields I'm assuming are an integer (or number, or whatever your rdms equivalent is)?
Can you past a snippet of your sql query?
Updating based on posted query:
I would add your explicit join criteria in order to rule out any funny business (my guess is since no criteria is specified, it's using 1=1, which always joins). So I would change your query to look like:
SELECT r.*
FROM returns as r
left join shipments as s ON
s.ShipId = R.ReturnId
where r.date = '2011-06-20'
The issue turned out to be very simple, just not readily apparent until going through all the columns. It turns out that the shipment ID was duplicated through every row as it hit the upper limit for the int datatype. This is why joins were returning every record.
After switching the datatype to bigint and reimporting, everything worked great. Thanks all for looking into it.

Losing Records in a Left join after using Group by

Basically after executing this query:
SELECT
`view_customer_locations`.customerid,
`view_customer_locations`.community_groupid,
`view_customer_locations`.community_group,
`view_sip_user_agents`.sip_user_agentid,
`view_sip_user_agents`.didid,
`view_sip_user_agents`.temporary_didid,
`view_sip_user_agents`.active_did,
GROUP_CONCAT( (IF(`view_sip_user_agents`.active_did = 'permanent', cast(`permanent_dids`.did as char(10)), cast(`temporary_dids`.did as char(10)))) SEPARATOR ', ') as did,
`view_sip_user_agents`.sip_user_agents_date_archived
FROM `view_customer_locations`
LEFT JOIN `view_sip_user_agents` on `view_customer_locations`.customerid = `view_sip_user_agents`.customerid
LEFT JOIN `dids` AS permanent_dids ON `view_sip_user_agents`.didid = `permanent_dids`.id
LEFT JOIN `dids` AS temporary_dids ON `view_sip_user_agents`.temporary_didid = `temporary_dids`.id
Group by `view_customer_locations`.customerid
i still want all the rows from the view_customer_locations table.. but i am losing any entries in the view_customer_locations table that don't have a corresponding record in the view_sip_user_agents table. I also want the entries to be grouped by customerid .. so that each customer only has one entry in the resulting query.
If i remove the group by clause, i get all the entires from the view_customer_locations table but naturally i have multiple entries per customer which is not what i want.
please help
Although MySQL does let you "get away" with expressing a GROUP BY clause with fields in the SELECT tha that might conceivable vary over the GROUP BY fields (theoretically picking an "arbitrary/random value"), the results of this ill-conceived, logically not well-founded operation are sometimes surprising, as you've noticed.
Try using correct SQL, e.g. with a MAX operator over the fields you're not "grouping by". If the implied assumption that those fields are strictly determined by the grouped-by fields is right, this can't possibly damage your results in any way, right? And yet sometimes you'll find that results do appear, or change (meaning the implied assumption was, simply, wrong).
In your case, since some of the fields might be uniformly NULL in a group, and MAX in that case is not necessarily well-defined, you might further try to use IFNULL there, of course.
I don't think GROUP BY is what you really want here. DISTINCT is more correct because it will eliminate duplicates but the results are defined on the non-grouped by fields
SELECT DISTINCT
`view_customer_locations`.customerid,
`view_customer_locations`.community_groupid,
`view_customer_locations`.community_group,
`view_sip_user_agents`.sip_user_agentid,
`view_sip_user_agents`.didid,
`view_sip_user_agents`.temporary_didid,
`view_sip_user_agents`.active_did,
GROUP_CONCAT( (IF(`view_sip_user_agents`.active_did = 'permanent', cast(`permanent_dids`.did as char(10)), cast(`temporary_dids`.did as char(10)))) SEPARATOR ', ') as did,
`view_sip_user_agents`.sip_user_agents_date_archived
FROM `view_customer_locations`
LEFT JOIN `view_sip_user_agents` on `view_customer_locations`.customerid = `view_sip_user_agents`.customerid
LEFT JOIN `dids` AS permanent_dids ON `view_sip_user_agents`.didid = `permanent_dids`.id
LEFT JOIN `dids` AS temporary_dids ON `view_sip_user_agents`.temporary_didid = `temporary_dids`.id
Group by `view_customer_locations`.customerid