How to join on _first_ record? - mysql

With my limited knowledge of complex mysql queries I'm trying to retrieve some information from a database.
The story is this; users get an invite to come to our company. On the basis of this one invite, users can get multiple notifications and multiple appointments. I've got three relevant tables:
invites
-------
| id | name | created |
-----------------------------
| 1 | someth1 | 2018-02-03 |
| 2 | someth2 | 2018-02-03 |
| 3 | someth3 | 2018-02-03 |
notifications
-------------
| id | inv_id | message |
--------------------------
| 1 | 101 | hello |
| 2 | 287 | hi |
| 3 | 827 | hey |
appointments
------------
| id | inv_id | start_at |
-----------------------------
| 1 | 101 | 2018-02-03 |
| 2 | 287 | 2018-02-08 |
| 3 | 827 | 2018-02-15 |
I currently have a query, which shows a list of notifications send to users, for all invites done after 1 feb 2018, and which have an appointment no later than '2018-03-10'.
SELECT id, inv_id, message
FROM notifications
WHERE inv_id IN (
SELECT id
FROM invites as invite
WHERE created > '2018-02-01'
AND id IN (
SELECT inv_id
FROM appointments
WHERE invite.id = inv_id
AND start_at < '2018-03-10'
)
)
ORDER BY inv_id ASC;
The result looks something like this:
| id | inv_id | message |
--------------------------
| 1 | 101 | hello |
| 2 | 287 | hi |
| 3 | 827 | hey |
I now want to add the start_at of the first appointment for these notifications
| id | inv_id | message | start_at |
---------------------------------------
| 1 | 101 | hello | 2018-02-03 |
| 2 | 287 | hi | 2018-02-08 |
| 3 | 827 | hey | 2018-02-15 |
But from here I'm kinda lost in how I should do that.
Does anybody know how I can add the start_at of the first appointment which corresponds to the invite for the respective notification? So it should show the start_at of the first appointment for the invite of the notification inv_id?

Try this:
SELECT id, N.inv_id, message,A.start_at
FROM notifications N
JOIN(
SELECT inv_id,MIN(start_at) start_at
FROM appointments
WHERE inv_id IN (
SELECT id
FROM invites as invite
WHERE created > '2018-02-01'
AND id IN (
SELECT inv_id
FROM appointments
WHERE invite.id = inv_id
AND start_at < '2018-03-10'
)
)
GROUP BY inv_id
)A ON N.inv_id = A.inv_id
WHERE inv_id IN (
SELECT id
FROM invites as invite
WHERE created > '2018-02-01'
AND id IN (
SELECT inv_id
FROM appointments
WHERE invite.id = inv_id
AND start_at < '2018-03-10'
)
)
ORDER BY inv_id ASC;

You don't need a lot of subqueries to get the desired result. Just good use of join operations and just one subquery to get the first appointment so here is the query you need:
select n.id, n.inv_id, n.message, a.startat as start_at
from invites i
inner join notifications n
on i.id = n.inv_id
inner join (select inv_id, min(start_at) startat
from appointments
where start_at < '2018-03-10'
group by inv_id) a
on n.inv_id = a.inv_id
where i.created > '2018-02-01';
Notice that for your current sample data your desired result is impossible since in your query you use the notifications.inv_id IN invites.id and there is no equivalence (1,2,3 are different from 101, 287, 827).
Because of that I created a SQLFiddle to show the working query but with those ids "101, 287, 827" as invites. Here it is: http://sqlfiddle.com/#!9/2d5175/2
Also, if you want the notifications even if there is no appointment for it change the join operation between notifications and the subquery from inner join to left join

Related

Displaying Latest Record and Join Multiple table

how do I join multiple tables and displaying each users sold item, display the latest record who sold the items
I need output like this
Sold by:
"jon" item "#1" "book" with a price of "1000"
tried :
SELECT uid , users.name AS uname, transact.transaction_id AS transacted INNER JOIN users on transaction_table.c_id=c_table.c_id
User table
--------------------------
| uid | name | timezone |
--------------------------
| 1 | jon | +1 gmt |
| 2 | mix | +2 gmt |
| 3 | vic | +1 gmt |
--------------------------
transaction table
-------------------------------
| transaction_id | uid | c_id |
-------------------------------
| dafsf22sdfssgs | 2 | 1 |
| 23425asda3afaa | 1 | 1 |
-------------------------------
C-table
------------------------
| c_id | c_name | price |
------------------------
| 1 | book | 1000 |
| 2 | comic | 100 |
| 3 | notes | 10 |
-------------------------
If you want to group by item name and get the total
select u.name,count(*) as count, c.c_name, c.price*count(*) as totalPrice from user u
inner join transaction t on u.uid=t.uid
inner join ctable c on c.c_id=t.c_id
group by c.c_name
If you want to query all the transactions
select u.name, c.c_name, c.price from user u
inner join transaction t on u.uid=t.uid
inner join ctable c on c.c_id=t.c_id
If you just want to return the last transaction info
select u.name, c.c_name, c.price from user u
inner join transaction t on u.uid=t.uid
inner join ctable c on c.c_id=t.c_id
order by t.transaction_id desc limit 1
And one more thing. It is a much much more better practice if your field names are consistent.

When I use "WHERE user_id in ( sub query )" generate syntax error

I have a users table used below.
Users have referal_code, refered_by columns.Users has following data.
+----+--------------+------------+
| id | referal_code | refered_by |
+----+--------------+------------+
| 1 | abc | null |
| 2 | xxx | abc |
+----+--------------+------------+
I have Reviews table in which I store users reviewe by other users.
It does have user_id, evaluation columns.
+----+---------+------------+
| id | user_id | evaluation |
+----+---------+------------+
| 28 | 2 | 4 |
| 32 | 2 | 6 |
+----+---------+------------+
I'm trying to count users referred by each user have an average evaluation of 3 or more.
SELECT users.*, COUNT(
SELECT reviews.user_id FROM reviews
WHERE reviews.user_id IN(
SELECT A2.id FROM users as A2 WHERE A2.refered_by = users.referal_code
)
HAVING AVG(evaluation) >= 3) as total_3_estrelas
FROM users
WHERE 1
I have a syntax error #1064 on: WHERE user_id IN
The result I expect:
+----+--------------+------------+------------------+
| id | referal_code | refered_by | total_3_estrelas |
+----+--------------+------------+------------------+
| 1 | abc | null | 1 |
| 2 | xxx | abc | 0 |
+----+--------------+------------+------------------+
Look at this if it helps:
SELECT A.ID, A.REFERAL_CODE, A.REFERED_BY, COALESCE(TOTAL_3_ESTRELAS,0) AS TOTAL_3_ESTRELAS
FROM USERS A
LEFT JOIN
(SELECT REFERED_BY, COUNT(*) AS TOTAL_3_ESTRELAS
FROM USERS U
INNER JOIN (SELECT USER_ID, AVG(EVALUATION)
FROM REVIEWS
GROUP BY USER_ID
HAVING AVG(EVALUATION)>=3) R
ON U.ID=R.USER_ID
GROUP BY REFERED_BY) T
ON A.REFERAL_CODE=T.REFERED_BY;
From the deeper nested condition, first I calculated the average evaluation for each user_id on REVIEWS throwing away USER_ID with avg below 3, then I made the inner join with USERS and I grouped by REFERED_BY to obtain the count desired. Finally I did a left join to obtain the output in the form you expect.

How to sum the value and retrieve the result using if condition- using MySQL?

I want to sum every time and check with if condition. If condition matches I want the get the created date of the final matched row.
+------------+----------------------------------+------------+--------+
| id | EMAIL | created | Amt |
+------------+----------------------------------+------------+--------+
| 61 | abc#gmail.com | 1514909390 | 57.00 |
| 25 | xyz#gmail.com | 1515534837 | 360.00 |
| 36 | zccc#abv.com | 1515645391 | 240.00 |
| 22 | vv#aa.com | 1516419622 | 320.40 |
| 48 | aa#xyz.com | 1516706121 | 240.00 |
+------------+----------------------------------+------------+--------+
I try this query but I'm not getting the solution...
select
sum(a.amount) as amt,
if(sum(a.amount)>8000,slp.sal_time,0) as Amt_exceed_date
from employee a
join emp_user u
on a.cmp_id=u.user_id
left join emp_sal as slp
on slp.user_id=a.cmp_id
where
order by slp.sal_time;
Somewhat like row wise sum
select e.*,(
select sum(Amt)
from employee
where created <= e.created
) row_wise_sum
from employee e
having row_wise_sum < 800
order by e.created
desc limit 1
Demo

MySQL Joining Tables While Counting & Grouping

I want to join 2 tables:
source_table
----------------------------------
| source_id label |
|----------------------------------|
| 1 Contact Form |
| 2 E-Mail |
| 3 Inbound Call |
| 4 Referral |
----------------------------------
related_table
---------------------------------------
| id created_at source |
|---------------------------------------|
| 1 2013-12-26 2 |
| 2 2013-12-26 2 |
| 3 2013-12-26 4 |
| 4 2013-12-25 1 |
| 5 2013-12-18 2 |
| 6 2013-12-16 4 |
| 7 2013-11-30 2 |
---------------------------------------
So that it looks like this:
---------------------------------------
| created_at source amount |
|---------------------------------------|
| 2013-12-26 E-Mail 2 |
| 2013-12-26 Referral 1 |
| 2013-12-25 Contact Form 1 |
| 2013-12-18 E-Mail 1 |
| 2013-12-16 Referral 1 |
---------------------------------------
I want to count the occurrences of each source in related_table grouped by the source for each date in the range.
But I'm not sure how to write the query.
Here's what I have so far:
SELECT DISTINCT
source_table.source_id,
source_table.label AS source,
related_table.created_at,
COUNT(*) AS amount
FROM source_table
INNER JOIN related_table
ON related_table.source=source_table.source_id AND
related_table.created_at>='2013-12-01' AND
related_table.created_at<='2013-12-31'
GROUP BY `source`
ORDER BY `created_at` ASC
I'm not very good with SQL, so the above query might be far off from what I need to have. All I know is that it doesn't work as expected.
My implementation:
select created_at, s.label, amount
from
(
select count(r.Source) as amount, r.source, r.created_at
from related_table r
group by r.source, r.created_at) a inner join source_table s
on a.source = s.source_id
where created_at between '2013-12-01' and '2013-12-31'
order by amount desc, created_at desc
http://sqlfiddle.com/#!2/841bd/2
adjusted demo to your example...
SELECT
created_at
,label as source
,COUNT(*) AS amount
FROM source_table
INNER JOIN related_table
ON source_table.source_id = related_table.source
GROUP BY label, created_at
ORDER BY created_at DESC

Mysql include column with no rows returned for specific dates

I would like to ask a quick question regarding a mysql query.
I have a table named trans :
+----+---------------------+------+-------+----------+----------+
| ID | Date | User | PCNum | Customer | trans_In |
+----+---------------------+------+-------+----------+----------+
| 8 | 2013-01-23 16:24:10 | test | PC2 | George | 10 |
| 9 | 2013-01-23 16:27:22 | test | PC2 | Nick | 0 |
| 10 | 2013-01-24 16:28:48 | test | PC2 | Ted | 10 |
| 11 | 2013-01-25 16:36:40 | test | PC2 | Danny | 10 |
+----+---------------------+------+-------+----------+----------+
and another named customers :
+----+---------+-----------+
| ID | Name | Surname |
+----+---------+-----------+
| 1 | George | |
| 2 | Nick | |
| 3 | Ted | |
| 4 | Danny | |
| 5 | Alex | |
| 6 | Mike | |
.
.
.
.
+----+---------+-----------+
I want to view the sum of trans_in column for specific customers in a date range BUT ALSO include in the result set, those customers that haven't got any records in the selected date range. Their sum of trans_in could appear as NULL or 0 it doesn't matter...
I have the following query :
SELECT
`Date`,
Customer,
SUM(trans_in) AS 'input'
FROM trans
WHERE Customer IN('George','Nick','Ted','Danny')
AND `Date` >= '2013-01-24'
GROUP BY Customer
ORDER BY input DESC;
But this will only return the sum for 'Ted' and 'Danny' because they only have transactions after the 24th of January...
How can i include all the customers that are inside the WHERE IN (...) function, even those who have no transactions in the selected date range??
I suppose i'll have to join them somehow with the customers table but i cannot figure out how.
Thanks in advance!!
:)
In order to include all records from one table without matching records in another, you have to use a LEFT JOIN.
SELECT
t.`Date`,
c.name,
SUM(t.trans_in) AS 'input'
FROM customers c LEFT JOIN trans t ON (c.name = t.Customer AND t.`Date` >= '2013-01-24')
WHERE c.name IN('George','Nick','Ted','Danny')
GROUP BY c.name
ORDER BY input DESC;
Of course, I would mention that you should be referencing customer by ID, and not by name in your related table. Your current setup leads to information duplication. If the customer changes their name, you now have to update all related records in the trans table instead of just in the customer table.
try this
SELECT
`Date`,
Customer,
SUM(trans_in) AS 'input'
FROM trans
inner join customers
on customers.Name = trans.Customer
WHERE Customer IN('George','Nick','Ted','Danny')
GROUP BY Customer
ORDER BY input DESC;