Select users that have more received photos than sent photos - mysql

I'm struggling with this SQL query. Say I have these two tables
**USERS**
+----+-------+
| id | name |
+----+-------+
| 1 | james |
| 2 | tom |
| 3 | kate |
+----+-------+
**PHOTOS**
+-----------+-----------+---------+
| name | sent_from | sent_to |
+-----------+-----------+---------+
| beach.jpg | 1 | 2 |
| trees.jpg | 3 | 1 |
| earth.jpg | 2 | 1 |
+-----------+-----------+---------+
How could I get, using one SQL query, all the users that have more sent_to associated with their id than sent_from ?

I think of this as aggregating the data twice and then doing the comparison:
select sf.sent_from
from (select sent_from, count(*) as numsent
from photos
group by sent_from
) sf left outer join
(select sent_to, count(*) as numrecv
from photos
group by sent_to
) st
on sf.sent_from, st.sent_to
where numsent > numrecv;
If you want user information, then join that in.
An alternative way restructures the data first and then does the aggregation:
select who
from (select sent_from as who, 1 as sent_from, 0 as sent_to
from photos
union all
select sent_to as who, 0, 1
from photos
) p
group by who
having sum(sent_from) > sum(sent_to);

I think here is something that might help you:
SELECT * FROM (
SELECT `id`, `name`,
IFNULL((SELECT count(*) FROM `photos` WHERE `sent_from` = `users`.`id`),0) AS `sent_from_count`,
IFNULL((SELECT count(*) FROM `photos` WHERE `sent_t`o = `users`.`id`),0) AS `sent_to_count`
FROM `users`) AS `t1`
WHERE `t1`.`sent_to_count` > `t1`.`sent_to_count`

Related

sql many-to-many query result only if matches only and exactly all items in array

I have three tables on a mysql database:
| pieces | line_up | instrument |
--------------------------------------------
| id_piece | piece_id | id_instrument |
| title | instrument_id | instrument |
now what I'm trying to achieve is: I'd like to query those pieces whose line_up is made up by exactly the instruments given by a list, not one less not one more.
This sql query reduces the result to those piece who are only played by the 2 instruments, but it includes the solos
SELECT id_piece, title FROM pieces WHERE
(SELECT COUNT(*) FROM line_up WHERE line_up.piece_id = pieces.id_piece)
=
(SELECT COUNT(*) FROM line_up
INNER JOIN instruments ON instruments.id_instrument = line_up.instrument_id
WHERE line_up.piece_id = pieces.id_piece
AND instruments.instrument IN ('guitar', 'drums'));
For example with these tables:
| pieces | | line_up | | instruments |
----------------------- --------------------------- ------------------------------
| id_piece | title | | piece_id | instrument_id | | id_instrument | instrument |
----------------------- ---------------------------- ------------------------------
| 1 | hello | | 1 | 1 | | 1 | guitar |
| 2 | goodbye | | 1 | 2 | | 2 | drums |
| 3 | goodnight | | 2 | 1 | ------------------------------
------------------------ | 3 | 2 |
----------------------------
the only actual piece for both guitar and drums, hence the result of my query, should be 1 | hello.
Any suggestions? Thank you!
You could use aggregation like so:
select p.id_piece, p.title
from pieces as p
inner join line_up as l on l.piece_id = p.id_piece
inner join instruments as i on l.instrument_id = i.id_instrument
group by p.id_piece
having sum(i.instrument in ('guitar', 'drums')) = 2
and sum(i.instrument not in ('guitar', 'drums')) = 0
If you don't have too many instruments to search for, an alternative is string aggregation in the having clause:
having group_concat(i.instrument order by i.instrument) = 'drums,guitar'
This second expression requires that you give the query an alphabetically-ordered list of instruments.
Another approach. Match by instrument combination.
SELECT title
FROM pieces
WHERE id_piece IN (
SELECT piece_id
FROM line_up
WHERE instrument_id IN (
SELECT id_instrument
FROM instruments
WHERE instrument IN ('Guitar', 'Drums')
)
GROUP BY piece_id
HAVING group_concat(instrument_id order by instrument_id separator ',') = (
SELECT group_concat(id_instrument order by id_instrument separator ',')
FROM instruments
WHERE instrument IN ('Guitar', 'Drums')
)
);
You need to count the number of rows that were filtered with instruments and compare it with the total number of instruments and number of instruments you've selected.
db<>fiddle
with pieces as (
select 1 as id_piece, 'hello' as title union all
select 2 as id_piece, 'goodbye' union all
select 3 as id_piece, 'goodnight'
)
, line_up as (
select 1 as piece_id, 1 as instrument_id union all
select 1 as piece_id, 2 as instrument_id union all
select 2 as piece_id, 1 as instrument_id union all
select 3 as piece_id, 2 as instrument_id
)
, instruments as (
select 1 as id_instrument, 'guitar' as instrument union all
select 2 as id_instrument, 'drums' as instrument
)
select p.id_piece, p.title
from pieces as p
join line_up as l
on l.piece_id = p.id_piece
join instruments as i
on l.instrument_id = i.id_instrument
group by p.id_piece, p.title
having sum(case when i.instrument in ('guitar', 'drums') then 1 else 0 end) = count(1)
and count(1) = (
/*To select only valid instruments*/
select count(1)
from instruments as f
where f.instrument in ('guitar', 'drums')
)
-----------+--------
| id_piece | title |
|----------+-------|
| 1 | hello |
-----------+--------

How To Select the total sum of an entity that appears in two tables

I Have two types of tractions tables where a user can be present in either one or both tables my Goal is to get a sum total for each user. the tables are as follows.
users
------------+----------+
| user_id | Name |
+-----------+----------+
| 1 | John |
------------+----------+
| 2 | Wells |
------------+----------+
shop1
------------+----------+--------------+
| trans_id | user_id | amount_spent |
+-----------+----------+--------------+
| 1 | 1 | 20.00 |
------------+----------+--------------+
| 2 | 1 | 10.00 |
------------+----------+--------------+
shop2
------------+----------+--------------+
| trans_id | user_id | amount_spent |
+-----------+----------+--------------+
| 1 | 2 | 20.05 |
------------+----------+--------------+
Expected Result after Summing
------------+-------------+
| user | Total Spent |
+-----------+-------------+
| John | 30.00 |
------------+-------------+
| Wells | 20.05 |
------------+-------------+
Use union all and group by:
select user_id, sum(amount_spent)
from ((select user_id, amount_spent from shop1) union all
(select user_id, amount_spent from shop2)
) s
group by user_id;
That said, you have a poor data model. In general, tables with the same columns are a very bad idea. You should have a single table for all shops with an additional column for the shop.
If you want the name, you need to join:
select u.name, sum(amount_spent)
from users u join
((select user_id, amount_spent from shop1) union all
(select user_id, amount_spent from shop2)
) s
using (user_id)
group by user_id, u.name;
You can use union all and aggregation:
select user_id, sum(amount_spent) total_spent
from (
select user_id, amount_spent from shop1
union all
select user_id, amount_spent from shop2
) t
group by user_id
select user_id, sum(amount_spent)
from
(shop1
UNION ALL
ship2)
group by user_id

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

Count multiple columns with same WHERE condition

I have a users table with columns: user_id, teacher_id1, teacher_id2, teacher_id3
and
teachers table with id
Each user can have the same id's for teacher_id1, teacher_id2, teacher_id3
I would like to count how many users have same teacher.
User table
+----------------------------------------+
| user_Id teacher_id1 teacher_id2 teacher_id3 |
+----------------------------------------+
| 1 1 1 1 |
| 2 2 1 3 |
| 3 2 3 3 |
| 4 2 2 2 |
+----------------------------------------+
Teacher table
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
Count for $id1 is: 2
Count for $id2 is: 3
Count for $id3 is: 2
I tried something like this, but it is not correct!
SELECT COUNT(*) FROM users WHERE concat_ws('',teacher_id1 OR teacher_id2
OR teacher_id3) like '{$id}' ";
You have data in three different columns. You need to combine it into one column, to get the distinct counts that you want. For this, you can use union all. Then the count is simply count(distinct):
select teacher_id, COUNT(distinct USER_ID)
from ((select user_id, teacher_id1 as teacher_id
from t
) union all
(select user_id, teacher_id2
from t
) union all
(select user_id, teacher_id3
from t
)
) s
group by teacher_id;
Try this query
select b.id, count(*)
from
tbl1 a
inner join
tbl2 b
on b.id = teacher_id1 or b.id = teacher_id2 or b.id = teacher_id3
group by b.id
SQL FIDDLE:
| ID | COUNT(*) |
-----------------
| 1 | 2 |
| 2 | 3 |
| 3 | 2 |