how to increase the performance of this mysql query
SELECT '' AS sharedid,
hubber_posts.userID AS postowner,
hubber_posts.*,
'' AS sharedby,
hubber_posts.userID AS userID,
hubber_posts.posted_date AS DATE,
'' AS sharebyusr,
'' AS sharebyusrimg,
Concat_ws(' ', firstname, lastname) AS fullname,
username AS postedBy,
hubber_user.image,
hubber_user.gender AS gender,
(SELECT accounttype
FROM hubber_user_security us
WHERE hubber_user.ID = us.userID
AND hubber_posts.userID = us.userID) AS accounttype,
'' AS sharebyusrtype
FROM hubber_posts
INNER JOIN hubber_user
ON hubber_posts.userID = hubber_user.ID
WHERE hubber_posts.status = 1
Your example code has a correlated subquery. MySQL performs poorly with those, as of late 2016.
Try converting it to a JOINed table.
SELECT all that stuff,
us.accounttype
FROM hubber_posts
JOIN hubber_user ON hubber_posts.userID = hubber_user.ID
LEFT JOIN hubber_user_security us ON hubber_user.ID = us.userID
WHERE hubber_posts.status = 1
I used LEFT JOIN. Without the LEFT, any rows without a matching entry in that table will be suppressed from the result set.
You query is essentially this:
SELECT . . .
(SELECT accounttype
FROM hubber_user_security us
WHERE u.ID = us.userID AND
p.userID = us.userID
) AS accounttype,
. . .
FROM hubber_posts p INNER JOIN
hubber_user u
ON p.userID = u.ID
WHERE p.status = 1 ;
For this query, the optimal indexes are:
hubber_posts(status, userId)
hubber_user(Id)
hubber_user_security(userId)
I would note that the subquery has an extra correlation condition that is not necessary -- the user ids are already equal. And, you run the risk of getting an error if there are multiple account types.
You may intend:
(SELECT GROUP_CONCAT(accounttype)
FROM hubber_user_security us
WHERE u.ID = us.userID
) as accounttypes,
My suggestion is to support a join where hubber_posts is the base table and the 2 other tables are joined using nested loops.
No need to index hubber_posts for the join.
hubber_user.ID should be a PK.
hubber_user_security.userID should be indexed (and defined as a FK references hubber_user.ID).
As for the WHERE clause - only if you have relatively few rows where hubber_posts.status = 1 then you should add an index on hubber_posts.status
P.s.
since the join contain the condition -
ON hubber_posts.userID = hubber_user.ID
There is no need to compare both hubber_posts.userID and hubber_user.ID to us.userID
Related
I have a query like this
left outer join ( select tm.item_id as item_id,function_role =(CASE
mf.function_name
when mf.function_name is not null
then mf.function_name
else 'admin'
end)
from team_members tm
join mem_function mf on mf.function_id=tm.function_id
) bnu on bnu.item_id = p.pro_id
When there's NO value in the function_name column of mem_function table,I want to set that value as 'admin'.
when i tried this it's throwing "From keyword not found where expected"
anyway to resolve this?
Is this what you want?
left outer join
(select tm.item_id as item_id,
coalesce(function_name, 'admin') as function_role
from team_members tm join
mem_function mf
on mf.function_id = tm.function_id
) bnu
on bnu.item_id = p.pro_id
Although you can do this using case, coalesce() is really the better solution.
I think your problem involves the function_role =. That assigns a variable, not a column name.
EDIT:
Your problem seems to be in the outer query. So you want:
select . . ., coalesce(function_name, 'admin') as function_role
from . . .
left outer join
(select tm.item_id as item_id, function_name
from team_members tm join
mem_function mf
on mf.function_id = tm.function_id
) bnu
on bnu.item_id = p.pro_id
You probably don't need the subqueries, but that is another matter.
I'm having an issue updating a table with a select using multiple joins. I feel like everything is in place but I'm getting some syntax problems around the end, as commented below.
UPDATE ambition.ambition_totals a
INNER JOIN (SELECT
c.user AS UserID,
COUNT(*) AS dealers,
ROUND((al.NumberOfDealers / al.NumberOfDealerContacts) * 100 ,2) AS percent
FROM contact_events c
JOIN users u
ON c.user = u.id
JOIN dealers d
ON c.dealer_num = d.dealer_num
LEFT JOIN (
SELECT user_id, COUNT(*) AS NumberOfDealerContacts,
SUM(CASE WHEN ( d.next_call_date + INTERVAL 7 DAY) THEN 1 ELSE 0 END) AS NumberOfDealers
FROM attr_list AS al
JOIN dealers AS d ON d.csr = al.data
WHERE al.attr_id = 14
GROUP BY user_id)) as al
ON al.user_id = a.ext_id -- this is where I have a syntax error
SET a.dealers_contacted = al.dealers,
a.percent_up_to_date = al.percent;
As shown, I'm getting the data needed from these joins but I'm unable to update based on my ON clause in the final join. The select itself works apart from this, but I'm just trying to alter it to update a table.
I'm sure I'm just overlooking something in the syntax but I get an error that 'every derived table must have its own alias'.
UPDATE
Original working select that needs to be converted into the update:
SELECT
c.user AS UserID,
COUNT(*) AS Number_of_recorded_events,
ROUND((al.NumberOfDealers / al.NumberOfDealerContacts) * 100 ,2) AS Percentage_up_to_date
FROM contact_events c
JOIN users u
ON c.user = u.id
JOIN dealers d
ON c.dealer_num = d.dealer_num
LEFT JOIN (
SELECT user_id, COUNT(*) AS NumberOfDealerContacts,
SUM(CASE WHEN ( d.next_call_date + INTERVAL 7 DAY) THEN 1 ELSE 0 END) AS NumberOfDealers
FROM jackson_id.attr_list AS al
JOIN jfi_dealers.dealers AS d ON d.csr = al.data
WHERE al.attr_id = 14
GROUP BY user_id) AS al
ON al.user_id = c.user
GROUP BY UserID;
'every derived table must have its own alias'
This error is pretty clear. A derived table is when you put a subquery in a FROM clause or JOIN clause, which you do twice in your query.
Every time you do this, you must give each of these derived table subqueries an alias, so you can reference columns returned by the subquery.
Like:
SELECT t.foo FROM (SELECT foo FROM MyTable) AS t
This must be done for every such subquery. In your case, you have something like this form:
UPDATE a
INNER JOIN (
SELECT ... FROM c JOIN u JOIN d
LEFT JOIN (SELECT ... FROM al JOIN d ...)
) AS al
SET ...
You have one level of subquery, which you give the alias al.
But you don't give an alias for the innermost subquery, the one you did a LEFT JOIN on. That one needs an alias too.
P.S.: This question is actually a duplicate of What is the error "Every derived table must have its own alias" in MySQL? from 2009. I know Stack Overflow encourages us to close new questions as duplicates if there is already an old answer. But I also know the reality is that people tend not to search old posts much.
On the other hand, that old Stack Overflow post from 2009 is literally the first result when I google for the error string 'every derived table must have its own alias'.
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;
I want to use subquery inside of IFNULL statement
SELECT t.col1
, IFNULL(t.col2, (SELECT an.col_11
FROM another_table an
WHERE an.col1 = t.col5)) as alias_name
, t.col3
FROM table t;
In IFNULL statement second expression should be subquery.
Please give me proper syntax
My actual query is
SELECT u.username, up.gender, d.name, desg.name,
IFNULL(up.creative_lead_id,
(SELECT au.username FROM auth_user au
WHERE au.id=up.creative_lead_id)) as creative_lead, up.image
FROM user_profile up, department d, designation, auth_user
WHERE up.department_id=d.id
AND up.designation_id = desg.id up.auth_uesr_id = u.id;
This query is giving syntax error because of IFNULL statement.
You can rewrite your query with join,Correlated query will execute for each row in your table and it might affect the performance
SELECT
t.col1,
IFNULL(t.col2, an.col_11) AS alias_name,
t.col3
FROM
`table` t
LEFT JOIN another_table an
ON an.col1 = t.col5
Don't use a subquery for this situation, try a query like that instead (use of jointure):
SELECT t.col1
,IFNULL(t.col2, an.col_11) AS alias_name
,t.col3
FROM your_table t
LEFT JOIN another_table an ON an.col1 = t.col5
In your full query, your using twice up.creative_lead_id for your IFNULL clause (once as first parameter and then in the subquery). That make no sense because if the first param is NULL, your subquery will return no result!
In order to show you the principe that will solve your problem, i just replaced the first param by a fictive one that i called up.creative_lead. This fictive column is the name of the creative lead stored in your table user_profile and if this value is null, i'm looking to the username of the user corresponding to creative_lead_id.
Here is the full query that'll solve your problem with the correction mentioned above:
SELECT u.username
,up.gender
,d.name
,desg.name
,IFNULL(up.creative_lead, cl.username) AS creative_lead
,up.image
FROM user_profile up
INNER JOIN department d ON d.id = up.department_id
INNER JOIN designation desg ON desg.id = up.designation_id
INNER JOIN auth_user u ON u.id = up.auth_user_id
INNER JOIN auth_user cl ON cl.id = up.creative_lead_id
Notice that i changed the syntax of your query, it's highly recommended to avoid the use of old syntax for jointures (use explicit JOIN clause instead).
Hope this will help you.
I have the following query:
SELECT PKID, QuestionText, Type
FROM Questions
WHERE PKID IN (
SELECT FirstQuestion
FROM Batch
WHERE BatchNumber IN (
SELECT BatchNumber
FROM User
WHERE RandomString = '$key'
)
)
I've heard that sub-queries are inefficient and that joins are preferred. I can't find anything explaining how to convert a 3+ tier sub-query to join notation, however, and can't get my head around it.
Can anyone explain how to do it?
SELECT DISTINCT a.*
FROM Questions a
INNER JOIN Batch b
ON a.PKID = b.FirstQuestion
INNER JOIN User c
ON b.BatchNumber = c.BatchNumber
WHERE c.RandomString = '$key'
The reason why DISTINCT was specified is because there might be rows that matches to multiple rows on the other tables causing duplicate record on the result. But since you are only interested on records on table Questions, a DISTINCT keyword will suffice.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
Try :
SELECT q.PKID, q.QuestionText, q.Type
FROM Questions q
INNER JOIN Batch b ON q.PKID = b.FirstQuestion
INNER JOIN User u ON u.BatchNumber = q.BatchNumber
WHERE u.RandomString = '$key'
select
q.pkid,
q.questiontext,
q.type
from user u
join batch b
on u.batchnumber = b.batchnumber
join questions q
on b.firstquestion = q.pkid
where u.randomstring = '$key'
Since your WHERE clause filters on the USER table, start with that in the FROM clause. Next, apply your joins backwards.
In order to do this correctly, you need distinct in the subquery. Otherwise, you might multiply rows in the join version:
SELECT q.PKID, q.QuestionText, q.Type
FROM Questions q join
(select distinct FirstQuestion
from Batch b join user u
on b.batchnumber = u.batchnumber and
u.RandomString = '$key'
) fq
on q.pkid = fq.FirstQuestion
As to whether the in or join version is better . . . that depends. In some cases, particularly if the fields are indexed, the in version might be fine.