How to build query from other queries - mysql

I want to create a query that will list all users and their profile information, however the profile info has to be gotten using a rather complex procedure.
SELECT options.`value` FROM users
RIGHT JOIN profile on(profile.uid = users.uid)
RIGHT JOIN field_data_field_ethnicity on(field_data_field_ethnicity.entity_id = profile.pid)
RIGHT JOIN options on(field_data_field_city.field_ethnicity_value = bdc_options.`index` AND bdc_options.`key` = "ethnicity")
WHERE users.uid = 1 #needs to go through all users in table
ORDER BY field_data_field_ethnicity.revision_id DESC
LIMIT 1
SELECT options.`value` FROM users
RIGHT JOIN profile on(profile.uid = users.uid)
RIGHT JOIN field_data_field_city on(field_data_field_city.entity_id = profile.pid)
RIGHT JOIN options on(field_data_field_city.field_city_value = bdc_options.`index` AND bdc_options.`key` = "city")
WHERE users.uid = 1
ORDER BY field_data_field_ethnicity.revision_id DESC
LIMIT 1
Just going from these two fields, how would I go through the users table and get something like
USER | CITY | ETHNICITY
1 | London | Koalabear
2 | Paris | Fiddle
Here are the tables in question: http://pastebin.com/feNvzMZw
The field table has all the entries with a entity_id that refers to the profile table and a revision id which refers to the revision number.. I always need the highest to be up to date

Related

MySQL - two tables and two joins

I am stuck with one query where I want to do several things:
- I have a table with some events, each event has a limited amount of people that can apply to it
- In other table I keep record of who applied to which event
- On the website, to the user, I want to display only the events that he can apply to (to be more precise, the events he hasn't applied to; once he applies, the event should no longer be visible to the user) AND/OR that number of applied people is less than max_no_people (when an event is full, no need to show it to the user
I got help from here in another thread, but only for part of the problem.
Tables look like this:
other_events (eventID, max_no_people, active)
event_applied (eventID, userID)
Here is the query:
SELECT count(event_applied.eventid) AS no_applied,
e.eventID, e.max_no_people, e.active
FROM other_events e
INNER JOIN
event_applied ON event_applied.eventID = e.eventID
LEFT JOIN
event_applied ea ON ea.eventID
AND event_applied.userID = :userID
WHERE ea.eventID IS NULL
AND e.active = 1
GROUP BY event_applied.eventID
HAVING (no_applied < max_no_people)
This query works fine - BUT - for every other user than the currently selected one.
Let's say you have userID = 42 (which is not in events_applied table paired with eventID = 123):
other_events
eventID | max_no_people | active
--------|---------------|-------
21 | 5 | 1
and in events_applied
eventID | userID
--------|-------
123 | 10
123 | 11
123 | 12
123 | 13
123 | 14
The Query will return empty row (because event is maxed out and you cannot apply anymore), but if you are userID = 12 (which is in the events_applied table and has eventID pair), the result will look like this:
no_applied | e.eventID | e.max_no_people | e.active
-----------|-----------|-----------------|---------
**4** | 123 | 5 | 1
and hence, the event will be visible and you will be able to apply again and again (I know I have to add some additional controls, but I'll tackle that later).
So, the problem is that this query somehow discards the row of a current user and doesn't add it into final count.
Anybody? :)
The main issue with your query is that you are not correlating the left joined query on a specific eventID.
Consider the following code:
SELECT
COUNT(*) AS no_applied,
e.eventID,
e.max_no_people,
e.active
FROM
other_events e
INNER JOIN event_applied ea1 ON ea1.eventID = e.eventID
LEFT JOIN event_applied ea2 ON ea1.eventID = ea2.eventID AND ea2.userID = :userID
WHERE ea2.eventID IS NULL AND e.active = 1
GROUP BY e.eventID, e.max_no_people, e.active
HAVING (no_applied < e.max_no_people)
If you run this query in this DB Fiddle with your sample data:
with userID = 42, it returns no record: this is because there is no event available (there is only one event, which is already full)
with userID = 12, it returns no record: this is because this user already applied to the only available event
I would suggest creating a more representative dataset, for example by creating another event that is not full, so you can thouroughly validate the query.
The problem is that in your current query you are only explicitly excluding the entries where the User has applied, and therefore the count is reduced by one. You instead need to exclude all the events where they applied.
Since you're using a LEFT JOIN you'll need to use a subquery to select all those events and then only join on the events (and not join on the user).
SELECT count(event_applied.eventid) AS no_applied, e.eventID, e.max_no_people, e.active
FROM other_events e
INNER JOIN event_applied ON event_applied.eventID = e.eventID
LEFT JOIN (SELECT * FROM event_applied WHERE event_applied.userID = :userID ) ea
ON ea.eventID = e.eventID
WHERE ea.eventID IS NULL AND e.active = 1
GROUP BY e.eventID, e.max_no_people, e.active
HAVING (no_applied < max_no_people)
Thank you both Graeme Tate and GMB, both approaches work perfectly!
I learned something new today :)

Error on using outer query column in subquery for date constraint

We have a Mariadb table with users details in (users)
We have a 2nd table for review dates (reviewdates)
| reviewID |USERID |A review date |
| 001 | 123 |2017-01-08 09:02:10 |
etc...
That records review meeting dates against each user.
We have a 3rd table (userdata) with multiple types of user data in. Field id 101 is new targets for the review. Field id 98 is old targets from the previous review.
|dataID|Userid |Field ID |FieldValue |UpdatedOn |UpdatedBy|
-------------------------------------------------------------
|0001 |123 | 101 |my new target|2017-01-10|145 |
|0002 |123 | 98 |my old target|2017-01-10|0 |
New Target (field ID 101) gets copied to old targets (field id 98) when the review is completed.
Either field can be updated at any time.
Each user has many review dates. I need to compare the first value of the old field after the review is complete with the last value before the review date to make sure they have copied over correctly. As users can change either field it has to a comparison of immediately before and after the completion process.
so I join users and reviewdates
select users.userid,users.username,reviewdates.meetingdate
from companyusers users
join reviewdates on reviewdates.userid = users.userid
and this gives me all the review dates for all users
I then tried to find the most recent entry for the 101 field :
select users.userid,users.username, reviewdates.meetingdate, latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101)
) as latest101 on (latest101.userid = users.userid)
and this works OK too but when I try to find the most recent entry before each review date:
select users.userid,users.username,reviewdates.meetingdate,latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101
#date limit
and u2.updatedOn < reviewdates.meetingdate)
) as latest101 on (latest101.userid = users.userid)
I get an
"unknown column reviewdates.meetingdate in where clause"
error. I've found loads of statements saying I can't refer to an outer join in a subquery but none that provide possible answers that apply to these date constraints.
Any help or pointers would be appreciated.
I do not see that you are filtering the most outer 'reveiwdates' table with anything specific, it is just used to display the 'meetingdate' based on the inner queries,
in that case firstly, there is no reason to use the same reference inside subquery.
Secondly, there are so many 'meetingdates', which specific meeting date are we comparing against the 'updatedOn' ?
You cannot join two tables on an in-equality condition.
Either a constant review date filter needs be applied or a procedure needs be written to loop through each meeting date for a user in the 'reviewdates' table.
If you just care about the latest review for that user then you could fetch and compare the latest 'reviewdate' just like how you are comparing the latest updated on, 'updatedOn' does not seem to have time component and to avoid other issues use date() or equivalent while comparing.
select users.userid,users.username,reviewdates.meetingdate,latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101
#date limit
and u2.updatedOn < (select date(max(reviewdates.meetingdate)) from reviewdates where u2.userid = reviewdates.userid))
) as latest101 on (latest101.userid = users.userid)

Left join how can i use where if right field exists

i have two tables:
users (used fields in query) ->
|uuid|name|last_name|email|rol |status|
|1234|abcd| abc |1#2.3|pre-thunder| 1 |
|5678|efgh| efg |4#5.6|pre-thunder| 1 |
And documentation_thunder table (used fields in query)->
|id|id_thunder|doc_type|
|1 |1234 |image |
I try to get all records on my left table but additional i get a count from my right table:
SELECT
users.uuid,
users.email,
users.name,
users.last_name,
COUNT(documentation_thunder.id) as documentation// i need to know how many documents have the user
FROM users
LEFT JOIN documentation_thunder
ON users.uuid = documentation_thunder.id_thunder
WHERE
users.status = 1 AND users.rol = 'pre-thunder' AND (documentation_thunder.doc_type = 'image' OR documentation_thunder.doc_type IS NULL)
This query only returns first user but i need returns all users no matter if not have documents.
Any idea? how do i do it?
Since you want to return a count for each user, you should also be using group by. Perhaps this is what you're after:
SELECT
users.uuid,
users.email,
users.name,
users.last_name,
COUNT(documentation_thunder.id) as documentation
FROM users
LEFT JOIN documentation_thunder
ON users.uuid = documentation_thunder.id_thunder
WHERE
users.status = 1 AND users.rol = 'pre-thunder' AND
(documentation_thunder.doc_type = 'image' OR documentation_thunder.doc_type IS NULL)
GROUP BY users.uuid

MYSql search perfomance problems

account
act_id | act_name | grp_id | grp_id_2
2 | test | 4 | 10
promotion
pml_id | act_id | grp_id
2 | 2 | null
3 | null | 4
4 | null | 10
I have two tables, shown above (trimmed down). Account has about 15000 records, promotion about 20000.
Customer basically wants it so that they could search for an account name, say 'test'. And it would show the promotions 2, 3 and 4.
Promotion 3 would show because the account 'test' has grp_id = 4.
Promotion 4 would show because account 'test' has grp_id_2 = 10.
I originally did this with a couple of joins
SELECT pml_id FROM promotion
LEFT JOIN account AS account1 ON promotion.act_id = account1.act_id
LEFT JOIN account AS account2 ON promotion.grp_id = account2.grp_id
LEFT JOIN account AS account3 ON promotion.grp_id = account3.grp_id_2
WHERE account1.act_name LIKE 'test%' or account2.act_name LIKE 'test%'
or account3.act_name LIKE 'test%'
GROUP BY pml_id
The problem with this is this ended up taking a long time when I started having to join 5 times to the account table. It also gave me about 10000000 records (without the group by). Most of the promotions use a grp_id, rarely do they use an act_id.
Is there any way to search the act_name column quickly in this scenario? Without having to do so many joins?
I have single indexes on act_id, pml_id, grp_id, grp_id_2
Note: This is part of a query where the user may not search by account. I.E the where clause may not always be there
Use an INNER JOIN instead to avoid scanning the entire table :
SELECT p.pml_id
FROM account a
INNER JOIN promotion p
ON (p.act_id = a.act_id OR p.grp_id = a.grp_id OR p.grp_id = a.grp_id_2)
WHERE a.act_name LIKE "test%";
Is this any faster?
SELECT pml_id FROM promotion p
LEFT JOIN account a
ON (p.act_id = a.act_id OR p.grp_id = a.grp_id OR p.grp_id = a.grp_id_2)
WHERE a.act_name LIKE "test%";
Try this one as well, if you have an index on act_id, this should be quite a bit faster:
SELECT * FROM promotion p
LEFT JOIN account a
ON (p.act_id = a.act_id OR p.grp_id = a.grp_id OR p.grp_id = a.grp_id_2)
WHERE a.act_id IN (
SELECT act_id FROM account WHERE act_name LIKE "test%"
)

How to do this simple MySQL statement without resorting to a sub query?

Users table:
user_id | avatar
----------------------------------
1 | file-name.jpg
2 | friendly-ghost.jpg
Profile views table:
profile_user_id | viewer_user_id
--------------------------------
2 | 1
I need to get all the information about the user who user 1 viewed.
The query:
SELECT *
FROM `profile_views`
INNER JOIN `users` ON profile_views.viewer_user_id = users.user_id
WHERE profile_views.viewer_user_id = '1'
This gives back:
profile_user_id: 2
avatar: file-name.jpg
As you can see it's giving back user 1's avatar not user 2's. I need it to give back the avatar of the user that user 1 viewed. In this case that would be "friendly-ghost.jpg".
Is there a way to do this without resorting to a sub query?
SELECT user_id, avatar
FROM profile_views AS PV
INNER JOIN users AS U ON U.user_id = PV.profile_user_id
WHERE PV.viewer_user_id = 1
SELECT * FROM users
JOIN profile_views ON (profile_user_id = user_id AND viewer_user_id = 1)
Just change your join condition will work.
ON profile_views.profile_user_id = users.user_id