MYSql search perfomance problems - mysql

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%"
)

Related

SQL Distinct based on different colum

I have problem to distinct values on column based on other column. The case study is:
Table: List
well | wbore | op|
------------------
wella|wbore_a|op_a|
wella|wbore_a|op_b|
wella|wbore_a|op_b|
wella|wbore_b|op_c|
wella|wbore_b|op_c|
wellb|wbore_g|op_t|
wellb|wbore_g|op_t|
wellb|wbore_h|op_k|
So, I want the output to be appear in different field/column like:
well | total_wbore | total_op
----------------------------
wella | 2 | 3
---------------------------
wellb | 2 | 2
the real study case come from different table but to simplify it I just assume this case happened in 1 table.
The sql query that I tried:
SELECT well.well_name, wellbore.wellbore_name, operation.operation_name, COUNT(*)
FROM well
INNER JOIN wellbore ON wellbore.well_uid = well.well_uid
INNER JOIN operation ON wellbore.well_uid = operation.well_uid
GROUP BY well.well_name,wellbore.wellbore_name
HAVING COUNT(*) > 1
But this query is to calculate the duplicate row which not meet the requirement. Anyone can help?
you need to use count distinct
SELECT
count(distinct wellbore.wellbore_name) as total_wbore
count(distinct operation.operation_name) as total_op
FROM well
INNER JOIN wellbore ON wellbore.well_uid = well.well_uid
INNER JOIN operation ON wellbore.well_uid = operation.well_uid
Final query:
SELECT
well.well_name,
COUNT(DISTINCT wellbore.wellbore_name) AS total_wbore,
COUNT(DISTINCT operation.operation_name) AS total_op
FROM well
INNER JOIN wellbore ON wellbore.well_uid = well.well_uid
INNER JOIN operation ON wellbore.well_uid = operation.well_uid
GROUP BY well.well_name

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 :)

MYSQL - Select only if row in LEFT JOIN is not present

I have 2 simple mysql tables. The first 1 called mail and has 2 rows:
sender | receiver
Marley | Bob
Saget | Bob
The second one called block and has 1 row:
blocker | blocked
Bob | Marley
I want to select sender(s) from the first table who sent Bob emails but aren't blocked in the block table. So the results should be:
sender
saget
I tried the following query but it's not returning results:
SELECT * FROM mail
LEFT JOIN block ON (block.blocker = 'Bob')
WHERE (block.blocked <> mail.sender)
The left join will produce null rows for the mismatches.
It's those null rows that you need to filter on.
SELECT * FROM mail
LEFT JOIN block ON (block.blocker = 'Bob')
WHERE block.blocker IS NULL
It's kind of strangle to be joining on a fixed value however, a more common join (given your tables) would be:
SELECT * FROM mail
LEFT JOIN block ON (block.blocker = mail.receiver
and block.blocked = mail.sender)<<-- these should match
WHERE block.blocker IS NULL <<-- select only mismatches
AND mail.receiver like 'bob';
Try this:
SELECT sender
FROM mail m
WHERE NOT EXISTS (SELECT 1 FROM block
WHERE blocker = m.receiver
AND blocked = m.sender)

How to build query from other queries

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

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