MySQL using Inner joins on an alias of a calculated column - mysql

I have a query like so:
SELECT User.id, 10*10 as distance
FROM USERS
INNER JOIN
(
SELECT Location.user_id,
min(10 * 10) as mindistance
FROM Location
GROUP BY Location.user_id
) L ON Users.id = Location.user_id AND distance = L.mindistance
If I leave it as is, I keep getting:
Unknown column 'distance' in 'on clause'
But if I put User.distance instead of just distance, I get:
MySQL syntax error near....
Can I not use alias' this way on a calculated field? The 10 * 10 is just a simple placeholder as the calculation is much more complex.

You are trying to use a column alias in the ON clause. The MySQL documentation has this to say about this situation:
The conditional_expr used with ON is any conditional expression of the form that can be used in a WHERE clause.
And also:
Standard SQL disallows references to column aliases in a WHERE clause. This restriction is imposed because when the WHERE clause is evaluated, the column value may not yet have been determined. For example, the following query is illegal:
That said, you can use variables to calculate the expression in the ON clause and then use the value in the column list, like this:
SELECT User.id, #dist as distance
FROM USERS
INNER JOIN
(
SELECT Location.user_id,
min(10 * 10) as mindistance
FROM Location
GROUP BY Location.user_id
) L ON Users.id = Location.user_id AND (#dist := 10*10) = L.mindistance

To avoid having to make the calculation three times in the query, you can wrap the outer calculation in a FROM subselect, which will give you access to the aliased field name (where it wasn't accessible in your original query):
SELECT a.*
FROM
(
SELECT id, 10*10 AS distance
FROM USERS
) a
INNER JOIN
(
SELECT user_id,
MIN(10 * 10) AS mindistance
FROM Location
GROUP BY user_id
) L ON a.id = L.user_id AND a.distance = L.mindistance
Here, the calculation is only done two times instead of three.

SELECT User.id, 10*10 as distance
FROM USERS
INNER JOIN
(
SELECT Location.user_id,
min(10 * 10) as mindistance
FROM Location
GROUP BY Location.user_id
) L ON User.id = Location.user_id AND L.mindistance =10*10

You can't used derived values in a query where clause - where is used to restrict records and which indexes to use - derived values can't be used by the optimizer so you need to filter the final results.
not quite sure what you're doing but try something like:
SELECT User.id, 10*10 as distance
FROM USERS
INNER JOIN
(
SELECT Location.user_id,
min(10 * 10) as mindistance
FROM Location
GROUP BY Location.user_id
) L ON User.id = Location.user
HAVING USERS.distance = L.mindistance

Related

How to add condition on virtual column in mysql?

I want to add a condition for the tbl_restaurant_featured_history.id column but I can't add that condition in where clause because It shows an error saying Unknown column 'featured' in 'where clause' and If I add a condition featured is not null in having clause It is returning 0 rows.
Below is the query before adding the condition
SELECT
DISTINCT(tbl_restaurant.id) as restaurant_id,
tbl_restaurant.name,
tbl_restaurant_featured_history.id as featured,
tbl_restaurant.min_order_amount,
tbl_restaurant.latitude as latitude,
tbl_restaurant.logo,
tbl_favourite_restaurant.id as is_fav,
tbl_restaurant.address as address,
IF(tbl_restaurant_timing.start_time <= '19:56:26' && tbl_restaurant.service = 'Available' && tbl_restaurant_timing.end_time >= '19:56:26', 'Open', 'Closed') AS availblity,
tbl_restaurant.longitude as longitude,
(
SELECT ROUND(AVG(tbl_rate_review.rate))
FROM tbl_rate_review
where tbl_rate_review.restaurant_id = tbl_restaurant.id
GROUP BY restaurant_id
) as avgrating,
(
SELECT ROUND(AVG(tbl_rate_review.rate), 2)
FROM tbl_rate_review
where tbl_rate_review.restaurant_id = tbl_restaurant.id
GROUP BY restaurant_id
) as rating,
111.045 * DEGREES(ACOS(COS(RADIANS(23.0266941)) * COS(RADIANS(latitude)) * COS(RADIANS(longitude) - RADIANS(72.6008731)) + SIN(RADIANS(23.0266941)) * SIN(RADIANS(latitude)))) AS distance_in_km
FROM tbl_restaurant
LEFT JOIN tbl_restaurant_featured_history ON tbl_restaurant_featured_history.restaurant_id = tbl_restaurant.id
LEFT JOIN tbl_restaurant_menu ON tbl_restaurant_menu.restaurant_id = tbl_restaurant.id AND tbl_restaurant_menu.status='Active'
LEFT JOIN tbl_favourite_restaurant ON tbl_favourite_restaurant.restaurant_id=tbl_restaurant.id AND tbl_favourite_restaurant.user_id=19
LEFT JOIN tbl_restaurant_timing ON tbl_restaurant_timing.restaurant_id = tbl_restaurant.id AND tbl_restaurant_timing.day = 'Saturday'
WHERE tbl_restaurant.status = 'Active'
HAVING distance_in_km <= 10
ORDER BY availblity DESC, distance_in_km ASC LIMIT 10, 10
And the output of this query
The query is poorly formated and hence rather hard to follow.
I can see this in the select clause:
tbl_restaurant_featured_history.id as featured
The where clause of a query just can't refer to an alias defined in the select clause. If you want to filter on this, then you need to use the column name (tbl_restaurant_featured_history.id) rather than the alias (featured):
where tbl_restaurant_featured_history.id is not null
The table tbl_restaurant_featured_history is LEFT joined to the table tbl_restaurant and this is why you get nulls in the results because some rows do not match the conditions of the ON clause that you have set.
If you want to add the condition:
tbl_restaurant_featured_history.id is not null
this means that you want only the matching rows and from your sample data I see that there is only 1 matching row.
In this case all you have to do is change the join to an INNER join:
.................................
FROM tbl_restaurant
INNER JOIN tbl_restaurant_featured_history ON tbl_restaurant_featured_history.restaurant_id = tbl_restaurant.id
.................................

Left join sql query

I want to get all the data from the users table & the last record associated with him from my connection_history table , it's working only when i don't add at the end of my query
ORDER BY contributions DESC
( When i add it , i have only the record wich come from users and not the last connection_history record)
My question is : how i can get the entires data ordered by contributions DESC
SELECT * FROM users LEFT JOIN connections_history ch ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
The order by should not affect the results that are returned. It only changes the ordering. You are probably getting what you want, just in an unexpected order. For instance, your query interface might be returning a fixed number of rows. Changing the order of the rows could make it look like the result set is different.
I will say that I find = to be more intuitive than EXISTS for this purpose:
SELECT *
FROM users u LEFT JOIN
connections_history ch
ON u.id = ch.guid AND
ch.date = (SELECT Max(ch1.date)
FROM connections_history ch1
WHERE ch.guid = ch1.guid
)
ORDER BY contributions DESC;
The reason is that the = is directly in the ON clause, so it is clear what the relationship between the tables is.
For your casual consideration, a different formatting of the original code. Note in particular the indented AND suggests the clause is part of the LEFT JOIN, which it is.
SELECT * FROM users
LEFT JOIN connections_history ch ON
users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date
)
We can use nested queries to first check for max_date for a given user and pass the list of guid to the nested query assuming all the users has at least one record in the connection history table otherwise you could use Left Join instead.
select B.*,X.* from users B JOIN (
select A.* from connection_history A
where A.guid = B.guid and A.date = (
select max(date) from connection_history where guid = B.guid) )X on
X.guid = B.guid
order by B.contributions DESC;

LEFT JOIN SUM with WHERE clause

The following query always outputs SUM for all rows instead of per userid. Not sure where else to look. Please help.
SELECT * FROM assignments
LEFT JOIN (
SELECT SUM(timeworked) AS totaltimeworked
FROM time_entries
) assignments ON (userid = assignments.userid AND ticketid = ?)
WHERE ticketid = ?
ORDER BY assigned,scheduled
If you want to keep the SELECT *, you would have to add a group by clause in the subquery. Something like this
SELECT * FROM assignments
LEFT JOIN (
SELECT SUM(timeworked) AS totaltimeworked
FROM time_entries
GROUP BY userid
) time_entriesSummed ON time_entriesSummed.userid = assignments.userid
WHERE ticketid = ?
ORDER BY assigned,scheduled
But a better way would be to change the SELECT * to instead select the fields you want a add a group by clause directly. Something like this
SELECT
assignments.id,
assignments.assigned,
assignments.scheduled,
SUM(time_entries.timeworked) AS totalTimeworked
FROM assignments
LEFT JOIN time_entries
ON time_entries.userid = assignments.userid
GROUP BY assignments.id, assignments.assigned, assignments.scheduled
Edit 1
Included table names in query 2 as mentioned in chameera's comment below

Column ALIAS not recognized in HAVING clause, unkown column error

SELECT u.*, zz.contid, zz.lng, zz.lat, zz.zip, zz.city, zz.state, (**calc distance**) AS distance
FROM contacts zz
INNER JOIN users u
ON zz.id = u.id
WHERE cond1 = 1, cond2=2, etc..
GROUP BY u.id
HAVING MIN(distance) < 100
Error: unknown column distance
Any idea why this is? and how to fix it?
I appreciate any advice, many thanks in advance!
What database is this? If it's something like MSSQL, you can't use aliases like that elsewhere in a query. You'll have to replicate the whole alias definition:
SELECT big+ugly+calculation AS foo
...
HAVING (big+ugly_calculation) = bar
Or wrap the query in another:
SELECT *
FROM ( SELECT *, big+ugly+calculation AS foo )
WHERE foo = bar
You can't refer to an alias in an aggregate function. Move the query into a subquery, and then aggregate it in the outer query.
SELECT *
FROM (
SELECT u.*, zz.contid, zz.lng, zz.lat, zz.zip, zz.city, zz.state, (**calc distance**) AS distance
FROM contacts zz
INNER JOIN users u
ON zz.id = u.id
WHERE cond1 = 1, cond2=2, etc..) subquery
GROUP BY id
HAVING MIN(distance) < 100

MySQL Inner Join with where clause sorting and limit, subquery?

Everything in the following query results in one line for each invBlueprintTypes row with the correct information. But I'm trying to add something to it. See below the codeblock.
Select
blueprintType.typeID,
blueprintType.typeName Blueprint,
productType.typeID,
productType.typeName Item,
productType.portionSize,
blueprintType.basePrice * 0.9 As bpoPrice,
productGroup.groupName ItemGroup,
productCategory.categoryName ItemCategory,
blueprints.productionTime,
blueprints.techLevel,
blueprints.researchProductivityTime,
blueprints.researchMaterialTime,
blueprints.researchCopyTime,
blueprints.researchTechTime,
blueprints.productivityModifier,
blueprints.materialModifier,
blueprints.wasteFactor,
blueprints.maxProductionLimit,
blueprints.blueprintTypeID
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
So what I need to get in here is the following table with the columns below it so I can use the values timestamp and sort the entire result by profitHour
tablename: invBlueprintTypesPrices
columns: blueprintTypeID, timestamp, profitHour
I need this information with the following select in mind. Using a select to show my intention of the JOIN/in-query select or whatever that can do this.
SELECT * FROM invBlueprintTypesPrices
WHERE blueprintTypeID = blueprintType.typeID
ORDER BY timestamp DESC LIMIT 1
And I need the main row from table invBlueprintTypes to still show even if there is no result from the invBlueprintTypesPrices. The LIMIT 1 is because I want the newest row possible, but deleting the older data is not a option since history is needed.
If I've understood correctly I think I need a subquery select, but how to do that? I've tired adding the exact query that is above with a AS blueprintPrices after the query's closing ), but did not work with a error with the
WHERE blueprintTypeID = blueprintType.typeID
part being the focus of the error. I have no idea why. Anyone who can solve this?
You'll need to use a LEFT JOIN to check for NULL values in invBlueprintTypesPrices. To mimic the LIMIT 1 per TypeId, you can use the MAX() or to truly make sure you only return a single record, use a row number -- this depends on whether you can have multiple max time stamps for each type id. Assuming not, then this should be close:
Select
...
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Left Join (
SELECT MAX(TimeStamp) MaxTime, TypeId
FROM invBlueprintTypesPrices
GROUP BY TypeId
) blueprintTypePrice On blueprints.blueprintTypeID = blueprintTypePrice.typeID
Left Join invBlueprintTypesPrices blueprintTypePrices On
blueprintTypePrice.TypeId = blueprintTypePrices.TypeId AND
blueprintTypePrice.MaxTime = blueprintTypePrices.TimeStamp
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
Order By
blueprintTypePrices.profitHour
Assuming you might have the same max time stamp with 2 different records, replace the 2 left joins above with something similar to this getting the row number:
Left Join (
SELECT #rn:=IF(#prevTypeId=TypeId,#rn+1,1) rn,
TimeStamp,
TypeId,
profitHour,
#prevTypeId:=TypeId
FROM (SELECT *
FROM invBlueprintTypesPrices
ORDER BY TypeId, TimeStamp DESC) t
JOIN (SELECT #rn:=0) t2
) blueprintTypePrices On blueprints.blueprintTypeID = blueprintTypePrices.typeID AND blueprintTypePrices.rn=1
You don't say where you are putting the subquery. If in the select clause, then you have a problem because you are returning more than one value.
You can't put this into the from clause directly, because you have a correlated subquery (not allowed).
Instead, you can put it in like this:
from . . .
(select *
from invBLueprintTypesPrices ibptp
where ibtp.timestamp = (select ibptp2.timestamp
from invBLueprintTypesPrices ibptp2
where ibptp.blueprintTypeId = ibptp2.blueprintTypeId
order by timestamp desc
limit 1
)
) ibptp
on ibptp.blueprintTypeId = blueprintType.TypeID
This identifies the most recent records for all the blueprintTypeids in the subquery. It then joins in the one that matches.