MySQL Query get the last record then join - mysql

Lets say I have a table like this:
table account
id | username
1 | myuser123
2 | secretuser
table subscription
id | username | subsStartDate | subsEndDate
1 | myuser123 | 2017-01-19 | 2017-02-19
2 | secretuser| 2017-01-19 | 2017-02-19
3 | myuser123 | 2017-02-19 | 2017-03-19
4 | secretuser| 2017-02-19 | 2017-03-19
How can I get the latest subsEndDate of each user in table account.
I am looking for an output similar to this:
output looking for
id | username | subsStartDate | subsEndDate
3 | myuser123 | 2017-02-19 | 2017-03-19
4 | secretuser| 2017-02-19 | 2017-03-19

To get the row with latest subsEndDate per user you can use following query
select a.*
from subscription a
left join subscription b on a.username = b.username
and a.subsEndDate < b.subsEndDate
where b.id is null
Demo

SELECT a.*
FROM subscription a
JOIN
( SELECT username
, MAX(subsenddate) subsenddate
FROM subscription
GROUP
BY username
) b
ON b.username = a.username
AND b.subsenddate = a.subsenddate;

A variant for the case where order id isn't equals order max_subsEndDate
SELECT s.*
FROM subscription s
JOIN
(
SELECT username,MAX(subsEndDate) max_subsEndDate
FROM subscription
GROUP BY username
) l
ON s.username=l.username AND s.subsEndDate=l.max_subsEndDate
My variant (if order id is equals order max_subsEndDate)
SELECT *
FROM subscription
WHERE id IN(
SELECT MAX(id)
FROM subscription
GROUP BY username
)
A variant with JOIN
SELECT s.*
FROM subscription s
JOIN
(
SELECT MAX(id) max_id
FROM subscription
GROUP BY username
) l
ON s.id=l.max_id

Something like this :
SELECT s.id, s.username, s.subsStartDate, endate as subsEndDate
FROM
subscription s
INNER JOIN
(SELECT id, Max(subsEndDate) as endate
FROM subscription
GROUP BY id) t ON t.id = s.id

Related

how to get count items that not exists for user with SQL

I have table user_item
+----+---------+---------+
| id | user_id | item_id |
+----+---------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
| 5 | 3 | 2 |
+----+---------+---------+
Is it possible to get for each user (except user_id 1) number of items that user_id 1 has and other users don't. The desired output should be:
+---------+-------+
| user_id | count |
+---------+-------+
| 2 | 1 |
| 3 | 2 |
+---------+-------+
Thanks.
Building on Gordon Linoff's answer, here's my take:
select
u.id,
count(*) - count(ui.item_id) cnt
from
users u
join user_items ui1 on ui.id=1
left join user_items ui on ui.user_id=u.id and ui.item_id=ui1.item_id
where
u.id <>1
group by
u.id
We start by taking each user except the one with id=1. Then we multiply each row by each item for user with id=1. Then to each of the resulting rows we try to join the row for the same item of the other user. Then we group them together and count. The total count(*) will always be the number of items that user with id=1 has. The count(ui.item_id) will be the count of items that both users have overlapping. And the difference is the count of items that user with id=1 has that the other user doesn't have.
Hmmmm . . . This is tricky. Let's start by getting the count that match user 1. Assuming user/item pairs are not duplicated:
select ui.user_id, count(ui1.item_id) as match_user_1
from user_items ui left join
user_items ui1
on ui1.item_id = ui.item_id and
ui1.user_id = 1
group by ui.user_id;
Now, let's subtract from the total number of items that the user has:
select ui.user_id, count(*) - count(ui1.item_id) as not_match_user_1
from user_items ui left join
user_items ui1
on ui1.item_id = ui.item_id and
ui1.user_id = 1
group by ui.user_id;
EDIT:
For the reverse, it is pretty much the same idea, but you need to subtract the matches from the total for user 1:
select ui.user_id, count(ui1.item_id) as match_user_1,
uuix.cnt - count(ui1.item_id) as not_match_user_1
from user_items ui cross join
(select count(*) as cnt
from user_items
where user_id = 1
) ui1x left join
user_items ui1
on ui1.item_id = ui.item_id and
ui1.user_id = 1
group by ui.user_id;
Creating the table and populating the table with sample data:
CREATE TABLE user_item
(
id int PRIMARY KEY,
user_id int,
item_id int
);
INSERT INTO user_item VALUES (1,1,1),(2,1,3),(3,2,1),(4,2,2),(5,3,2);
The below query displays the number of items of user_id 1 that other users don't have.
SELECT ui.user_id, (select count(item_id) - count(ui1.item_id) from user_item where user_id = 1) as count
FROM user_item UI
LEFT JOIN user_item ui1 ON ui1.item_id = ui.item_id AND ui1.user_id = 1
WHERE ui.user_id <> 1
GROUP BY ui.user_id
ORDER BY ui.user_id;
Output:
+---------+-------+
| user_id | count |
+---------+-------+
| 2 | 1 |
+---------+-------+
| 3 | 2 |
+---------+-------+
you can try this too..
select u2.user_id, (T.counter - COALESCE(SUM(u1.item_id), 0))counter
From user_item u2
LEFT OUTER JOIN (
SELECT user_id, item_id
FROM user_item
WHERE user_id=1
)u1 ON u2.item_id=u1.item_id
LEFT OUTER JOIN (
SELECT user_id, COUNT(1)counter FROM user_item where user_id=1 group by user_id
)T ON u2.user_id != T.user_id
where u2.user_id!=1
Group by u2.user_id,T.counter

SUM two columns of two different tables in one result based group by id of another table

I have 3 tables:
Users:
id | account_name
-------------------|----------------------|
18 | panic |
Deposits:
id | user_id | amount
-------------------|---------------------------|
1 | 18 | 100
2 | 18 | 100
Withdrawals:
id | user_id | amount
------------------------|--------------------------------|
1 | 18 | 200
2 | 18 | 200
and i'm trying to get a result like:
id | totalDeposits | totalWithdraws
------------------------|---------------------------|
18 | 200 | 400
Now when i try to get the totals for some reason they are cross adding themselves up, of course if there are no rows it should return 0.
SELECT t0.id,IFNULL(SUM(t1.amount),0) AS totalWithdrawals,
IFNULL(SUM(t2.amount),0) AS totalDeposits
FROM users t0
LEFT OUTER JOIN withdrawals t1 ON (t0.id = t1.user_id)
LEFT OUTER JOIN deposits t2 ON (t0.id = t2.user_id)
GROUP BY t0.id
Any idea how to do this cross join or where am i summing them wrong?
Try this-
SELECT A.id,
(SELECT SUM(amount) FROM Deposits WHERE user_id = A.id) totalDeposits,
(SELECT SUM(amount) FROM Withdrawals WHERE user_id = A.id) totalWithdraws
FROM users A
WHERE A.id = 18 -- WHERE can be removed to get all users details
You can try something along the lines of
SELECT u.id,
COALESCE(d.amount, 0) totalDeposits,
COALESCE(w.amount, 0) totalWithdrawals
FROM users u
LEFT JOIN (
SELECT user_id, SUM(amount) amount
FROM deposits
GROUP BY user_id
) d ON u.id = d.user_id
LEFT JOIN (
SELECT user_id, SUM(amount) amount
FROM withdrawals
GROUP BY user_id
) w ON u.id = w.user_id
SQLFiddle
Result:
| id | totalDeposits | totalWithdrawals |
|----|---------------|------------------|
| 18 | 200 | 400 |
The problem is that you are generating a Cartesian product. One solution is to aggregate first. Another method is to use UNION ALL and GROUP BY. I would structure this as:
SELECT u.id,
SUM(deposit) as deposits,
SUM(withdrawal) as withdrawal
FROM users u LEFT JOIN
((SELECT d.user_id, d.amount as deposit, 0 as withdrawal
FROM deposits d
) UNION ALL
(SELECT w.user_id, 0, w.amount
FROM withdrawals w
)
) dw
ON u.id = dw.user_id
GROUP BY u.id;

How to write this MYSQL Query with count

So i have the following table:
userid | name | referralcode
When users register on the website they put the referralcode of someone else (the referral code is the same number as the userid of someone else)
so im looking for a sql query that will output something like this
20 (this means 20 users have this userid on their referral code) , Gerardo Bastidas, Valencia
10 , Juan Bastidas, Valencia
I want to get all info on user. its all located in the same table.
Try this query:
SELECT yt1.*, COALESCE(yt2.referral_count, 0)
FROM yourtable yt1 LEFT JOIN
(
SELECT t1.userid, COUNT(*) AS referral_count
FROM yourtable t1 INNER JOIN yourtable t2
ON t1.userid = t2.referralcode
GROUP BY t1.userid
) yt2
ON yt1.userid = yt2.userid;
This query does a self-join and will list every user along with the number of referral codes where his userid appears.
This code will do the work in one query. Replace your table name with 'tbName'
Tested and working
SELECT countval, userid, email, address
FROM tbName t1 LEFT JOIN
(
SELECT COUNT(t2.userid) ASs countval, tt.userid AS xx
FROM tbName t2
GROUP BY t2.referralcode
) t3
ON t3.xx = t1.userid
Output:
+-------+-----+------+
| count | uid | name |
+-------+-----+------+
| 3 | 1 | abc |
| 2 | 2 | xyz |
| 5 | 3 | kmn |
+-------+-----+------+

combining 2 select statements with different number of columns, where 1st statement is already using JOIN

I'm trying to combine 2 select statements with different number of columns.
The 1st statement is this:
SELECT s.id, s.date_sent, m.sam_subject, m.sam_msg_id, (SELECT
COUNT(id) FROM tbl_something WHERE schedule_id = s.id) AS
total_recipients FROM tbl_something2 AS s INNER JOIN
tbl_something3 AS m ON s.message_id = m.sam_msg_id ORDER BY
s.date_sent DESC
The 2nd statement:
SELECT * FROM sms_something4 WHERE status = '0' ORDER BY id DESC
the table output for the 1st statement:
id date_sent sam_subject sam_msg_id total_recipients
1 1372880628 e-Newsletter 2 2
output for 2nd:
id | subject | sent | failed | date_sent | data_sent | data_failed | message | sam_uid from | select_members | status | from_no
11 | test | 2 | 0 | 1372881670 | 639176286411,639224588324 | | | | | | 0 | 0
any suggestions on how would i be able to combine these two statements?
my target output is
id | subject | sent | failed | date_sent | sam_subject | total_recipients | date_sent for email
sam_msg_id can be ignored.
Thank you.
here is basic that you need to have .. you might have to trouble shoot. add column as you need.
SELECT *
FROM (
SELECT s.id, s.date_sent, m.sam_subject, m.sam_msg_id, (SELECT COUNT(id)
FROM tbl_something
WHERE schedule_id = s.id) AS total_recipients
FROM tbl_something2 AS s
INNER JOIN tbl_something3 AS m
ON s.message_id = m.sam_msg_id
) as tbl
INNER JOIN (SELECT * FROM sms_something4 WHERE status = '0') as tbl2
ON tbl2.subject = tbl.sam_subject
and tbl.date_sent=tbl2.date_sent
and tbl.total_Recipients = tbl2.sent+ tbl2.failed
ORDER BY tbl.date_sent DESC
As AJP said you can just do this:
SELECT s.id, a.subject,a.sent, s.date_sent, m.sam_subject,
(SELECT COUNT(id) FROM tbl_something WHERE schedule_id = s.id) AS total_recipients
FROM tbl_something2 AS s
INNER JOIN tbl_something3 AS m ON s.message_id = m.sam_msg_id
INNER JOIN
(
SELECT * FROM sms_something4 WHERE status = '0' ORDER BY id DESC
) a on a.subject = m.sam_subject and a.date_sent = s.date_sent
ORDER BY
s.date_sent DESC

mysql select a value within a select

I have an implementation messages system.
My problem is, I would like to know whether a user already has a thread with another user and if so what is the mid
I have a messages_recips table which look like this
---------------------------
| mid | seq | uid | status|
|--------------------------
| 4 | 1 | 1 | A |
| 4 | 1 | 2 | A |
---------------------------
if user id 1 having a thread with user id 2 I hold 2 rows with same mid.
I know I can create 2 sqls to achieve what I'm asking for, but I'm trying to do it in 1 sql.
As noted by Waqar Janjua, the key to this is a self-join query:
SELECT m1.mid
FROM messages_recips AS m1
JOIN messages_recips AS m2 ON m1.mid = m2.mid
WHERE m1.uid = 1
AND m2.uid = 2
I think you have to write a self-join query:
Select u.uid, u1.uid from tablename u
INNER JOIN tablename u1 on u.mid = u1.mid
You will get all the users who have the same mid.
In order to get only user1 and user2 records you have to place a where clause at the end of the query lik this.
Select u.uid, u1.uid from tablename u
INNER JOIN tablename u1 on u.mid = u1.mid
Where ( u.uid In ( 1,2 ) OR u1.uid In ( 1,2 ) ) ;