Find oldest account per company in sql - mysql

I have a few tables that look like the below
Users
-----------------------------------------
| id | policyId | createdAt | updatedAt |
-----------------------------------------
| 1 | 1 | 2017/8/5 | 2017/8/5 |
| 2 | 1 | 2016/4/5 | 2017/8/5 |
| 3 | 2 | 2017/7/2 | 2017/8/5 |
| 4 | 2 | 2018/8/5 | 2017/8/5 |
-----------------------------------------
Policies
------------------------------------------
| id | companyId | createdAt | updatedAt |
------------------------------------------
| 1 | 1 | 2017/8/5 | 2017/8/5 |
| 2 | 2 | 2016/4/5 | 2017/8/5 |
------------------------------------------
Companies
-----------------------------------------
| id | policyId | createdAt | updatedAt |
-----------------------------------------
| 1 | 2 | 2017/8/5 | 2017/8/5 |
| 2 | 1 | 2016/4/5 | 2017/8/5 |
-----------------------------------------
I need to answer the question "What is the id of the user for each company with the oldest account. So the output should look something like this.
Output
----------------------------------
| CompanyId | UserId | CreatedAt |
----------------------------------
| 1 | 2 | 2016/4/5 |
| 2 | 3 | 2017/7/2 |
----------------------------------
What I have gotten so far looks something like this but I know it is no were near correct.
SELECT c.id, MIN(u.createdAt) FROM companies as c
JOIN policies as p on p.companyId = c.id
JOIN users as u on u.policyId = p.id
GROUP BY c.id;
This seems to let me get the oldest date for each company user but I am not sure how to correlate the users back to that date to get the user id's. I am thinking the query above might have to be a sub-query but that is about as far as my sql knowledge goes.
Any help would be appreciated.

I need to join the whole query to itself and join by comapny id and createdAt column.
see demo here: http://sqlfiddle.com/#!9/6f4ea7/22
SELECT c.id as companyID,
u.id as userID,
u.createdAt
FROM companies as c
JOIN policies as p on p.companyId = c.id
JOIN users as u on u.policyId = p.id
JOIN (SELECT c.id as companyID,
min(u.createdAt) as min_dt
FROM companies as c
JOIN policies as p on p.companyId = c.id
JOIN users as u on u.policyId = p.id
GROUP BY c.id) sub
on c.id=sub.companyID
where u.createdAt=sub.min_dt

SELECT p.company_id, u.user_id, MIN(u.createdAt) FROM policies p, user_u,
(SELECT min(createdAt) as minDate, policy_id as policyId from users
GROUP BY policy_id) as sub
WHERE p.id = u.policy_id
AND sub.minDate = u.createdAt
AND sub.policy_id = u.policyId;

It will give u the expected output But some hard coded way
SELECT c.id as companyid,u.id as userid, MIN(u.createdAt) as createdAt FROM company as c
JOIN policies as p on p.cmpid = c.id
JOIN users as u on u.policyId = p.id
GROUP BY c.id,u.id order by MIN(u.createdAt) asc limit 2;

Related

How to limit and search number of joined rows from table in multiple joins in mysql

i have a following tables in MySQL database:
+------------------------+
| Users |
+----+--------+----------+
| id | name | role |
+----+--------+----------+
| 1 | Martin | admin |
+----+--------+----------+
| 2 | George | admin |
+----+--------+----------+
| 3 | John | employee |
+----+--------+----------+
+-------------------------+
| Forms |
+----+--------------------+
| id | type |
+----+--------------------+
| 10 | marketing_form |
+----+--------------------+
| 11 | client_survey_form |
+----+--------------------+
| 12 | client_survey_form |
+----+--------------------+
+---------------------------------------------+
| UsersAssignToForms |
+----+---------+---------+--------------------+
| id | user_id | form_id | additional_comment |
+----+---------+---------+--------------------+
| 20 | 1 | 10 | Lorem ipsum... |
+----+---------+---------+--------------------+
| 21 | 2 | 10 | Lorem ipsum.... |
+----+---------+---------+--------------------+
| 22 | 3 | 10 | null |
+----+---------+---------+--------------------+
| 23 | 3 | 11 | null |
+----+---------+---------+--------------------+
I would like to have result:
+---------+---------+------------+--------------------+--------------------+
| user_id | form_id | first_name | form_type | additional_comment |
+---------+---------+------------+--------------------+--------------------+
| 1 | 10 | Martin | marketing_form | Lorem ipsum... |
+---------+---------+------------+--------------------+--------------------+
| 3 | 11 | John | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
| null | 12 | null | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
First of all i would like to limit number of users returned from join query (one user per one form). If user with admin role is assigned to form i would like to display this user (prioritize admin role over employee role) and limit number of returned users to 1, if admin is not assign, but employee is assigned query should return this user, if no-one is assign query should return nulls (left or right join probably).
I saw this question on stackoverflow - MySQL JOIN with LIMIT 1 on joined table, but unfortunately first answer has n+1 issue and rest of answers was made with simple one join. For my purposes i need to join more tables but wouldn't like to design this tables above to clarify what i would like to achieve, but it's very important.
So my query will looks like probably:
SELECT u.id, f.id, u.name, f.type, uf.additional_comment, [more selects from other tables...] FROM Forms as f
LEFT JOIN Users as u ON ......
INNER JOIN UsersAssignToForms as uf ON .....
[here i would like to put more and more inner joins.....]
In MySql >= 8.0 you can number the rows using some criteria (for each Form starting from one and order by u.role ASC and u.id ASC), then you can filter rows with number one:
WITH sq AS (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment,
ROW_NUMBER() OVER (PARTITION BY f.id ORDER BY u.role ASC, u.id ASC) AS num
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id)
SELECT *
FROM sq
WHERE num = 1;
Before MySql 8.0 you can try something like this (the idea is the same but with different implementation):
SELECT sq2.user_id, sq2.form_id, sq2.name, sq2.type, sq2.additional_comment
FROM (
SELECT
sq1.*,
#row_number:=CASE WHEN #form_id = sq1.form_id THEN #row_number + 1 ELSE 1 END AS num,
#form_id:= sq1.form_id
FROM (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id
ORDER BY f.id ASC, u.role ASC, u.id ASC) AS sq1
ORDER BY sq1.form_id) AS sq2
WHERE sq2.num = 1;

Find the number of users who made their first order in each country, each day

I have 2 tables which looked like:
user_dataset
+------+---------------+------------+
|userid| Register_time | Country |
+------+---------------+------------+
| 1 | 03/02/17 | TW |
| 2 | 20/03/17 | SG |
| 3 | 26/03/17 | PH |
| 4 | 05/02/17 | VN |
| 5 | 01/10/17 | ID |
| 6 | 03/09/17 | MY |
| ...| ........ | ... |
+------+---------------+------------+
order_dataset
+--------+--------+------------+--------+------------+
|orderid | userid | itemid | gmv | order_time |
+--------+--------+------------+--------+------------+
|1030132 | 3 | 3366770 | 27,0 | 24/04/17 |
|1030137 | 5 | 6130641 | 69,0 | 02/02/17 |
|1030147 | 1 | 6770063 | 87,0 | 25/04/17 |
|1030153 | 6 | 4193426 | 82,0 | 05/11/17 |
|1030155 | 4 | 8825994 | 29,0 | 03/07/17 |
|1030160 | 2 | 5660916 | 44,0 | 30/01/17 |
|....... | ... | ... | ... | ... |
+--------+--------+------------+--------+------------+
I have been told to Write a SQL statement to find the number of users who made their first order in each country, each day.
I don't understand the question, can someone explain to me what the output looks like? I'm not asking the Queries but if someone willing to write it would be wonderful tho.
Thank you in advance
Join the table user_dataset to a query that returns from order_dataset the date of the 1st order of each userid and aggregate:
select u.country, o.order_time, count(*) counter
from user_dataset u inner join (
select userid, min(order_time) order_time
from order_dataset
group by userid
) o on o.userid = u.userid
group by u.country, o.order_time
If there is a case a user has placed 2 orders at the same day, then instead of count(*) use count(distinct userid).
SELECT a.userid, a.purchase_time,c.country
FROM purchase_tab a
INNER JOIN
(SELECT userid, MIN(purchase_time) As first_occurence
FROM purchase_tab
GROUP BY userid) b
ON a.userid = b.userid AND a.purchase_time = b.first_occurence
INNER JOIN user_tabs c
ON a.userid=c.userid
ORDER BY a.userid
SELECT
country,
order_time,
COUNT(*)
FROM user u
LEFT JOIN(
SELECT
userid,
MIN(order_time) AS min_date
FROM order
GROUP BY userid
) o
ON u.userid = o.userid
LEFT JOIN order ot
ON u.userid = ot.userid
WHERE order_time = min_date
GROUP BY country, order_time
ORDER BY country;

Group 3 tables by max date

A customer can make many inquiries, and an inquiry can have many updates. I'm trying to view each inquiry and the latest update. I'm able to pull the latest date but not the update that's relevant to it. I've seen answers similar to mine but they only seem to deal with one join and I don't understand how I can use those to find a solution.
Here's my sql
select c.name, i.inquirycontent, u.updatecontent, max(u.date) from inquiries i
inner join customers c on c.customerid = i.customerid
left join updates u on u.inquiryid = i.inquiryid
group by i.inquiryid
Even if I omit the customer table, I am still unable to match the latest update to its content. How can I do this?
edit - as requested, here is some sample data
+------------+------+
| customerid | name |
+------------+------+
| 1 | jeff |
+------------+------+
| 2 | anne |
+------------+------+
+-----------+-----------------+------------+
| inquiryid | inquirycontent | customerid |
+-----------+-----------------+------------+
| 1 | inquiry1content | 1 |
+-----------+-----------------+------------+
| 2 | inquiry2content | 1 |
+-----------+-----------------+------------+
| 3 | inquiry3content | 2 |
+-----------+-----------------+------------+
+----------+-----------------+-----------+----------+
| updateid | updatecontent | inquiryid | date |
+----------+-----------------+-----------+----------+
| 1 | update1content | 1 | 01-01-17 |
+----------+-----------------+-----------+----------+
| 2 | update2content | 1 | 03-01-17 |
+----------+-----------------+-----------+----------+
| 3 | update3content | 3 | 04-01-17 |
+----------+-----------------+-----------+----------+
And here's what I want the query to output -
+------+-----------------+----------------+----------+
| name | inquirycontent | latestupdate | date |
+------+-----------------+----------------+----------+
| jeff | inquiry1content | update2content | 03-01-17 |
+------+-----------------+----------------+----------+
| jeff | inquiry2content | NULL | NULL |
+------+-----------------+----------------+----------+
| anne | inquiry3content | update3content | 04-01-17 |
+------+-----------------+----------------+----------+
This is a crude solution using UNION, but it works for what I need. The first select finds all unique inquiries by their latest update, and the second select finds all rows that haven't yet been updated.
(select c.name, i.inquirycontent, u.updatecontent, t2.mxdate
from updates u
inner join inquiries i on i.inquiryid = u.inquiryid
inner join customers c on c.customerid = i.customerid
inner join
(
select max(u.date) mxdate, i.inquiryid
from updates u
left join inquiries i on i.inquiryid = u.inquiryid
group by i.inquiryid
) t2
on u.inquiryid = t2.inquiryid
and u.date = t2.mxdate)
UNION
(select c.name, i.inquirycontent, u.updatecontent, u.date
from inquiries i
left join updates u on u.inquiryid = i.inquiryid
inner join customers c on c.customerid = i.customerid
where u.updatecontent is NULL)

MySQL GroupBy with null/zero results

I'm currently writing a ticket system that has three tables
one for users:
users
+----+-----------+----------+
| ID | FirstName | LastName |
+----+-----------+----------+
| 1 | First | User |
| 2 | Second | User |
| 3 | Third | User |
| 4 | Fourth | User |
| 5 | Fifth | User |
+----+-----------+----------+
one for tickets:
ticket
+----+---------------+
| ID | TicketSubject |
+----+---------------+
| 1 | Ticket #1 |
| 2 | Ticket #2 |
| 3 | Ticket #3 |
| 4 | Ticket #4 |
+----+---------------+
and one to assign users to tickets to action (can be more than one user per ticket):
ticket_assigned
+----+----------+--------+
| ID | TicketID | UserID |
+----+----------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 3 | 5 |
| 5 | 3 | 3 |
+----+----------+--------+
I'm trying to create a summary to show each user, and how many tickets they have assigned to them, example:
+------------+-------+
| Name | Count |
+------------+-------+
| First | 2 |
| Second | 1 |
| Third | 1 |
| Fourth | 0 |
| Fifth | 1 |
| Unassigned | 2 |
+------------+-------+
Note that the last entry is "unassigned", this is the number of records in the ticket table that DONT appear in the ticket_assigned table (thus being, unassigned). Also further note that user "Fourth" is zero, in that that user has no records in the ticket_assigned table.
Here is the current MySQL query I am using:
SELECT
CASE
WHEN users.FirstName IS NULL
THEN 'Unassigned'
ELSE users.FirstName
END as 'UserName',
COUNT(*) as 'TicketCount'
FROM tickets
LEFT OUTER JOIN ticket_assigned ON tickets.ticket_id = ticket_assigned.ticket_id
LEFT OUTER JOIN users ON ticket_assigned.user_id = users.user_id
GROUP BY ticket_assigned.user_id
ORDER BY UserName;
Problem with this is that it's not showing any of the users that don't feature in the ticket_assigned table, I'm essentially getting this:
+------------+-------+
| Name | Count |
+------------+-------+
| First | 2 |
| Second | 1 |
| Third | 1 |
| Fifth | 1 |
| Unassigned | 2 |
+------------+-------+
Is anyone able to assist and tell me how I can modify my query to include users that have no records in the ticket_assigned table? Thanks in advance!
Use a LEFT JOIN with a subquery to aggregate tickets:
SELECT t1.FirstName,
COALESCE(t2.ticket_count, 0) AS num_tickets
FROM users t1
LEFT JOIN
(
SELECT UserID, COUNT(*) AS ticket_count
FROM ticket_assigned
GROUP BY UserID
) t2
ON t1.ID = t2.UserID
UNION ALL
SELECT 'Unassigned', COUNT(*)
FROM tickets t
WHERE NOT EXISTS (SELECT 1 FROM tickets_assigned ta
WHERE ta.ticketId = t.id)
In MySQL, I think you need a left join and union all:
select u.id, u.firstname, count(ta.userId) as num_tickets
from users u left join
tickets_assigned ta
on ta.userId = u.id
group by u.id, u.firstname
union all
select NULL, 'Unassigned', count(*)
from tickets t
where not exists (select 1
from tickets_assigned
where ta.ticketId = t.id
);
I included the u.id in the aggregations. I'm uncomfortable just aggregating (and reporting) by first name, because different people frequently have the same first name, even in a relatively small group.
SELECT
u2.Firstname, IFNULL(tmp.count, 0) AS count
FROM users u2
LEFT JOIN (
SELECT u.id, u.Firstname, COUNT(1) as count
FROM ticket_assigned ta
LEFT JOIN ticket t ON t.id = ta.ticketID
LEFT JOIN users u ON u.id = ta.userID
GROUP BY u.id
) tmp ON tmp.id = u2.id
UNION
SELECT
'Unassigned', count(1) AS count
FROM ticket
WHERE id NOT IN (SELECT ticketid FROM ticket_assigned)

Join on same column name

Hello there I want to get data from two tables that share same column name. My table structure are
Table patients
---------------------------------------
| id | affiliate_id | somecolumn |
---------------------------------------
| 1 | 8 | abc |
---------------------------------------
| 2 | 8 | abc |
---------------------------------------
| 3 | 9 | abc |
---------------------------------------
Table Leads
---------------------------------------
| id | affiliate_id | someothern |
---------------------------------------
| 1 | 8 | xyz |
---------------------------------------
| 2 | 8 | xyz |
---------------------------------------
| 3 | 3 | xyz |
---------------------------------------
Now my requirement was to get COUNT(ID) from both tables in a single query. I want result like
----------------------------------------------------
| affiliate_id | total_patients | total_leads |
----------------------------------------------------
| 8 | 2 | 2 |
----------------------------------------------------
| 9 | 1 | 0 |
----------------------------------------------------
| 3 | 0 | 1 |
----------------------------------------------------
I wrote following query
SELECT `p`.`affiliate_id`, COUNT(p.id) AS `total_patients`,
COUNT(cpl.id) AS `total_leads`
FROM `patients` AS `p`
INNER JOIN `leads` AS `cpl` ON p.affiliate_id =cpl.affiliate_id
GROUP BY `p`.`affiliate_id`
But I am not getting result . This query results giving only one affiliate with same number of total_patients and total_leads
The problem is that you need to get a list of the distinct affiliate_id first and then join to your other tables to get the result:
select a.affiliate_id,
count(distinct p.id) total_patients,
count(distinct l.id) total_leads
from
(
select affiliate_id
from patients
union
select affiliate_id
from leads
) a
left join patients p
on a.affiliate_id = p.affiliate_id
left join leads l
on a.affiliate_id = l.affiliate_id
group by a.affiliate_id;
See SQL Fiddle with Demo
Two ways:
Select l.affiliate_id ,
count(distinct p.id) patientCount,
count(distinct l.id) LeadCOunt
From patients p Join leads l
On l.affiliate_id = p.Affiliate_id
Group By l.affiliate_id
or, (assuming affiliates are in their own table somewhere)
Select Affiliate_id,
(Select Count(*) From Patients
Where Affiliate_id = a.Affiliate_id) patientCount,
(Select Count(*) From Leads
Where Affiliate_id = a.Affiliate_id) LeadCount
From affiliates a