MySQL JOIN Queries to find references and conversions - mysql

I have the following three tables User, References and Conversions. The goal is to find out which users have given how many references and how many of these references have been converted.
user
id user_id
1 001
2 002
3 003
4 004
5 005
6 006
7 007
8 008
9 009
10 010
references
id referer_id referee_id
1 001 005
2 001 006
3 002 007
4 003 008
5 004 009
6 005 010
conversions
id user_id
1 005
2 006
3 007
4 008
5 009
REPORT
id references(count) conversions(count)
001 2 2
002 1 1
003 1 1
004 1 1
005 1 0
Please suggest a MYSQL Query to produce the above Report.
Copied from the comment into original question
SELECT
user_id,
referenceCount,
conversionCount
FROM
users u
JOIN (SELECT referer_id,
COUNT(referee_id) AS referenceCount
FROM references
GROUP BY referer_id) r
ON r.referer_id = user_id
LEFT JOIN (SELECT user_id, COUNT(user_id) AS conversionCount
FROM conversions) c
ON c.user_id = r.referee_id GROUP BY u.user_id

You are on the right track with joins, but they should be left-joins.
SELECT
u.user_id,
count( distinct r.referer_id ) as CntRefs,
count( distinct c.user_id ) as CntConvert
FROM
users u
LEFT JOIN references r
on u.user_id = r.referer_id
LEFT JOIN conversions c
on r.referee_id = c.user_id
group by
u.user_id

I was able to achieve my result using the following SQL command. Many thanks to #DRapp for the help.
SELECT
u.user_id,
count( r.referer_id ) as CntRefs,
count( c.user_id ) as CntConvert
FROM
users u
JOIN reference r
on u.user_id = r.referer_id
LEFT JOIN conversions c
on r.referee_id = c.user_id
group by
u.user_id

Related

How to create MySQL query that converts rows to columns?

I'm creating an employee tracking app.
I have a MySQL table which is as follows:
ID
PersonID
TypeID
DateTime
1
001
IN
2022-09-01T13:21:12
2
001
OUT
2022-09-01T13:25:12
3
001
IN
2022-09-01T14:21:12
4
001
OUT
2022-09-01T14:25:12
5
002
IN
2022-09-03T13:21:12
6
002
OUT
2022-09-03T13:25:12
7
002
IN
2022-09-03T14:21:12
8
002
IN
2022-09-03T14:25:12
9
002
OUT
2022-09-03T14:25:12
10
002
OUT
2022-09-03T16:25:12
11
002
OUT
2022-09-03T17:25:12
12
002
IN
2022-09-04T16:25:12
13
002
IN
2022-09-05T17:25:12
I would like to create a view that returns records first sorted by PersonID and then by the ID but transforms the rows into columns.
Something like this:
PersonID
InID
In_DateTime
OutID
Out_DateTime
001
1
2022-09-01T13:21:12
2
2022-09-01T13:25:12
001
3
2022-09-01T14:21:12
4
2022-09-01T14:25:12
002
5
2022-09-03T13:21:12
6
2022-09-03T13:25:12
002
7
2022-09-03T14:21:12
null
null
002
8
2022-09-03T14:25:12
9
2022-09-03T14:25:12
002
null
null
10
2022-09-03T16:25:12
002
null
null
11
2022-09-03T17:25:12
002
12
2022-09-04T16:25:12
null
null
002
13
2022-09-05T17:25:12
null
null
Does anyone have an idea how to do this in MySQL?
Thanks for any suggestions.
Use window functions LEAD() or LAG() to get for each row its pair row, depending on its TypeID and do a left join of the results to the table:
WITH cte AS (
SELECT *,
CASE
WHEN TypeID = 'IN' AND LEAD(TypeID) OVER w = 'OUT' THEN LEAD(ID) OVER w
WHEN TypeID = 'OUT' AND LAG(TypeID) OVER w = 'IN' THEN LAG(ID) OVER w
END other_ID
FROM tablename
WINDOW w AS (PARTITION BY PersonID ORDER BY DateTime)
)
SELECT DISTINCT c.PersonID,
CASE WHEN c.TypeID = 'IN' THEN c.ID ELSE t.ID END InID,
CASE WHEN c.TypeID = 'IN' THEN c.DateTime ELSE t.DateTime END In_DateTime,
CASE WHEN c.TypeID = 'IN' THEN t.ID ELSE c.ID END OutID,
CASE WHEN c.TypeID = 'IN' THEN t.DateTime ELSE c.DateTime END Out_DateTime
FROM cte c LEFT JOIN tablename t
ON t.ID = c.other_ID
ORDER BY c.PersonID, COALESCE(In_DateTime, Out_DateTime);
See the demo.

add date to every name

hello please help me to solve this,, i am using mysql.. and I think, needing a full outer join to solve it,.
this is my query:
SELECT
e.NIK,
e.name,
a.dt
FROM
employee e
LEFT JOIN
attandence a
ON e.NIK=a.NIK
WHERE
month(a.dt)=12 AND year(a.dt)=2021
GROUP BY
e.NIK
this is the result:
NIK
name
dt
001
ana
23/12/2021
001
ana
24/12/2021
001
ana
26/12/2021
001
ana
27/12/2021
002
susi
23/12/2021
002
susi
24/12/2021
002
susi
25/12/2021
002
susi
26/12/2021
but i need to join it one more time with this table :
holidayTable
id
mark
dt
1
off_day
22/12/2021
2
chrismast
25/12/2021
I've tried using left/right/cross join, but it doesn't work
The result I want is like this:
NIK
name
dtWork
holiday
001
ana
null
22/12/2021
001
ana
23/12/2021
null
001
ana
24/12/2021
null
001
ana
null
25/12/2021
001
ana
26/12/2021
null
001
ana
27/12/2021
null
002
susi
null
22/12/2021
002
susi
23/12/2021
null
002
susi
24/12/2021
null
002
susi
25/12/2021
25/12/2021
002
susi
26/12/2021
null
You need a calendar table to achieve the output.
As of now I used union between holidaytable and attendance as Calendar.
You can achieve your desired result with below query.
SELECT e.nik,
e.name,
a.dt AS dtWork,
h.dt AS holiday
FROM employee e
CROSS JOIN(SELECT dt
FROM holidaytable
UNION
SELECT dt
FROM attendance)cal
LEFT JOIN attendance a
ON e.nik = a.nik
AND a.dt = cal.dt
LEFT JOIN holidaytable h
ON h.dt = cal.dt
WHERE a.dt IS NOT NULL
OR h.dt IS NOT NULL
ORDER BY nik ASC,
cal.dt ASC
SQLFiddle: Try it here
A union where the first part gets attendence and the second gets holidays (with as you thought a cross join) including a dummy column to assist ordering
select e.id,employeelastname,a.dt attendence,null holiday,a.dt as orderdt
from employees e
left join attendence a on a.nik = e.id
union
select e.id,employeelastname,null,h.dt holiday,h.dt
from employees e
cross join holidays h
order by id,orderdt;

mysql query not working as expected complex

I have 3 tables in which all 3 tables are joined to get the result.
Below is the table,
//tbl_order
order_id order_no_first order_no order_no_last order_date
---------------------------------------------------------------------
1 C 1000 a 2017-05-16
2 C 1001 a 2017-05-16
3 C 1001 b 2017-05-16
4 A 1002 a 2017-05-16
5 A 1002 b 2017-05-16
//tbl_assign
assign_id order_id order_no_first central_status central_assign_unit
----------------------------------------------------------------------------
1 1 C 1 1
2 2 C 1 1
3 3 C 1 1
4 4 A 1 1
//tbl_unit_status
status_id assign_id status_status
---------------------------------------
1 1 Cutter
2 1 Stitch
3 1 Delivery
4 2 Cutter
5 2 Stitch
6 3 Cutter
7 4 Cutter
I want the result as below,
//Required output
order_id assign_id order_no_first order_no order_no_last status_status
---------------------------------------------------------------------------
2 2 C 1001 a Stitch
3 3 C 1001 b Cutter
4 4 A 1002 a Cutter
5 A 1002 b
from the table tbl_unit_status the status below status_status field, if it is Despatch then do not display that result.
I have tried to get the above result. But no success below is my code.
`SELECT *
FROM tbl_order o
LEFT JOIN tbl_assign a
ON a.order_id = o.order_id AND o.order_no_first = a.order_no_first
LEFT JOIN (
SELECT u.assign_id, max(u.status_id) AS maxid
FROM tbl_unit_status u GROUP BY u.assign_id) uu
ON uu.assign_id = a.assign_id
LEFT JOIN tbl_unit_status u2 on u2.status_id = uu.maxid
WHERE a.central_status = 1 AND a.central_assign_unit = 1
OR (u2.status_status != "Delivery" AND u2.status_status != "Despatch")
GROUP BY o.order_id
From the above code, the result is
//wrong output
order_id assign_id order_no_first order_no order_no_last status_status
---------------------------------------------------------------------------
1 1 C 1000 a Delivery
2 2 C 1001 a Stitch
3 3 C 1001 b Cutter
4 4 A 1002 a Cutter
5 A 1002 b
Is there any way to get the Required output as soon in the first output. I have tried and am stuck in here.
Thank you.
Use parenthesis correctly arround AND OR clauses and I recommend using NOT LIKE instead of != when dealing with strings.
SELECT *
FROM tbl_order o
LEFT JOIN tbl_assign a
ON a.order_id = o.order_id AND o.order_no_first = a.order_no_first
LEFT JOIN (
SELECT u.assign_id, max(u.status_id) AS maxid
FROM tbl_unit_status u GROUP BY u.assign_id) uu
ON uu.assign_id = a.assign_id
LEFT JOIN tbl_unit_status u2 on u2.status_id = uu.maxid
WHERE a.central_status = 1 AND (a.central_assign_unit = 1 OR u2.status_status NOT LIKE 'Delivery' AND u2.status_status NOT LIKE 'Despatch')
GROUP BY o.order_id
Obs: I can't comment due to my reputation, I'm sorry
You should be using AND instead of OR in your where clause. You don't need the parentheses either.
SELECT *
FROM tbl_order o
LEFT JOIN tbl_assign a
ON a.order_id = o.order_id
AND o.order_no_first = a.order_no_first
LEFT JOIN
(SELECT u.assign_id, max(u.status_id) AS maxid
FROM tbl_unit_status u
GROUP BY u.assign_id
) uu ON uu.assign_id = a.assign_id
LEFT JOIN tbl_unit_status u2
on u2.status_id = uu.maxid
WHERE a.central_status = 1
AND a.central_assign_unit = 1
AND (
u2.status_status IS NULL
OR
u2.status_status NOT IN ("Delivery", "Despatch")
)
GROUP BY o.order_id

MYSQL Stuck Generating temp table (massive query)

I have 4 tables (1 to many):
Dont say anything about that "email" relation. It is how my developer boss built it years ago.
EMPLOYEES (+-50 results)
------------------------------------------------
id name
1 EmpName 1
2 EmpName 2
CUSTOMERS (+50k results)
------------------------------------------------
id name email employee_assigned
1 John john#doe.com 12
2 Donald donald#duck.com 6
INTERESTS_CATEGORIES (+650k results)
------------------------------------------------
id customer_email category_id
1 john#doe.com 97
2 john#doe.com 13
3 donald#duck.com 56
4 donald#duck.com 126
5 donald#duck.com 45
INTERESTS_PRODUCTS (+650k results)
------------------------------------------------
id customer_email product_id
1 john#doe.com 78
2 john#doe.com 23
3 donald#duck.com 19
4 donald#duck.com 56
5 donald#duck.com 45
So I need to filter the customers by their assigned employee and their interests.
And here is the query:
SELECT
*
FROM
(
SELECT
customers.id AS 'id',
customers.name AS 'first_name',
customers.email,
employees.id AS 'employee_id'
FROM
customers,
employees
WHERE
employees.id = 2
AND
customers.employee_assigned = employees.id
) AS myCustomers
LEFT JOIN interests_categories
ON interests_categories.customer_email = myCustomers.email
LEFT JOIN interests_products
ON interests_categories.customer_email = myCustomers.email
WHERE
(
interests_categories.category_id = 20
OR
interests_categories.category_id = 21
)
GROUP BY myCustomers.email
So, the problem:
If the employee has a low number of assigned customers (like 3) query
is successfull.
If the employee has a medium-high number of assigned customers (over 100) query stucks.
I execute SHOW PROCESSLIST and it is stucked "Generating temp table".
Anyone has idea? :(
Thank you.
Check the indexes on your tables and try this:
SELECT
c.id AS 'id',
c.name AS 'first_name',
c.email,
e.id AS 'employee_id'
ic.*,
ip.*
FROM customers c
JOIN employees e
ON c.employee_assigned = e.id
LEFT JOIN interests_categories ic
ON ic.customer_email = c.email
LEFT JOIN interests_products ip
ON ic.customer_email = c.email
WHERE
(
ic.category_id IN (20,21)
AND e.id = 2
)
GROUP BY myCustomers.email
Incidentally, a less dumb design might look like as follows. If it was me, I'd start with this, and provide properly representative CREATE and INSERT statements accordingly. Also, I'm curious about where category_id comes from - because that's potentially an area for further optimization.
EMPLOYEES
------------------------------------------------
employee_id name
6 EmpName 1
12 EmpName 2
CUSTOMERS
------------------------------------------------
customer_id name email employee_assigned
1 John john#doe.com 12
2 Donald donald#duck.com 6
INTERESTS_CATEGORIES
------------------------------------------------
customer_id category_id
1 97
1 13
2 56
2 126
2 45
INTERESTS_PRODUCTS
------------------------------------------------
customer_id product_id
1 78
1 23
2 19
2 56
2 45

MySQL: Finding the missing values from tables

I have two different tables:
checkin_out consists of two fields: emp_code, checked_date
temp_days consists of two fields: id, date_value
Table checkin_out has following data:
emp_code | checked_date
-----------------------
001 2012-11-01
001 2012-11-02
001 2012-11-03
002 2012-11-01
003 2012-11-01
003 2012-11-02
While table temp_days has following data:
id | date_value
-----------------
1 2012-11-01
2 2012-11-02
3 2012-11-03
4 2012-11-04
5 2012-11-05
From the above tables, I need to show the missing dates in the table temp_days; I need to query to get a result as follow:
emp_code | date_value
-----------------------
001 2012-11-04
001 2012-11-05
002 2012-11-02
002 2012-11-03
002 2012-11-04
002 2012-11-05
003 2012-11-03
003 2012-11-04
003 2012-11-05
If anyone could help, please! Thanks!
The below works for a more complex data set with multiple emp_codes.
SELECT alldays.emp_code, alldays.date_value
FROM (
SELECT date_value, emp_code
FROM temp_days
CROSS JOIN checkin_out
GROUP BY date_value, emp_code
) alldays
LEFT JOIN checkin_out C
ON alldays.date_value = C.checked_date
AND alldays.emp_code = C.emp_code
WHERE C.emp_code IS NULL
SELECT c.emp_code, a.date_value
FROM temp_days a
LEFT JOIN checkin_out b
ON a.date_value = b.checked_date
CROSS JOIN
(
SELECT emp_code
FROM checkin_out
GROUP BY emp_code
) c
WHERE b.emp_code IS NULL
SQLFiddle Demo