MySQL: Query two fields with different lookup id using one lookup table - mysql

How can I improve my existing query to display the correct lookup value if the second lookup.id was used already. Would this be better if I use derived tables? sub-queries? Can someone teach me please?
PROBLEM:
RECORDS TYPE TYPE_DESC PROCESS_ID STATUS QUEUE_DESC
1 1 Queued 55 4 Queued
1 2 Cancelled 84 7 Cancelled
MY GOAL:
RECORDS TYPE TYPE_DESC PROCESS_ID STATUS QUEUE_DESC
1 1 Initial 55 4 Queued
1 2 Follow Up 84 7 Cancelled
Existing query:
SELECT
COUNT(q.id) as records,
q.type,
l.description AS type_desc,
q.process_id,
q.status,
l.description AS queue_desc
FROM
queues q,
lookups l
WHERE
l.id = q.status
GROUP BY q.status;
To better understand my problem, please see sqlfiddle entry:
http://sqlfiddle.com/#!2/6b7d10/6
Thanks

You have to join "lookups" table twice.
SELECT COUNT(q.id) AS records
,q.type
,l1.description AS type_desc
,q.process_id
,q.status
,l2.description AS queue_desc
FROM queues q
,lookups l1
,lookups l2
WHERE l1.id = q.type
and l2.id = q.status
GROUP BY q.status;
that's all.

Try this:
select count(q.id) as records,
q.type, a.description, q.process_id, q.status,
b.description as qdesc
from
queues q
inner join lookups a on q.type = a.id
inner join lookups b on q.status = b.id
group by q.status
You need to join with lookups twice - once to get the type and again to get the status. Note the use of explicit join syntax with ON clause.

I think this is the query you want:
SELECT COUNT(q.id) as records,
q.type,
lt.description AS type_desc,
q.process_id,
q.status,
ls.description AS queue_desc
FROM queues q join
lookups ls
on ls.id = q.status and ls.key = 'status' join
lookups lt
on lt.id = q.type and lt.key = 'type'
GROUP BY q.status;
Note that this ensures that the key type matches the values for the joins.

Related

MySQL query SLOW Don't know how to optimize

MySQL query SLOW don’t know how to optimize
I think I m fine with hardware 60GB RAM 10 Cores SSD
Hi I m having a big issue with this query running slow on Mysql they query is below:
# Thread_id: 1165100 Schema: back-Alvo-11-07-19 QC_hit: No
# Query_time: 9.015205 Lock_time: 0.000188 Rows_sent: 1 Rows_examined: 2616880
# Rows_affected: 0
SET timestamp=1568549358;
SELECT count(*) as total_rows FROM(
(SELECT m.*
FROM phpfox_channel_video AS m
INNER JOIN phpfox_channel_category AS mc
ON(mc.category_id = mc.category_id)
INNER JOIN phpfox_channel_category_data AS mcd
ON(mcd.video_id = m.video_id)
WHERE m.in_process = 0 AND m.view_id = 0
AND m.module_id = 'videochannel'
AND m.item_id = 0 AND m.privacy IN(0)
AND mcd.category_id = 17
GROUP BY m.video_id
ORDER BY m.time_stamp DESC
LIMIT 12
)) AS m
JOIN phpfox_user AS u
ON(u.user_id = m.user_id);
This query is running very slow as you can see 9 seconds
When looking for online help to optimize queries always talk about adding indexes,
as you can see below for EXPLAIN statment I already have indexes
Do you guys have any Idea where I should look to improve speed os this query? I m not DB guy having hard time with this. This is a website and have 400,000 videos.
Thanks
The explain shows that you are not using an index on table phpfox_channel_video as m, and that it is using a temporary index on table phpfox_channel_category AS mc, which means it is not using an index, but is building an index first, which takes considerable time.
Also, the index for table phpfox_channel_category_data AS mcd could be better.
The indexes you need are:
CREATE INDEX idx_cat_data_video_id ON phpfox_channel_category_data
(category_id, video_id);
CREATE INDEX idx_channel_cat_id ON phpfox_channel_category (category_id);
CREATE INDEX idx_video_mult ON phpfox_channel_video
(in_process, view_id, module_id, item_id, privacy, video_id, time_stamp);
Don't fetch m.* if you are only going to do COUNT(*).
If phpfox_channel_category is a many-to-many mapping table, follow the tips in http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
m needs INDEX(in_process, view_id, module_id, item_id, privacy) in any order.
Avoid the GROUP BY:
INNER JOIN phpfox_channel_category AS mc ON(mc.category_id = mc.category_id)
INNER JOIN phpfox_channel_category_data AS mcd ON(mcd.video_id = m.video_id)
AND mcd.category_id = 17
GROUP BY m.video_id
--> (something like)
AND EXISTS(
SELECT 1
FROM phpfox_channel_category AS mc
JOIN phpfox_channel_category_data AS mcd
ON mcd.video_id = mc.video_id
WHERE mcd.video_id = 17
AND mc.video_id = m.video_id
)
Let's make sure that we are optimizing the right query. I suggest we check this condition in the ON clause:
mc.category_id = mc.category_id
We know that's going to be TRUE for every row in mc with a non-NULL value of category_id. We could express that condition as:
mc.category_id IS NOT NULL
This means the join is almost a cross join; every row returned from m matched with every row from mc. That is, we could get an equivalent result writing:
FROM phpfox_channel_video m
JOIN phpfox_channel_category mc
ON mc.category_id IS NOT NULL
I suspect that's not actually the result we're after. I think we were meaning to match to m.category_id. But that's just a guess.
If video_id column is PRIMARY KEY or UNIQUE KEY on m, we can avoid the potentially expensive GROUP BY operation by avoiding the joins that create duplicated rows, by using EXISTS with correlated subqueries. If we can avoid generating an intermediate result with duplicate values of video_id, then we can avoid the need to do the GROUP BY.
Also, for the inline view query, rather than return all columns * we can return just the expressions that we need. In the outer query, the only column referenced is user_id.
So we could write something like this:
SELECT COUNT(*) AS total_rows
FROM (
SELECT m.user_id
FROM phpfox_channel_video m
WHERE EXISTS ( SELECT 1
FROM phpfox_channel_category mc
WHERE mc.category_id = m.category_id
-- mc.category_id = mc.category_id -- <original
)
AND EXISTS ( SELECT 1
FROM phpfox_channel_category_data mcd
WHERE mcd.video_id = m.video_id
AND mcd.category_id = 17
)
AND m.in_process = 0
AND m.view_id = 0
AND m.module_id = 'videochannel'
AND m.item_id = 0
AND m.privacy IN (0)
ORDER BY m.time_stamp DESC
LIMIT 12
) d
JOIN phpfox_user u
ON u.user_id = d.user_id
For tuning, optimal index for m will have leading columns that have equality predicates, followed by the time_stamp column so that we can avoid a "Using filesort" operation, the ORDER BY can be satisfied by returning rows in index order. It looks like the reason we need the rows ordered is for the LIMIT clause.
... ON phpfox_channel_video (in_process, view_id, item_id, module_id
, time_stamp, video_id, ... )
The other two tables, we want indexes with leading columns that have equality predicates
... ON phpfox_channel_category_data (video_id, category_id, ...)
... ON phpfox_channel_category ( category_id, ... )
NOTES:
(It's not entirely clear why we need an inline view, and we are delaying the join from the user_id reference. Then again, the point of the entire query isn't really obvious to me; I'm just providing a re-write, given the provided SQL, with the change to the condition category_id.)
The above assumed that category_id column exists in m, and that it's a one-to-many relationship.
But if that's not true... if the mcd table is actually junction table, resolving a many-to-many relationship between video and category, such that the join condition was meant to be
mcd.category_id = mc.category_id
^
Then we would want to replace the WHERE EXISTS and AND EXISTS in the query above, into a single correlated subquery. Something like this:
SELECT COUNT(*) AS total_rows
FROM (
SELECT m.user_id
FROM phpfox_channel_video m
WHERE EXISTS ( SELECT 1
FROM phpfox_channel_category mc
JOIN phpfox_channel_category_data mcd
ON mcd.category_id = mc.category_id
WHERE mcd.video_id = m.video_id
AND mcd.category_id = 17
)
AND m.in_process = 0
AND m.view_id = 0
AND m.module_id = 'videochannel'
AND m.item_id = 0
AND m.privacy IN (0)
ORDER BY m.time_stamp DESC
LIMIT 12
) d
JOIN phpfox_user u
ON u.user_id = d.user_id

MySQL select best (and oldest) perform per athlete, categories

I am trying to build the SQL query from following table (example):
Example of table with name "performances"
This is table with athletic performances. I want to select the best perform from this table per discipline and set of one or more categories. Each athlete should be only once in result though his best perform value is twice or more in performance table.
Here is expected result from table "performances"
Actually I have this SQL query, but from subquery join all rows with best value for athlete_id and best:
SELECT
p.athlete_id, p.value
FROM
(SELECT athlete_id, MAX(value) AS best FROM performances
WHERE discipline_id = 32 AND category_id IN (1,3,5,7,9)
GROUP BY athlete_id) f
INNER JOIN performances p
ON p.athlete_id = f.athlete_id AND p.conversion = f.best
ORDER BY p.value DESC, p.created
Please, how can I join only one row for each athlete, which has a oldest created attributte?
To get the single row for each athlete per discipline based on greatest value value you can do a self left join, To handle the tie case or if single athlete has more than 1 rows having same maximum value you can use case statement to pick the row with oldest date
select a.*
from performances a
left join performances b
on a.discipline_id = b.discipline_id
and a.athlete_id = b.athlete_id
and case when a.value = b.value
then a.created > b.created
else a.value < b.value
end
where b.discipline_id is null
DEMO
Further you can add filter in your where clause
and a.discipline_id = 32
and a.category_id IN (1,3,5,7,9)
DEMO
You don't have to use joins, you can do it with a window function:
SELECT
p.athlete_id,
p.value
FROM
(
SELECT
athlete_id,
value,
ROW_NUMBER() over (partition by athlete_id order by value desc, created) rowid
FROM
performances
WHERE
discipline_id = 32 AND
category_id IN (1,3,5,7,9)
) p
where
p.rowid = 1
Thank you a lot, Guys. After your answers I finally found the solution.
SELECT r.* FROM
(SELECT p.athlete_id, p.conversion, MIN(p.created) AS created FROM
(SELECT athlete_id, MAX(conversion) AS best
FROM performances
WHERE discipline_id = 32 AND category_id IN (1,3,5,7,9)
GROUP BY athlete_id) f
INNER JOIN performances p ON p.athlete_id = f.athlete_id AND p.conversion = f.best
GROUP BY p.athlete_id) w INNER JOIN performances r
ON w.athlete_id = r.athlete_id AND w.conversion = r.conversion
AND ((w.created = r.created) OR (w.created IS NULL AND r.created IS NULL))
ORDER BY r.conversion DESC, r.created

Mysql Query modify

This is my Query I want to get latest record in each group.I have two table t_service_request and t_request_chkpoint
t_service_request
------------
LTS,JFT,CUS_NO,REQUETST_ID...
t_request_chkpoint
------------
LTS ,REQUETST_ID...
Both table match by REQUETST_ID.
I want to group by cus_no in table t_service_request
SELECT S.*, A.ID as CID, A.ENTRY_ID, A.LTS
FROM maintenance.t_service_request S
WHERE JFT IN (
SELECT MAX(JFT)
FROM maintenance.t_service_request
GROUP BY CUS_NO
) LEFT OUTER JOIN maintenance.t_request_chkpoint A
ON S.REQUEST_ID = A.REQUEST_ID where S.COMPANY_ID = '0002' AND S.STATE >= 3 AND A.STATE >= 3
but didn't work any suggestions ?
t_service_request
------------
LTS|JFT|CUS_NO|REQUETST_ID|
t_request_chkpoint
------------
|LTS|REQUETST_ID|
Join above two table(Request_id) and select latest JFT in each CUS_NO
Try this, maybe works;)
SELECT DISTINCT
S.*,
A.ID AS CID,
A.ENTRY_ID,
A.LTS
FROM maintenance.t_service_request S
LEFT JOIN maintenance.t_request_chkpoint A ON A.REQUETST_ID = S.REQUETST_ID AND A.STATE >= 3
WHERE S.JFT = (SELECT MAX(B.JFT)
FROM maintenance.t_service_request B
WHERE B.CUS_NO = S.CUS_NO
GROUP BY B.CUS_NO)
AND S.COMPANY_ID = '0002' AND S.STATE >= 3
I think your sql may have some syntax errors and I am not sure I've misunderstood your requirement or not.
I must admit, I still don't understand what you are asking. Your query, however, is incomplete, and maybe fixing it solves your problem already.
You say you want "to get latest record in each group" and in your query you are looking for the maximum JFT per CUS_NO. Then, however you are only comparing the JFT and not the CUS_NO.
Moreover, your query is syntactically incorrect, as it has two WHERE clauses. Last but not least, (outer) join criteria (state >= 3 here) belongs in the ON clause, not in the WHERE clause.
Here is the corrected query:
select
sr.*,
rc.id as cid,
rc.entry_id,
rc.lts
from maintenance.t_service_request sr
left outer join maintenance.t_request_chkpoint rc on rc.request_id = sr.request_id
and rc.state >= 3
where sr.company_id = '0002' and sr.state >= 3
and (sr.cus_no, sr.jft) in
(
select cus_no, max(jft)
from maintenance.t_service_request
group by cus_no
);

Understanding why this query is slow

The below query is very slow (takes around 1 second), but is only searching approx 2500 records (+ inner joined tables).
if i remove the ORDER BY, the query runs in much less time (0.05 or less)
OR if i remove the part nested select below "# used to select where no ProfilePhoto specified" it also runs fast, but i need both of these included.
I have indexes (or primary key) on :tPhoto_PhotoID, PhotoID, p.Enabled, CustomerID, tCustomer_CustomerID, ProfilePhoto (bool), u.UserName, e.PrivateEmail, m.tUser_UserID, Enabled, Active, m.tMemberStatuses_MemberStatusID, e.tCustomerMembership_MembershipID, e.DateCreated
(do i have too many indexes? my understanding is add them anywhere i use WHERE or ON)
The Query :
SELECT e.CustomerID,
e.CustomerName,
e.Location,
SUBSTRING_INDEX(e.CustomerProfile,' ', 25) AS Description,
IFNULL(p.PhotoURL, PhotoTable.PhotoURL) AS PhotoURL
FROM tCustomer e
LEFT JOIN (tCustomerPhoto ep INNER JOIN tPhoto p ON (ep.tPhoto_PhotoID = p.PhotoID AND p.Enabled=1))
ON e.CustomerID = ep.tCustomer_CustomerID AND ep.ProfilePhoto = 1
# used to select where no ProfilePhoto specified
LEFT JOIN ((SELECT pp.PhotoURL, epp.tCustomer_CustomerID
FROM tPhoto pp
LEFT JOIN tCustomerPhoto epp ON epp.tPhoto_PhotoID = pp.PhotoID
GROUP BY epp.tCustomer_CustomerID) AS PhotoTable) ON e.CustomerID = PhotoTable.tCustomer_CustomerID
INNER JOIN tUser u ON u.UserName = e.PrivateEmail
INNER JOIN tmembers m ON m.tUser_UserID = u.UserID
WHERE e.Enabled=1
AND e.Active=1
AND m.tMemberStatuses_MemberStatusID = 2
AND e.tCustomerMembership_MembershipID != 6
ORDER BY e.DateCreated DESC
LIMIT 12
i have similar queries that but they run much faster.
any opinions would be grateful:
Until we get more clarity on your question between working in other query etc..Try EXPLAIN {YourSelectQuery} in MySQL client and see the suggestions to improve the performance.

mysql sub select newbie

SELECT a.lead_id, c.state_name AS COL1DATA, count( c.state_name ) AS leadcount, (
SELECT count( won_loss ) AS wonlosscount
FROM lead_status
WHERE (won_loss = 'loss')
AND lead_id = a.lead_id
) AS losscount
FROM lead AS a
JOIN states AS c ON a.state_id = c.states_id
GROUP BY c.state_name
ORDER BY losscount DESC
the answer i get is
lead_id COL1DATA leadcount losscount
1 Queensland 7 0
8 Victoria 3 0
lead status
lead_id won_loss won_price won_mainreason loss_mainreason loss_attachment_id lost_dont_sell_note add_note dealer_satisfaction
5 win 4655 pricing fghfg somewhat
8 won 34543 pricing sfdgs satisfied
7 loss service Additional Notes verygood
9 loss not_in_stock Additi satisfied
but the loss count should be 1 and 1
any help is appricated
I'm guessing that there's a problem with mixing the non-aggregated lead_id in the correlated query all the while grouping on state_name. Perhaps you can describe what you're looking to get back.
EDIT: Based on OP feedback in comment below.
EDIT 2: Changed to left outer joins based on chat session. Not all leads have a lead_status.
SELECT
s.state_name AS COL1DATA, count(c.state_name) AS leadcount,
sum(case when ls.won_loss = 'loss' then 1 else 0 end) as losscount
FROM
lead AS l
INNER JOIN states AS s ON s.state_id = l.states_id
LEFT OUTER JOIN lead_status as ls on ls.lead_id = l.lead_id
GROUP BY s.state_name
ORDER BY losscount DESC
I might argue that this version is slightly better. But I didn't want to totally change your query. (I did change the aliases because A and C were confusing.)
SELECT
min(s.state_name) AS COL1DATA,
count(l.lead_id) AS leadcount, /* counting non-nullable key on the outer side */
sum(case when ls.won_loss = 'loss' then 1 else 0 end) as losscount
FROM
lead AS l
INNER JOIN states AS s ON s.state_id = l.states_id
LEFT OUTER JOIN lead_status as ls on ls.lead_id = l.lead_id
GROUP BY s.state_id /* might be better to group on the id */
ORDER BY losscount DESC
The lead_id column you have included in the output is unpredictable unless the group only has one row. Based on what you've said, I doubt that you really want it.