I have a nested SQL query that is exhibiting results which I can't understand. The query joins the PARTNER and USER tables via the PARTNER_USER table. A partner is basically a collection of users, and the objective of this query is to figure out when the 20th user registered with the partner that has ID 34:
select p.partner_id id,
u.created_on launch_date
from user u join partner_user pu
using (user_id) join partner p
using (partner_id)
where p.partner_id = 34
and u.user_id =
(select nu.user_id
from user nu
join partner_user npu using (user_id)
join partner np using (partner_id)
where np.partner_id = 34
order by nu.created_on limit 19, 1)
However, if I change the 2nd last line to
where np.partner_id = p.partner_id
The query fails with the error message "Subquery returns more than 1 row".
Why does the first query work, but not the second? They look equivalent to me.
Thanks,
Don
JPunyon is right. One or the other query has to run first, and then have its results trimmed after the fact.
If you look at the queries as written, the outer query has to know the result of the inner query to apply its where clause. However, when you specify
where np.partner_id = p.partner_id
in the inner query, then you're trying to make the inner query know the result of the outer query to apply its where clause as well. That's a circular dependency.
As a human, you can read the query and you can tell that in this particular case, you're asking for one particular value in the where clause in the outer query and you're asking to use that same value in the inner query, so it seems as though the database should see that and use the same literal value from the outer query.
In reality, the inner query is simply run first without knowing the possible values of p.partner_id, hence the "multiple rows" error.
Whe you use the = operator to compare with the results of a subquery, your subquery may return only a single row.
if you want to check for all rows that are returned by the subquery, you have to use the IN operator.
AND u.User_Id IN ( SELECT .... )
When you change the where clause in the sub query you're letting the floodgates open. The where clause in the main query doesn't restrict the subquery. So you're getting more than 1 result.
EDIT: What database is this? I've not come across the "using" construct before...
#Jason Punyon
mysql supports the USING construct.
Related
I'm trying to run a query on a table to see how many unique users have a usage record in the system at a given point. I've been working with the following query, but I've yet to see a proper result.
SELECT count(distinct usageUser), divisionName
FROM records R
INNER JOIN locate L
ON L.computerID=R.usageComputerID
WHERE R.usageWhen LIKE "2012-07-08T12:%"
GROUP BY L.divisionName;
Currently the query returns 18, for each division in the joined table. Without the GROUP BY clause I get the same number of records.
EDIT:
I ran the query again, with suggestions from a comment. By removing the group by and count clause, I get this this (too big to post). This data is very poorly formatted, unfortunately it's inherited and fairly large.
It is not possible for these users to have used every lab like it's listed.
SELECT count(*) cnt, L.divisionName, R.usageUser
FROM records R
INNER JOIN locate L
ON L.computerID=R.usageComputerID
WHERE R.usageWhen LIKE "2012-07-08T12:%"
GROUP BY L.divisionName,R.usageUser;
I'm having an issue getting this SQL query to work properly.
I have the following query
SELECT apps.*,
SUM(IF(adtracking.appId = apps.id AND adtracking.id = transactions.adTrackingId, transactions.payoutAmount, 0)) AS 'revenue',
SUM(IF(adtracking.appId = apps.id AND adtracking.type = 'impression', 1, 0)) AS 'impressions'
FROM apps, adtracking, transactions
WHERE apps.userId = '$userId'
GROUP BY apps.id
Everything is working, HOWEVER for the 'impressions' column I am generating in the query, I am getting a WAY larger number than there should be. For example, one matching app for this query should only have 72 for 'Impressions' yet it is coming up with a value of over 3,000 when there aren't even that many rows in the adtracking table. Why is this? What is wrong here?
Your problem is you have no join conditions, so you are getting every row of every table being joined in your query result - called a cartesian product.
To fix, change your FROM clause to this:
FROM apps a
LEFT JOIN adtracking ad ON ad.appId = a.id
LEFT JOIN transactions t ON t.adTrackingId = ad.id
You haven't provided the schema for your tables, so I guessed the names of the relevant columns - you may have to adjust them. Also, your transaction table may join to adtracking - it's impossible to know from your question, so agin you have have to alter things slightly. Hopefully you get the idea.
Edit:
Note: your group-by clause is incorrect. You either need to list every column of apps (not recommended), or change your select to only select the id column from apps (recommended). Change your select to this:
SELECT apps.id,
-- rest of query the same
Otherwise you'll get weird, incorrect, results.
SELECT cf.FK_collection, c.collectionName,
uf.FK_userMe, uf.FK_userYou,
u.userId, u.username
FROM userFollows as uf
INNER JOIN collectionFollows as cf ON uf.FK_userMe = cf.FK_user
INNER JOIN collections as c ON cf.FK_collection = c.collectionId
INNER JOIN users as u ON uf.FK_userYou = u.userId
WHERE uf.FK_userMe = 2
Hey guys.
I'm trying to make this query, and it of course won't do as I want it to, since it's returning multiple rows which is in some way what I want, and yet it's not. Let me try to explain:
I trying to get both collectionFollows and userFollows, for showing a users activity on the site. But when doing this, I will have multiple rows from userFollows even tho a user only follows 1. This occurs because I'm following multiple collectionFollows.
So when I show my result it will return like this:
John is following 'webdesign'
John is following 'Lisa'
John is following 'programming'
John is following 'Lisa'
I would like to know if I have to make multiple queries or use an subquery? What would be best practice? And how would I write the query then?
You are actually combining two quite unrelated queries. I would keep them as separate queries, especially since you report them like that too. You could, if you like, use UNION ALL to combine those queries. This way, you have just a list of names of items you follow, regardless of the type of item it is. If you want, you can specify that too.
SELECT
cf.user,
cf.FK_collection as followItem,
c.collectionName as followName,
'collection' as followType
FROM collectionFollows as cf
INNER JOIN collections as c ON cf.FK_collection = c.collectionId
WHERE cf.user = 2
UNION ALL
SELECT
uf.FK_userMe,
u.userId,
u.username
'user' as followType
FROM userFollows as uf
INNER JOIN users as u ON uf.FK_userYou = u.userId
WHERE uf.FK_userMe = 2
An alternative would be to filter unique values in PHP, but even then your query will fail. Because of the inner joins, you will not get any results if a user only follows other users or only follows collections. You need at least one of both to get any results.
You could change INNER JOIN to LEFT JOIN, but then you would still have to post-process the query to filter doubles and filter out the NULL values.
UNION ALL is fast. It just sticks two query results together without furthes processing. This is different from UNION, which will filter double as well (like DISTINCT). In this case, it is not needed, because I assume a user can only follow a collection or other user once, so these queries will never return duplicate records. If that is indeed the case, UNION ALL will do just fine and will be faster than UNION.
Apart from UNION ALL, two separate queries is fine too.
I am fairly new at MySQL and I am working with a database system which has four main tables, described here:
http://www.pastie.org/3832181
The table that this query primarily works with is here:
http://www.pastie.org/3832184
Seems fairly simple right?
My query's purpose is to grab all the BusinessID's for a explicit User where the OpportunityID's are NULL, once it has those BusinessID's, I want it to find the associated BusinessName in the Business table and match that BusinessName with the BusinessName(Business) in the EmploymentOpportunity table.
This is my query to perform that action.
SELECT EmploymentOpportunity.OpportunityID, Business, Description
FROM UserBusinessOpportunity, Business, EmploymentOpportunity
WHERE UserBusinessOpportunity.BusinessID =
(SELECT UserBusinessOpportunity.BusinessID
FROM UserBusinessOpportunity
WHERE UserBusinessOpportunity.UserID=1 AND
UserBusinessOpportunity.OpportunityID is NULL)
AND UserBusinessOpportunity.BusinessID = Business.BusinessID
AND Business.BusinessName = EmploymentOpportunity.Business;
The sub-select statement is supposed to return the subset of BusinessID's. I'm sure this is an extremely simple query, but it keeps giving me duplicate results and I'm sure why. The set of results should be 3, but it's sending me 24, or 8 repeating sets of those 3.
Thanks, if you can help me figure this out.
Use distinct keyword to remove duplicates.
Have a look here
You might be missing a constraint somewhere when you are doing the join in your SQL statement. From what I got out of your description, it sounds like this query might work for you.
SELECT
User.UserID,
Business.BusinessID,
Business.BusinessName
FROM
User,
UserBusinessOpportunity,
Business
WHERE
User.UserID = 1 AND
User.UserID = UserBusinessOpportunity.UserID AND
UserBusinessOpportunity.OpportunityID IS NULL AND
UserBusinessOpportunity.BusinessID = Business.BusinessID
I think you intend an inner join but your query is an implicit outer join.
Joining with null fields often works best with a left join.
Try this:
SELECT EmploymentOpportunity.OpportunityID, Business, Description
FROM EmploymentOpportunity
JOIN Business ON Business.BusinessName = EmploymentOpportunity.Business
LEFT JOIN UserBusinessOpportunity USING(BusinessID)
WHERE
UserBusinessOpportunity.UserID=1
AND
UserBusinessOpportunity.OpportunityID is NULL
Julian
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.