Mysql - Finding missing dates with multiple join tables - mysql

I need some help writing a mysql query. I need to find all missed reports for a given date range for a specific business id / project id.
Basically, for a given business id, I need to know the name of the project and all dates that a report is either missing or not marked as completed.
I am using the calendar table trick (as described here and here) to find the missing report dates, but I am having problems joining the projects table to find the associated project / business that a report was missed for.
I basically need a result set that will give me data similar to this:
+------------+-----------+--------------+
| project_id | name | missing_date |
+------------+-----------+--------------+
| 1 | Project 1 | 2014-01-01 |
| 1 | Project 1 | 2014-01-03 |
| 1 | Project 1 | 2014-01-04 |
| 1 | Project 1 | 2014-01-07 |
| 1 | Project 1 | 2014-01-09 |
| 2 | Project 2 | 2014-01-02 |
| 2 | Project 2 | 2014-01-03 |
| 2 | Project 2 | 2014-01-04 |
+------------+-----------+--------------+
Here is my schema:
projects table:
+----------------+------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+------------------+------+-----+-------------------+----------------+
| project_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| business_id | int(10) unsigned | NO | MUL | NULL | |
| name | tinytext | YES | | NULL | |
+----------------+------------------+------+-----+-------------------+----------------+
reports table:
+---------------------+------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+------------------+------+-----+-------------------+----------------+
| report_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| project_id | int(10) unsigned | NO | MUL | NULL | |
| report_date | date | NO | MUL | NULL | |
| completed | bit(1) | NO | | b'0' | |
+---------------------+------------------+------+-----+-------------------+----------------+
calendar table:
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| dt | date | NO | PRI | NULL | |
| month_name | varchar(9) | YES | | NULL | |
| day_name | varchar(9) | YES | | NULL | |
| y | smallint(6) | YES | | NULL | |
| q | tinyint(4) | YES | | NULL | |
| m | tinyint(4) | YES | | NULL | |
| d | tinyint(4) | YES | | NULL | |
| dw | tinyint(4) | YES | | NULL | |
| w | tinyint(4) | YES | | NULL | |
| is_weekday | bit(1) | YES | | NULL | |
| is_holiday | bit(1) | YES | | NULL | |
| holiday_desc | varchar(32) | YES | | NULL | |
+--------------+-------------+------+-----+---------+-------+
The following query below works to return a list of incompleted reports, but I still need to fill the gaps in with the dates where there is no report record at all.
select
p.project_id,
p.name,
c.dt as missing_date,
r.completed
from reports r
join projects p on (r.project_id = p.project_id)
right join calendar c on (c.dt = r.report_date)
where c.dt >= '2014-02-01'
and c.dt <= '2014-02-10'
-- and r.report_date is null /** THE RESULT SET IS EMPTY IF I UNCOMMENT THIS **/
and r.completed = false
and c.is_holiday = false
and c.is_weekday = true
and p.business_id = 1001
order by p.project_id, r.report_date, c.dt;
Any help would be much appreciated!

I read the links you have provided and this method of storage for dates is great.
any way in your query you used:
right join calendar c on (c.dt = r.report_date)
but I don't see any report_date in your report table.
I suggest you check it if the problem is because of it ,since apart from that your query seems to work properly.

Ok, I finally got it to work. It's a fairly complicated query that requires an inner join on the projects table but I don't know a better way to do it - I'm a Java guy who relies on hibernate way too much these days to build my queries :). If anyone has a more efficient solution I am all ears!
The final query:
select
p.project_id,
p.name,
c.dt as missing_date,
r.report_date,
r.completed
from calendar c
inner join (
select
p1.project_id,
p1.name
from projects p1
where p1.project_id = 1005
-- where p1.business_id = 1001 /** OR USE THE BUSINESS ID **/
) p on c.dt between '2014-02-01' and '2014-02-28'
left join reports r on r.report_date = c.dt
and r.project_id = p.project_id
and r.completed = false
where (r.report_date is null or r.completed = false)
and c.is_holiday = false
and c.is_weekday = true
order by p.project_id, c.dt;
Produces the correct results:
+------------+--------------+--------------+-------------+-----------+
| project_id | name | missing_date | report_date | completed |
+------------+--------------+--------------+-------------+-----------+
| 1005 | Project 1005 | 2014-02-03 | 2014-02-03 | 0 |
| 1005 | Project 1005 | 2014-02-04 | 2014-02-04 | 0 |
| 1005 | Project 1005 | 2014-02-05 | NULL | NULL |
| 1005 | Project 1005 | 2014-02-06 | 2014-02-06 | 0 |
| 1005 | Project 1005 | 2014-02-07 | NULL | NULL |
| 1005 | Project 1005 | 2014-02-10 | 2014-02-10 | 0 |
| 1005 | Project 1005 | 2014-02-11 | 2014-02-11 | 0 |
| 1005 | Project 1005 | 2014-02-12 | 2014-02-12 | 0 |
| 1005 | Project 1005 | 2014-02-13 | NULL | NULL |
| 1005 | Project 1005 | 2014-02-14 | NULL | NULL |
| 1005 | Project 1005 | 2014-02-18 | NULL | NULL |
| 1005 | Project 1005 | 2014-02-19 | NULL | NULL |
| 1005 | Project 1005 | 2014-02-20 | 2014-02-20 | 0 |
| 1005 | Project 1005 | 2014-02-21 | 2014-02-21 | 0 |
| 1005 | Project 1005 | 2014-02-24 | 2014-02-24 | 0 |
| 1005 | Project 1005 | 2014-02-25 | 2014-02-25 | 0 |
| 1005 | Project 1005 | 2014-02-26 | NULL | NULL |
| 1005 | Project 1005 | 2014-02-27 | NULL | NULL |
| 1005 | Project 1005 | 2014-02-28 | NULL | NULL |
+------------+--------------+--------------+-------------+-----------+
Thanks for the help guys and gals!

Related

Self join with counts on a nested categories table

I have a listing_categories table that has many listing_plans and listings, and many subcategories. I am trying to write a view to show the listing categories with listing_plan count, listings count and subcategory count.
Here is my listing_categories table:
MariaDB [railsapp_development]> describe listing_categories;
+---------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| website_id | int(11) | YES | MUL | NULL | |
| listing_category_id | int(11) | YES | | NULL | |
| name | varchar(255) | YES | | NULL | |
| slug | varchar(255) | YES | | NULL | |
| description | text | YES | | NULL | |
| category_card | text | YES | | NULL | |
| listing_card | text | YES | | NULL | |
| layout | varchar(255) | YES | | NULL | |
| status | int(11) | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
| keywords | varchar(255) | YES | | NULL | |
+---------------------+--------------+------+-----+---------+----------------+
And the view I am trying to write:
create or replace view `listing_category_details` AS
select
cats1.*,
count(l.id) as listing_count,
count(cats2.id) as subcategory_count,
count(lp.id) as plan_count
from
listing_categories as cats1
left join
listings as l on cats1.id = l.listing_category_id
left join
listing_plans as lp on cats1.id = lp.listing_category_id
inner join
listing_categories as cats2 on cats1.listing_category_id = cats2.id
group by
cats1.id
order by
cats1.name asc;
The output is incorrect, it is not showing the right count, as follows:
MariaDB [railsapp_development]> select id, slug, name, listing_count, subcategory_count, plan_count from listing_category_details limit 10;
+----+-------------------+-------------------+---------------+-------------------+------------+
| id | slug | name | listing_count | subcategory_count | plan_count |
+----+-------------------+-------------------+---------------+-------------------+------------+
| 17 | ares | Ares | 22 | 22 | 22 |
| 30 | automotive | Automotive | 16 | 16 | 16 |
| 19 | crist-osinski-inc | Crist-Osinski Inc | 12 | 12 | 12 |
| 29 | esl-cologne | ESL Cologne | 20 | 20 | 20 |
| 18 | executive-office | Executive Office | 22 | 22 | 22 |
| 27 | gfinity-london | GFinity London | 24 | 24 | 24 |
| 25 | hephaestus | Hephaestus | 28 | 28 | 28 |
| 24 | iem-championship | IEM Championship | 14 | 14 | 14 |
| 26 | league-all-stars | League All Stars | 30 | 30 | 30 |
| 21 | machinery | Machinery | 14 | 14 | 14 |
+----+-------------------+-------------------+---------------+-------------------+------------+
In the above example none of the figures are corect, for instance each listing_category has exactly 2 plans.
What am I doing wrong?
Use COUNT(DISTINCT):
select cats1.*,
count(distinct l.id) as listing_count,
count(distinct cats2.id) as subcategory_count,
count(distinct lp.id) as plan_count
from listing_categories cats1 left join
listings l
on cats1.id = l.listing_category_id left join
listing_plans lp
on cats1.id = lp.listing_category_id inner join
listing_categories cats2
on cats1.listing_category_id = cats2.id
group by cats1.id
order by cats1.name asc;
COUNT() just counts the number of non-NULL values. You are joining along a hierarchy, so the rows get multiplied as you descend the hierarchy.

Combine data from three unrelated tables

I need to use a data visualization tool that can only query a single source for a given chart. I have three tables with the data I need to visualize. So, I need to combine them into a single view or output table. Here are the table schemas:
MySQL [bdCaloriesNeeded]> desc activity;
+---------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+----------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | text | YES | | NULL | |
| Gender | text | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| length | text | YES | | NULL | |
| weight | int(11) | YES | | NULL | |
| exercise | int(11) | YES | | NULL | |
| food_consumed | int(11) | YES | | NULL | |
| date | datetime | YES | | NULL | |
+---------------+----------+------+-----+---------+-------+
MySQL [bdCaloriesNeeded]> desc exercise;
+---------------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+---------+------+-----+---------+-------+
| Gender | text | YES | | NULL | |
| Min_Age | int(11) | YES | | NULL | |
| Max_Age | int(11) | YES | | NULL | |
| min_exercise_hours | int(11) | YES | | NULL | |
| med_exercise_hours | int(11) | YES | | NULL | |
| high_exercise_hours | int(11) | YES | | NULL | |
+---------------------+---------+------+-----+---------+-------+
MySQL [bdCaloriesNeeded]> desc food;
+---------------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+---------+------+-----+---------+-------+
| size | text | YES | | NULL | |
| min_pounds | int(11) | YES | | NULL | |
| max_pounds | int(11) | YES | | NULL | |
| min_food_oz_per_day | int(11) | YES | | NULL | |
| max_food_oz_per_day | int(11) | YES | | NULL | |
+---------------------+---------+------+-----+---------+-------+
Here's the actual source data in the above tables:
MySQL [bdCaloriesNeeded]> select * from activity;
+------+----------+--------+------+--------+--------+----------+---------------+---------------------+
| id | name | Gender | age | length | weight | exercise | food_consumed | date |
+------+----------+--------+------+--------+--------+----------+---------------+---------------------+
| 14 | spot | M | 2 | 2'7" | 13 | 5 | 13 | 2017-10-08 00:00:00 |
| 67 | princess | F | 6 | 3'3" | 75 | 3 | 15 | 2017-09-05 00:00:00 |
+------+----------+--------+------+--------+--------+----------+---------------+---------------------+
MySQL [bdCaloriesNeeded]> select * from exercise
+--------+---------+---------+--------------------+--------------------+---------------------+
| Gender | Min_Age | Max_Age | min_exercise_hours | med_exercise_hours | high_exercise_hours |
+--------+---------+---------+--------------------+--------------------+---------------------+
| M | 1 | 2 | 1 | 4 | 6 |
| M | 3 | 7 | 1 | 3 | 4 |
| M | 8 | 15 | 1 | 2 | 2 |
| F | 1 | 2 | 1 | 4 | 6 |
| F | 3 | 7 | 1 | 3 | 5 |
| F | 8 | 15 | 1 | 2 | 2 |
+--------+---------+---------+--------------------+--------------------+---------------------+
MySQL [bdCaloriesNeeded]> select * from food;
+--------+------------+------------+---------------------+---------------------+
| size | min_pounds | max_pounds | min_food_oz_per_day | max_food_oz_per_day |
+--------+------------+------------+---------------------+---------------------+
| small | 1 | 10 | 12 | 18 |
| medium | 11 | 30 | 15 | 30 |
| large | 31 | 100 | 25 | 50 |
+--------+------------+------------+---------------------+---------------------+
Here's the SQL I'm executing:
SELECT activity.id, activity.name, activity.Gender, activity.age, activity.weight, activity.exercise, activity.date, exercise.min_exercise_hours, exercise.high_exercise_hours, food.size, food.min_food_oz_per_day, food.max_food_oz_per_day
from activity, exercise, food
where (
activity.exercise between exercise.min_exercise_hours and exercise.high_exercise_hours
)
and
(
activity.weight between food.min_pounds and food.max_pounds
)
and
(
activity.Gender = exercise.Gender
)
Here's the undesired result I'm getting:
+------+----------+--------+------+--------+----------+---------------------+--------------------+---------------------+--------+---------------------+---------------------+
| id | name | Gender | age | weight | exercise | date | min_exercise_hours | high_exercise_hours | size | min_food_oz_per_day | max_food_oz_per_day |
+------+----------+--------+------+--------+----------+---------------------+--------------------+---------------------+--------+---------------------+---------------------+
| 14 | spot | M | 2 | 13 | 5 | 2017-10-08 00:00:00 | 1 | 6 | medium | 15 | 30 |
| 67 | princess | F | 6 | 75 | 3 | 2017-09-05 00:00:00 | 1 | 6 | large | 25 | 50 |
| 67 | princess | F | 6 | 75 | 3 | 2017-09-05 00:00:00 | 1 | 5 | large | 25 | 50 |
+------+----------+--------+------+--------+----------+---------------------+--------------------+---------------------+--------+---------------------+---------------------+
I'm getting two rows for Princess. I need one row for each dog. The desired result should use Princess's's weight to look up the correct range of food per day, and use her gender and age to look up the correct range of exercise.
I've been banging on this for hours, can't see what doing wrong here.
So interestingly your question says that the tables are Unrelated but they are actually related and this is the whole point of a relational database, to join data based on those relationships.
The issue is that your exercise table is only being joined on exercise hours using the between so princess matches rows 4 and 5 in the exercise table. (the first where clause matches rows 1 and 2 also but the later where clause limits the Gender)
It looks to me like you should also limit the match on the exercise table to age as well as exercise and gender
so add
and (activity.age between exercise.min_age and exercise.max_age)
Also personally i like to use JOIN clauses rather than WHERE - it keeps all the stuff together.
SELECT activity.id,
activity.name,
activity.Gender,
activity.age,
activity.weight,
activity.exercise,
activity.date,
exercise.min_exercise_hours,
exercise.high_exercise_hours,
food.size,
food.min_food_oz_per_day,
food.max_food_oz_per_day
FROM activity
JOIN exercise
ON activity.exercise BETWEEN exercise.min_exercise_hours AND exercise.high_exercise_hours
AND activity.Gender = exercise.Gender
AND activity.age BETWEEN exercise.min_age AND exercise.max_age
JOIN food
ON activity.weight BETWEEN food.min_pounds AND food.max_pounds
Since you are looking for things that may be OUTSIDE of the ranges suggested you may want to consider LEFT JOIN on the exercise and food tables, so that the dogs on the activity table that fall outside of any range will still show up (with NULL values for the missing data for the other table.)
just change the join lines to LEFT JOIN like so:
LEFT JOIN exercise
LEFT JOIN food
See also: What is the difference between "INNER JOIN" and "OUTER JOIN"?

Adding (results of a query) to an existing table

So i have the following problem. I have 2 tables (Users: 100 rows and geo-zipcodes: 450k rows) and I want to update my users table to display the lon and lat if the zipcode is filled in. When it's not filled in i want the lon/lat to display NULL. How do I achieve this?
I do this in MySQL using the standard provided MySQL workbench.
+CURRENT SITUATION----+------+
| id | zipcode | lon | lat |
+----+---------+------+------+
| 88 | 3531EK | NULL | NULL |
| 89 | 5691JN | NULL | NULL |
| 90 | 5701NR | NULL | NULL |
| 91 | 3531EK | NULL | NULL |
| 92 | 5691JN | NULL | NULL |
| 93 | NULL | NULL | NULL |
| 94 | NULL | NULL | NULL |
| 95 | NULL | NULL | NULL |
| 96 | NULL | NULL | NULL |
| 97 | NULL | NULL | NULL |
+----+---------+------+------+
+FUTURE SITUATION------------------+-----------------+
| id | zipcode | lat | lon |
+-----+---------+------------------+-----------------+
| 583 | NULL | NULL | NULL |
| 632 | NULL | NULL | NULL |
| 797 | 4194WD | 51.8724978062918 | 5.2758839816285 |
| 812 | 9723ZT | 53.2067353295688 | 6.5886266741127 |
| 782 | 5617BD | 51.4471593854488 | 5.4566869544855 |
| 799 | NULL | NULL | NULL |
| 800 | 5623ET | 51.4618395108795 | 5.4733910341403 |
| 179 | 5709BN | 51.4752182995384 | 5.7022349534995 |
| 112 | 5701CN | 51.4759330063412 | 5.6780783810570 |
| 90 | 5701NR | 51.4775509176254 | 5.6576320175919 |
+-----+---------+------------------+-----------------+
In MySQL, you can do this with a join:
update users u join
zipcodes z
on u.zipcode = z.zipcode
set u.lat = z.lat, u.lon = z.lon;
However, I don't see why this is necessary. Why not just look up the coordinates using the zip code table when you need it? There might be good reasons -- for instance, you might have other sources of coordinates. But if the zip code is the only source, then there is no need to store the same information in two places.

MySQL query not producing the correct result

Given this:
MariaDB [master]> describe history;
+--------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+----------------+
| historyid | int(10) unsigned | NO | PRI | NULL | auto_increment |
| amount | float | NO | | NULL | |
| subsidy | char(1) | NO | | NULL | |
| last_payment | date | NO | | NULL | |
| amount_paid | float | NO | | NULL | |
| balance | float | NO | | NULL | |
| attend | char(1) | NO | | N | |
| attend_date | date | NO | | NULL | |
| groupid | int(11) unsigned | NO | | NULL | |
| clientid | int(10) unsigned | NO | MUL | NULL | |
| memberid | int(10) unsigned | NO | MUL | NULL | |
+--------------+------------------+------+-----+---------+----------------+
MariaDB [master]> describe participation;
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| partid | int(11) | NO | PRI | NULL | auto_increment |
| notes | varchar(255) | NO | | NULL | |
| groupdate | date | NO | | NULL | |
| clientid | int(10) unsigned | NO | MUL | NULL | |
| memberid | int(10) unsigned | NO | MUL | NULL | |
+-----------+------------------+------+-----+---------+----------------+
MariaDB [master]> select * from participation;
+--------+-----------+------------+----------+----------+
| partid | notes | groupdate | clientid | memberid |
+--------+-----------+------------+----------+----------+
| 194 | test test | 2016-01-26 | 3 | 1 |
+--------+-----------+------------+----------+----------+
How do I write the following query
MariaDB [master]> SELECT attend_date, groupdate, h.clientid, h.memberid
-> FROM history AS h
-> LEFT JOIN participation AS p ON groupdate = attend_date
-> WHERE h.memberid = "1"
-> AND MONTH(attend_date) = "1"
-> AND YEAR(attend_date) = "2016"
-> AND attend_date <> "0000-00-00"
-> ORDER BY attend_date ASC;
+-------------+------------+----------+----------+
| attend_date | groupdate | clientid | memberid |
+-------------+------------+----------+----------+
| 2016-01-26 | 2016-01-26 | 3 | 1 |
| 2016-01-26 | 2016-01-26 | 4 | 1 |
| 2016-01-26 | 2016-01-26 | 1 | 1 |
| 2016-01-26 | 2016-01-26 | 2 | 1 |
| 2016-01-28 | NULL | 3 | 1 |
| 2016-01-28 | NULL | 4 | 1 |
| 2016-01-28 | NULL | 1 | 1 |
| 2016-01-28 | NULL | 2 | 1 |
+-------------+------------+----------+----------+
so that my return is like this
+-------------+------------+----------+----------+
| attend_date | groupdate | clientid | memberid |
+-------------+------------+----------+----------+
| 2016-01-26 | 2016-01-26 | 3 | 1 |
| 2016-01-26 | NULL | 4 | 1 |
| 2016-01-26 | NULL | 1 | 1 |
| 2016-01-26 | NULL | 2 | 1 |
| 2016-01-28 | NULL | 3 | 1 |
| 2016-01-28 | NULL | 4 | 1 |
| 2016-01-28 | NULL | 1 | 1 |
| 2016-01-28 | NULL | 2 | 1 |
+-------------+------------+----------+----------+
The whole idea here is to be able to pull all the clients who don't yet have notes applied to a groupdate matching history table's attend_date. And only when a note is posted will the groupdate be set to the attend_date. Yet, the query, as I have it , returns a groupdate of 2016-01-26 for all four clients instead of just the one. Thanks in advance for your help.
Looking at your table structure, I see the relationship between your attend_date and groupdate on which you attempted to join, but I also see that those tables have clientid and memberid in common.
Since you only joined on the date, you're getting back a cartesian product of all clients and all dates for that condition. You can make the join's ON clause more specific by adding additional conditions with a logical AND.
LEFT JOIN participation AS p
ON groupdate = attend_date
AND h.clientid = p.clientid
It isn't fully obvious to me if your desired result set also requires a condition to match memberid between those tables, but if it does, the solution is as simple as another AND.
LEFT JOIN participation AS p
ON groupdate = attend_date
AND h.memberid = p.memberid
It helps not to think of a join's ON clause as merely a match between common columns, though that's the most common way it is used. Instead the ON clause just needs to present some condition that when true results in a joined row being returned (just like you're used to with a WHERE clause). That means you can place just about anything there which can be evaluated as true or false, allowing for complex joining logic. And the same can be said for ORDER BY and GROUP BY.

MySQL count 2 columns in a row and join with another query

I have a table visits like this:
+--------------+-------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| vis_id | int(11) | NO | | NULL | |
| unit | int(11) | NO | | NULL | |
| time_in | timestamp | NO | | CURRENT_TIMESTAMP | |
| time_out | timestamp | NO | | 0000-00-00 00:00:00 | |
| in_username | varchar(16) | NO | | NULL | |
| out_username | varchar(16) | NO | | NULL | |
+--------------+-------------+------+-----+---------------------+----------------+
and a table users like this:
+------------+-------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| fname | varchar(32) | NO | | NULL | |
| lname | varchar(32) | NO | | NULL | |
| date_added | timestamp | NO | | CURRENT_TIMESTAMP | |
| username | varchar(16) | NO | | NULL | |
| password | varchar(40) | NO | | NULL | |
| auth_level | int(1) | NO | | 1 | |
| last_login | timestamp | NO | | 0000-00-00 00:00:00 | |
+------------+-------------+------+-----+---------------------+----------------+
I want to be able to count how many times each user is in in_username AND out_username... The query I was using before looks like this:
select count(*) as "count", u.fname as "fname"
from visits v
inner join users as u on u.username = v.in_username
group by u.username order by u.fname
But that only returns how many in_username's there are... I'd like to have both in the same query if possible, so I could get results like this:
+----------+-----------+----------+
| count_in | count_out | fname |
+----------+-----------+----------+
| 118 | 224 | Bo |
| 27 | 64 | James |
| 147 | 138 | Jeremy |
| 23 | 37 | Jim |
| 182 | 172 | Robert |
| 120 | 158 | Tom |
+----------+-----------+----------+
Where count_in is how many times their username appears in visits.in_username, and count_out is how many times their username appears in visits.out_username
Everything I've tried with UNION seems to add the counts together, or removes rows for some reason. Any ideas?
Do a subquery to get each total, combine them with UNION, and then merge them with SUM().
SELECT SUM(count_in) count_in, SUM(count_out) count_out, fname
FROM (SELECT COUNT(*) count_in, 0 count_out, in_username fname
FROM visits v
GROUP BY fname
UNION
SELECT 0 count_in, COUNT(*) count_out, out_username fname
FROM visits v
GROUP BY fname) combined