mysql sub select newbie - mysql

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.

Related

MySQL Join v. Subquery to get a conditional date while optimizing performance

I need your help on deciding which query to use since we are facing performance issue with MySQL joins and Subqueries.
The problem is that I'm trying to find out user's 'first order date' while they should fit certain conditions:
order_status = 1(completed) or order_status = 2(canceled)
The Tables are tb_order and tb_user; All the columns that contain a 'time' are using Unix Time Stamp.
The result I need looks like this:
order_id
user_id
user_1st_order_date
1
47
1666876594
2
982
1667095997
Option 1: JOIN
Select
o.id as 'order_id',
u.id as 'user_id',
ox.create_time as 'user_1st_order_date'
from
tb_order o
left join tb_user u on o.user_id = u.id
/* here I have about 10 joins */
left join
(
select
ux.id,
ox.create_time
from
tb_user u
left join tb_order ox on ox.user_id = u.id
where
( ox.order_status = 1 or ox.order_status = 2 )
/* Orders can be (completed) or (canceled) */
group by
ux.id
) x on x.id = u.id
/* The thought here is by using group by `ux.id` I will get the
user's earliest completed or canceled order and it's `create_time`
then this can be used to `join` the order info */
where
o.create_time != 0
and
( o.order_status = 1 or o.order_status = 2 )
group by
o.id
Option 2: Subquery
Select
o.id as 'order_id',
u.id as 'user_id',
(
select
ox.create_time
from
tb_order ox
where
(ox.order_status = 1 or ox.order_status = 2)
and
ox.user_id = u.id
order by
ox.id asc
limit 1
) as 'user_1st_order_date'
from
tb_order o
left join tb_user u on o.user_id = u.id
/* here I have about 10 joins */
where
o.create_time != 0
and
( o.order_status = 1 or o.order_status = 2 )
group by
o.id
/* Option 1 stopped working somehow yesterday and start to give me the latest order time instead, and I don't know why. Though I can get the correct date back by putting 'Min()' in front of the ox.create_time */
left join
(
select
ux.id,
Min(ox.create_time)
Both worked but I'm trying to find the most efficient one since I'll use this on a daily basis to update our data source for Tableau Online.
Many thanks in advance.
Just looking at query 1, you have set out a crazy set of table relationships.
Starting with the Select in parentheses, you have a Left Join that implies there are users without orders. That's OK, but your Where filter is based solely on order status, which is NULL when there is no order, so all such users will be filtered out. There is no useful purpose being served by joining the tb_user table and it can be omitted from that subquery.
In the outer query the Left join of tb_order to tb_user implies there are orders without users, but then joining the subquery using u.id instead of o.userid guarantees that nothing from the subquery will be usable in that case. Once again, there is no purpose served in bring tb_user in there either.
To get the desired result set you set out above, you can vastly simplify things by looking only at the tb_order table like Option 3 below:
Option 3
Select * From (
Select id as 'order_id', user_id as 'user_id'
,min(Case When order_status In (1,2) Then create_time End)
Over (Partition By user_id
Between unbounded preceding And unbounded following)
AS 'user_1st_order_date'
From tb_order
)
Where order_status in (1,2)
Order by order_id
This can be further simplified by moving the Where order_status in (1,2) inside the inner query and removing the Case statement around the created_date, but it's less adaptable to use within other queries.

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

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

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.

mysql sum one table but different type

task_payments
SELECT t.id AS task_id, t.name, t.created_at
,COALESCE(SUM(tp1.amount),0) AS paid
,COALESCE(SUM(tp2.amount),0) AS paid_back
FROM tasks AS t
LEFT JOIN task_payments AS tp1 ON tp1.task_id=t.id AND tp1.type='1'
LEFT JOIN task_payments AS tp2 ON tp2.task_id=t.id AND tp2.type='0'
WHERE t.customer_id='4'
GROUP BY tp1.task_id, tp2.task_id
ORDER BY t.id ASC
Hi, There is two type(1 OR 0) on task_payments. type 0 is paid back. type 1 is paid. I want separately total amount as result. so I want result;
task_id=5
paid=450
paid_back=10
I should use join. If there is a filter request, I am going to use paid and paid_colums on where clause. ex: and paid_back>0
maybe the following query may help you :D
SELECT x.*
FROM
(
SELECT a.task_id,
SUM(CASE WHEN b.type = 1 THEN b.amount ELSE 0 END) paid,
SUM(CASE WHEN b.type = 0 THEN b.amount ELSE 0 END) paidBack
FROM tasks a
LEFT JOIN task_payments b
ON a.id = b.task_id
-- WHERE a.customer_id = 4
GROUP BY a.task_id
) x
-- WHERE x.paid > 100 -- sample Request
Apart from JW's answer I would like to suggest one thing,
If your requirement is to default nulls then go for
nvl(sum(field),0) instead of COALESCE(SUM(tp1.amount),0)
If your db doesnt support nvl then go for IFNULL
Hope this also helps you :)