MySQL subquery referencing a table outside it's scope - mysql

In the following query I'm receiving
Error Code: 1054 Unknown column 'PRF.user_id' in 'where clause'
This is due to the fact that the subquery is attempting to reference PRF.user_id which is outside it's own scope. How can I rewrite the query in order to overcome this issue?
SELECT user_id, lastname,
(
SELECT COUNT(*) FROM
(
SELECT *
FROM Demo_Orders_M
WHERE DemoOrderStatus = 253
AND DemoOrderDate BETWEEN '2015-06-19' AND '2015-06-26'
AND `User` = PRF.user_id
GROUP BY CustomerID, `User`
HAVING COUNT(*) > 1
) t1
) recycling
FROM tbl_profiles PRF
JOIN tbl_users U ON PRF.user_id = U.id
WHERE PRF.user_id IN ( SELECT a.user_id FROM tbl_profiles a WHERE a.user_id IN ('1210', '789') )
GROUP BY user_id

The first thing I've noticed is that your subquery has a GROUP BY and HAVING clause without any aggregate functions, which shouldn't be the case. It looks to me that what you are trying to accomplish is getting the COUNT(*) of demo orders for each user/customer combination. You can write that individual query like this:
SELECT customerID, user, COUNT(*) AS numOrders
FROM demo_orders_m
WHERE demoOrderStatus = 253 AND demoOrderDate BETWEEN '2015-06-19' AND '2015-06-26'
GROUP BY customerID, user
HAVING COUNT(*) > 1;
This is a simple aggregation query that will pull the number of orders each user has that meet the given criteria, and as long as they have two or more orders.
Then, you can join these results to your users table to get other info about those users. One thing to note about your query also is that you don't need to write an extra select statement that pulls the user ids like that, what you've written is redundant. The whole condition can be rewritten as:
WHERE PRF.user_id IN(1210, 789)
So, the final query would be as follows. Note that I used a LEFT JOIN and the coalesce function to make sure a zero value is returned for users who didn't meet the order criteria, and as a result were not returned by the subquery:
SELECT user_id, lastname, COALESCE(numOrders, 0) AS numOrders
FROM tbl_profiles PRF
JOIN tbl_users U ON U.id = PRF.user_id
LEFT JOIN(
SELECT customerID, user, COUNT(*) AS numOrders
FROM demo_orders_m
WHERE demoOrderStatus = 253 AND demoOrderDate BETWEEN '2015-06-19' AND '2015-06-26'
GROUP BY customerID, user
HAVING COUNT(*) > 1) TMP ON TMP.user = U.id AND TMP.user IN (1210, 789);

Related

Getting the top sales

How to i get the top vendor for each country? I have this code and it shows the connection between the two tables, now I have to get the largest gmv per country.
Here is my working code:
SELECT DISTINCT a.country_name, b.vendor_name,
SUM(a.gmv_local) as total_gmw
from `my-project-67287.order1.order2`a
join `my-project-67287.vendor1.vendor2` b on a.vendor_id = b.id
group by a.country_name, b.vendor_name;
The top 3 should show this:
Assuming you can save that SELECT into a table called vendors, you need to use it as the subquery in the FROM clause.
You could use this:
SELECT vendors.country_name, vendors.vendor_name, MAX(vendors.total_gmw)
FROM
(
SELECT DISTINCT a.country_name, b.vendor_name,
SUM(a.gmv_local) as total_gmw
from `my-project-67287.order1.order2`a
join `my-project-67287.vendor1.vendor2` b on a.vendor_id = b.id
group by a.country_name, b.vendor_name
) AS vendors
GROUP BY vendors.country_name;
I must mention I have not tested your query, since I do not have your tables, so I assumed it's correct.
I only created the vendors table with the required fields and values from your picture. This should print:
SELECT odr.c_name,vdr.v_name,odr.gmv
FROM(
SELECT *
FROM(
SELECT c_name,v_id,gmv
FROM `order`
ORDER BY gmv DESC
)
GROUP BY c_name
)AS odr
LEFT JOIN vender AS vdr ON vdr.id = odr.v_id
GROUP BY odr.c_name
c_name short for country_name

Creating a join where I pull a count from another table

I have a table with real estate agent's info and want to pull firstname, fullname, and email from rets_agents.
I want to then get a count of all of their sales from a different table called rets_property_res_mstr.
I created a query that doesn't work yet so I need some help.
SELECT r.firstname, r.fullname, r.email
from rets_agents r
LEFT JOIN rets_property_res_mstr
ON r.email = rets_property_res_mstr.ListAgentEmail
LIMIT 10;

I'm not sure how to get the count in this.
You seem to be looking for aggregation:
SELECT a.firstname, a.fullname, a.email, COUNT(p.ListAgentEmail) cnt
FROM rets_agents a
LEFT JOIN rets_property_res_mstr p ON r.email = p.ListAgentEmail
GROUP BY a.firstname, a.fullname, a.email
ORDER BY ?
LIMIT 10;
Note that, for a LIMIT clause to really make sense, you need a ORDER BY clause so you get a deterministic results (otherwise, it is undefined which records will be shown) - I added that to your query with a question mark that you should replace with the relevant column(s).
I would consider using a CTE for this:
WITH sales as (
SELECT ListAgentEmail, count(*) count_of_sales
FROM rets_property_res_mstr
GROUP BY ListAgentEmail
)
SELECT r.firstname, r.fullname, r.email, count_of_sales
from rets_agents r
LEFT JOIN sales
ON r.email = sales.ListAgentEmail
LIMIT 10;

SQL intermediate table having column = max(column)

I have 2 tables: user and review, a one-to-many relationship.
When I execute the following query:
SELECT
user_id,
count(*) totalReviews,
USER . NAME
FROM
review,
USER
WHERE
USER .id = review.user_id
GROUP BY
user_id
I get:
1 2 marius
2 2 daniela
3 1 alin
What I want to do now is to display first 2 users because they have given the most reviews(2).
I tried adding having, if I hardcode having totalReviews=2 it works, but if I write having total = max(total) I get 0 results, while if I'm trying with,
SELECT
*
FROM
(
SELECT
user_id,
count(*) total,
USER . NAME
FROM
review,
USER
WHERE
USER .id = review.user_id
GROUP BY
user_id
) A
WHERE
total = (SELECT max(total) FROM A) `
I get an error (table A doesn't exist)
You would do this with ORDER BY and LIMIT:
SELECT u.id, count(*) as totalReviews, u.name
FROM review r JOIN
user u
ON u.id = r.user_id
GROUP BY u.id, u.name
ORDER BY totalReviews DESC
LIMIT 2;
Notes:
Never use commas in the FROM clause. Always use proper, explicit JOIN syntax.
Table aliases make the query easier to write and read.
EDIT:
If occurs to me that you want all users with the maximum number of reviews, not exactly 2. Here is one method:
SELECT u.id, COUNT(*) as totalReviews, u.name
FROM review r JOIN
user u
ON u.id = r.user_id
GROUP BY u.id, u.name
HAVING totalReviews = (SELECT COUNT(*)
FROM review r2
GROUP BY r2.user_id
ORDER BY COUNT(*) DESC
LIMIT 1
);
Note that the subquery in the HAVING clause is simpler than the outer query. There is no need to bring in the user name.

How to pass parent field in sub union query

I've this query and its giving an error that unknown U.UserID column in where clause but I can't see any error in it as my Users table instance is U
SELECT GROUP_CONCAT(U.UserID),
(
SELECT COUNT(DISTINCT(UserID))
FROM (
SELECT FriendID as UserID
FROM Friends
WHERE UserID=U.UserID AND Status=1
UNION All
SELECT UserID
FROM Follow
WHERE Type='user' AND TypeEntityID=U.UserID
) tbl
) as NoOfFriendsFollow
FROM `Users` `U`
WHERE `U`.`UserID` IN('1')
LIMIT 10
Any solution for this query or let me know where I'm wrong
SELECT GROUP_CONCAT(UserID),
(
SELECT COUNT(DISTINCT(UserID)) FROM
(
SELECT FriendID as UserID FROM Friends F
INNER JOIN Users U ON U.UserID=F.FriendID
WHERE F.Status=1
UNION All
SELECT FF.UserID FROM Follow FF
INNER JOIN Users U ON U.UserID=FF.UserID
WHERE Type='user' AND TypeEntityID=U.UserID
) tbl
) as NoOfFriendsFollow
FROM Users WHERE UserID IN('1') LIMIT 10;
Just try above code.
Hope this will helps.
Unfortunately, MySSQL does not permit you to use an outer table reference "two levels down". So you can do:
SELECT U.UserID,
(SELECT COUNT(DISTINCT UserID)
FROM (SELECT fr.FriendID as UserID, fr.FriendId as compareId
FROM Friends fr
WHERE fr.Status = 1
UNION All
SELECT f.UserID, f.TypeEntityID as compareId
FROM Follow f
WHERE f.Type = 'user'
) tbl
WHERE tbl.UserID = compareId
) as NoOfFriendsFollow
FROM Users U
WHERE U.UserID IN (1)
LIMIT 10;
Notes:
This moves the comparison into the middle subquery, so the query parses.
Presumably UserId is an integer. Only use single quotes for string and date constants.
The GROUP_CONCAT() doesn't make sense. It turns the query into an aggregation query that returns only one row.
I double the LIMIT is needed either. There should be one row per UserId.
Always use qualified column names when you have multiple tables in a query.

Where clause with sum aggregate in sql query

I have a table and I want to display only those departments that have at least one registered user in them. My query is below:
SELECT departments.id as DepartmentID,
departments.depName as DepartmentName,
SUM(IF(users.isDelete=0, 1, 0)) AS NumberOfUsers
FROM (myDB.departments)
LEFT JOIN myDB.users ON departmentID=departments.id
AND `departments`.`isDelete` = 0
HAVING SUM(NumberOfUsers) >=0
The HAVING SUM(NumberOfUsers) >=0 is not working at all. I would like to check if the NumberOfUsers is more than one then display it if not more than one then do not display it. Any suggestions?
Try this:
SELECT departments.id as DepartmentID,
departments.depName as DepartmentName,
(SELECT COUNT(u.id)
FROM users u
WHERE u.departmentID = d.id) as NumberOfUsers
FROM departments d
WHERE d.isdelete = 0
AND EXISTS
(SELECT 'user'
FROM users u
WHERE u.departmentID = d.id)
In this way, you don't use a SUM in main query (where you must use GROUP BY to show other scalar fields like departments.id and departments.depName. In My Sql is not mandatory GROUP BY)
EXISTS clause garantee the presence at least one user. If you want to show all department (indipendently number of users, remove EXISTS clause)
I think you can simplify your query as you are checking condition in user table that is_delete should be 0, so there is no need of left join you can just use normal join. After this you are removing all rows those does not have record by having clause, so you can simply put this condition in where clause as per below-
SELECT dpt.id AS DepartmentID, dpt.depName AS DepartmentName,
COUNT(usr.id) AS NumberOfUsers
FROM myDB.departments AS dpt
JOIN myDB.users AS usr ON usr.departmentID=dpt.id
WHERE dpt.`isDelete` = 0 AND usr.isDelete=0
Note: Assuming users table have primary key as id, if not then you can use any other column in count function.
In this case you need to use the group by clause too.
SELECT departments.id as DepartmentID,
departments.depName as DepartmentName,
SUM(IF(users.isDelete=0, 1, 0)) AS NumberOfUsers
FROM (myDB.departments)
LEFT JOIN myDB.users ON departmentID=departments.id
AND `departments`.`isDelete` = 0
GROUP BY departments.id, departments.depName
HAVING SUM(IF(users.isDelete=0, 1, 0)) >=0
But if you want a shorter answer, you can use JOIN instead of LEFT JOIN and removing HAVING clause:
SELECT departments.id as DepartmentID,
departments.depName as DepartmentName,
SUM(IF(users.isDelete=0, 1, 0)) AS NumberOfUsers
FROM (myDB.departments)
JOIN myDB.users ON departmentID=departments.id
AND `departments`.`isDelete` = 0
GROUP BY departments.id, departments.depName