Creating a formulaic query with multiple columns and joined columns - mysql

I'm trying to produce a formula which pits our students' reward points against their negative behaviour flags.
Students are given LEAP points (in the transactions table) for their positive behaviour. They get more points depending on the category of their reward, i.e. Model Citizen gives the student 10 points.
On the other hand, students are given single Flags for negative behaviour. The category of the Flag is then weighted in a database table, i.e. the Aggressive Defiance category will have a high weighting of 4 whereas Low Level Disruption will only be worth 1.
The difficulty therefore is trying to factor in the Flag categories' weightings. They're stored in the categories table under the Weight column.
Here's the SQL fiddle: http://sqlfiddle.com/#!2/2e5756
In my head, the pseudo-SQL code would look something like this...
SELECT
CONCAT( stu.Surname, ", ", stu.Firstname ) AS `Student`,
SUM(t.Points) AS `LEAP Points`,
SUM(<<formula>>) AS `Flags`
( `LEAP Points` - `Flags` ) AS `Worked Out Points Thing`
FROM student stu
LEFT JOIN transactions t ON t.Recipient_ID = stu.id
LEFT JOIN flags f ON f.Student_ID = stu.id
LEFT JOIN categories c ON f.Category_ID = c.ID
GROUP BY stu.id
However, it's the <<formula>> that I have no idea how to implement in MySQL. It needs to be something like this:
SUM OF[ Each of Student's Flags * that Flag's Category Weighting ]
So, if a student has these flags...
#1 f.Reason "Being naughty", f.Category_ID "1", c.Title "Low Level Disruption", c.Weight "1"
#1 Reason "Aggressively naughty!", Category "Aggressive Defiance", Category Weighting "4"
#1 Reason "Missed detention", Category "Missed Detention", Category Weighting "3"
They would have a total of 1+4+3 = 9 points to use in the Worked Out Points Thing equation.
The desired output therefore is essentially...
Student LEAP Points Flags Equation Points LEAP Points minus Flag Points
D Wraight 1000 800 200
D Wraight2 500 800 -300
D Wraight3 1200 300 900
From the SQL fiddle above, here is the required output.. I've missed out some students because I had to work these out manually:
STUDENT FLAGS LEAP EQUATION
137608 4 (2+2) 12 (2+5+5) 8 (12-4)
139027 2 (2) 7 (2+5) 5 (7-2)
139041 4 (2+1+1+NULL) 8 (2+2+2+2) 4 (8-4)
139892 4 (4) 0 -4 (0-4)
138832 4 (4) 0 -4 (0-4)
34533 4 (4) 0 -4 (0-4)
137434 0 10 (2*5) 10 (10-0)
Which will help us to work out the choices we make available to each student when looking at end of year reward trips.
Hope that makes sense.. it's kinda boggled my head trying to explain it..
Thanks in advance,

figure out your 'formula' bit first because it's the deepest part. work outwards.
build a table of flags * weight per student
select sum(weight), student_id from flags f
join categories c
on f.category_id = c.id
group by student_id
so now you've got a table of flag values to minus from sum of transactions per student
select sum(points), recipient_id from transactions
group by recipient_id
so now we have two tables with positive and negative values by student id (assuming obviously that student id is recipient id)
you want those with transactions but without flags to appear in the result, so outer join.
and number minus null is null so ifnull function on the flags to get 0
select a.student, points - ifnull(penalties, 0) as netPoints
from
(select sum(points) as points, recipient_id as student from transactions
group by student) as a
left outer join
(select sum(weight) as penalties, student_id as student from flags f
join categories c
on f.category_id = c.id
group by student) as b
on
a.student = b.student
so with the name in there it's just
select
concat(firstname, ', ', surname) as name,
ifnull(points,0) as totalPoints,
ifnull(penalties,0) as totalPenalties,
ifnull(points,0) - ifnull(penalties, 0) as netPoints,
ifnull(countFlags, 0)
from
student
left join
(select sum(points) as points, recipient_id as student from transactions
group by student) as a
on student.id = a.student
left join
(select sum(weight) as penalties, count(f.id) as countFlags, student_id as student from flags f
join categories c
on f.category_id = c.id
group by student) as b
on
student.id = b.student
join condition is always from student's id column, which is never null.
there are probably more efficient ways, but who cares?

Returning to the question (and at the risk of repeating myself!), given the following data set, what would the desired result set look like...
SELECT * FROM flags;
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
| ID | Student_ID | Staff_ID | Datetime | Period_ID | Category_ID | Action_Taken_Category_ID | Action_Taken_Status |
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
| 8843 | 137608 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8844 | 137608 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8845 | 139027 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8846 | 139041 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8847 | 139041 | 34961 | 2014-03-11 09:01:02 | 2 | 12 | 26 | P |
| 8848 | 139041 | 34996 | 2014-03-11 09:23:21 | 3 | 12 | 27 | C |
| 8849 | 139041 | 35022 | 2014-03-11 11:07:46 | 4 | 34 | 28 | P |
| 8850 | 139892 | 138439 | 2014-03-11 11:12:47 | 4 | 21 | 7 | C |
| 8851 | 138832 | 138439 | 2014-03-11 11:12:48 | 4 | 21 | 7 | C |
| 8852 | 34533 | 138439 | 2014-03-11 11:12:48 | 4 | 21 | 7 | C |
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
SELECT * FROM categories;
+----+------+--------------------------------------+--------+----------+
| ID | Type | Title | Weight | Added_By |
+----+------+--------------------------------------+--------+----------+
| 10 | F | Low level disruption | 1 | NULL |
| 11 | F | Swearing directly at another student | 2 | NULL |
| 12 | F | Late | 1 | NULL |
| 13 | F | Absconded | 3 | NULL |
| 14 | F | Refusal to follow instruction | 3 | NULL |
| 15 | F | Smoking | 2 | NULL |
| 16 | F | No homework | 2 | NULL |
| 17 | F | Disruptive outside classroom | 2 | NULL |
| 18 | F | Eating/drinking in lesson | 1 | NULL |
| 19 | F | Incorrect uniform/equipment | 1 | NULL |
| 20 | F | Phone out in lesson | 3 | NULL |
| 21 | F | Aggressive defiance | 4 | NULL |
| 22 | F | Missed detention | 3 | NULL |
| 23 | F | Inappropriate behaviour/comments | 3 | NULL |
| 32 | F | IT Misuse | NULL | NULL |
| 34 | F | Inappropriate attitude towards staff | NULL | NULL |
| 35 | F | Care & Guidance | NULL | NULL |
+----+------+--------------------------------------+--------+----------+
SELECT * FROM transactions;
+----------------+------------+----------+--------------+--------+-------------+
| Transaction_ID | Datetime | Giver_ID | Recipient_ID | Points | Category_ID |
+----------------+------------+----------+--------------+--------+-------------+
| 34 | 2011-09-07 | 35019 | 137608 | 2 | 1 |
| 35 | 2011-09-07 | 35019 | 139027 | 2 | 1 |
| 36 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 37 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 38 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 39 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 40 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 41 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 42 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 43 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 44 | 2011-09-07 | 35006 | 137434 | 2 | 1 |
| 45 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 46 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 47 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 48 | 2011-09-07 | 35023 | 137608 | 5 | 2 |
| 49 | 2011-09-07 | 35023 | 139027 | 5 | 2 |
| 50 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 51 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 52 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 53 | 2011-09-07 | 35023 | 137608 | 5 | 3 |
+----------------+------------+----------+--------------+--------+-------------+
SELECT id,UPN,Year_Group,Tutor_Group,SEN_Status,Flags FROM student;
+--------+---------------+------------+-------------+------------+--------+
| id | UPN | Year_Group | Tutor_Group | SEN_Status | Flags |
+--------+---------------+------------+-------------+------------+--------+
| 137608 | A929238400044 | 11 | 11VID | A | |
| 139027 | A929238401045 | 10 | 10KS | | |
| 139041 | A929238402017 | 10 | 10RJ | A | FSM |
| 139892 | A929238403018 | 9 | 9BW | | |
| 139938 | A929238403020 | 9 | 9RH | | |
| 137434 | A929238500027 | 11 | 11VID | | |
| 138832 | A929238502002 | 10 | 10RY | A | FSM,PA |
| 34533 | A929238599028 | 0 | | | PA |
| 139564 | A929241500025 | 12 | | | PA |
| 90306 | A929253100006 | 12 | SLH | A | PA |
+--------+---------------+------------+-------------+------------+--------+

Related

How to join two tables such that same column from both the tables are not repeated? [duplicate]

This question already has answers here:
MySQL Removing duplicate columns on Left Join, 3 tables
(4 answers)
Closed 2 years ago.
I have following two tables
Table 1: employee
+----+---------+--------+--------+------+-------+------+----------+
| No | Name | Salary | Zone | Age | Grade | Dept | HireDate |
+----+---------+--------+--------+------+-------+------+----------+
| 1 | Mukul | 30000 | West | 28 | A | 10 | NULL |
| 2 | Kritika | 35000 | Centre | 30 | A | 10 | NULL |
| 3 | Naveen | 35200 | West | 40 | B | 20 | NULL |
| 4 | Uday | 41800 | North | 38 | C | 30 | NULL |
| 5 | Nupur | 32000 | East | 26 | B | 20 | NULL |
| 6 | Moksh | 37000 | South | 28 | B | 10 | NULL |
| 7 | Shelly | 36000 | North | 26 | A | 30 | NULL |
+----+---------+--------+--------+------+-------+------+----------+
Table 2:department
+------+---------+--------+--------+------+
| Dept | Dname | Minsal | Maxsal | HoD |
+------+---------+--------+--------+------+
| 10 | Sales | 25000 | 32000 | 1 |
| 20 | Finance | 30000 | 50000 | 5 |
| 30 | Admin | 25000 | 40000 | 7 |
+------+---------+--------+--------+------+
Suppose I want to display details of all the employees who work in the Sales department.
I tried INNER JOIN
SELECT *
FROM employee AS A
INNER JOIN department AS B
ON A.Dept = B.Dept AND B.Dname = "Sales";
This displayed this table
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
| No | Name | Salary | Zone | Age | Grade | Dept | HireDate | Dept | Dname | Minsal | Maxsal | HoD |
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
| 1 | Mukul | 30000 | West | 28 | A | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
| 2 | Kritika | 35000 | Centre | 30 | A | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
| 6 | Moksh | 37000 | South | 28 | B | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
As you can see the Dept column is displayed twice.
So here are my questions
1. How to display same name columns only once?
2. Instead of INNER JOIN how else this can be done i.e Any better ways to do this?
Instead of selecting everything with * you need to specify the columns you want. You can also limit the * to all columns of a single table. Example:
SELECT e.*, d.Dname, d.HoD
FROM employee AS e
INNER JOIN department AS d ON e.Dept = d.Dept
WHERE d.Dname = "Sales";
Like this:
select A.No, A.Name, A.Salary, A.Zone, A.Age, A.Grade, A.Dept, A.HireDate,
B.Dname, B.Minsal, B.Maxsal, B.HoD
(...)
Inner join is the right way to join. No problems on this side.

MySQL SELECT 8 unique users, then SELECT all records within those 8 users, then SELECT 4 most recent and available records of each 8 users?

I have a database (dates are just examples for order sake)...
---------------------
| user | item | date |
---------------------
| 1 | a | 123 |
| 3 | b | 124 |
| 1 | c | 125 |
| 2 | d | 126 |
| 5 | i | 127 |
| 4 | e | 128 |
| 6 | f | 129 |
| 9 | g | 130 |
| 3 | h | 131 |
| 9 | s | 132 |
| 1 | j | 133 |
| 2 | k | 134 |
| 1 | l | 135 |
| 1 | m | 136 |
| 1 | n | 137 |
| 8 | o | 138 |
| 5 | p | 139 |
| 9 | q | 140 |
| 7 | r | 141 |
---------------------
I would like to get all records up to the first 8 unique users, which would make the results...
---------------------
| user | item | date |
---------------------
| 1 | a | 123 |
| 3 | b | 124 |
| 1 | c | 125 |
| 2 | d | 126 |
| 5 | i | 127 |
| 4 | e | 128 |
| 6 | f | 129 |
| 9 | g | 130 |
| 3 | h | 131 |
| 9 | s | 132 |
| 1 | j | 133 |
| 2 | k | 134 |
| 1 | l | 135 |
| 1 | m | 136 |
| 1 | n | 137 |
| 8 | o | 138 |
---------------------
Then from those records, I'd like to get the most recent 4 records per unique user, making the results look like...
---------------------
| user | item | date |
---------------------
| 3 | b | 124 |
| 2 | d | 126 |
| 5 | i | 127 |
| 4 | e | 128 |
| 6 | f | 129 |
| 9 | g | 130 |
| 3 | h | 131 |
| 9 | s | 132 |
| 1 | j | 133 |
| 2 | k | 134 |
| 1 | l | 135 |
| 1 | m | 136 |
| 1 | n | 137 |
| 8 | o | 138 |
---------------------
Ideally I would be able to do this with one query. The closest I've been able to come is with this query:
SELECT users,
GROUP_CONCAT(items)
FROM db
GROUP BY users
ORDER BY date
LIMIT 8
But GROUP_CONCAT gives back all results for that user, not just the amount in the selection.
I've also tried...
SELECT users
FROM db AS u1
JOIN (SELECT DISTINCT users FROM db) AS u2 ON u1.users = u2.users
from another suggestion I found but this also didn't work.
I've tried a ton of other things that I didn't really save because they didn't work and I was pretty confident I'd figure it out, but it's been two weeks and I haven't got close. If any SQL gurus are out there that can point me in the right direction, that would be really great. Thanks.
Hoping this can help you.
--get all records up to the first 8 unique users,depending on there first order date
select distinct a.user,a.date as first_order_date from yourtable as a where
a.date = (select MIN(date) from yourtable as b where a.user=b.user) order by a.date LIMIT 8
Then using above result to get the most recent 4 records per unique user like below:
select * from yourtable as t where t.date in
(select date from yourtable as t2 where t.user=t2.user order by t2.date desc LIMIT 4 ) and
t.user in (select distinct a.user,a.date as first_order_date from yourtable as a
where a.date = (select MIN(date) from yourtable as b where a.user=b.user) order by a.date LIMIT 8)
References:
correlated subqueries
Example

Mysql query to convert table from long format to wide format

I have a table called ContactAttrbiutes which contains a list of each contacts' attributes. The kind of data stored for these contacts include: Title, Forename, Surname telephone number etc.
Current Table
+-------------+-----------+------------------------------+
| attributeId | ContactId | AttributeValue |
+-------------+-----------+------------------------------+
| 1 | 5 | Lady |
| 2 | 5 | Elizabeth |
| 3 | 5 | E |
| 4 | 5 | Anson |
| 5 | 5 | |
| 6 | 5 | |
| 7 | 5 | |
| 8 | 5 | |
| 10 | 5 | 0207 72776 |
| 11 | 5 | |
| 12 | 5 | 0207 22996 |
| 13 | 5 | 0207 72761 |
| 14 | 5 | |
| 15 | 5 | |
| 60 | 5 | Lloyds |
| 61 | 5 | |
| 1 | 10 | Mr |
| 2 | 10 | John |
| 3 | 10 | J C |
| 4 | 10 | Beveridge |
| 5 | 10 | Esq QC |
| 6 | 10 | Retired |
| 7 | 10 | |
| 8 | 10 | |
| 10 | 10 | 0207 930 |
| 11 | 10 | |
| 12 | 10 | |
| 13 | 10 | 0207 930 |
| 14 | 10 | |
| 15 | 10 | |
| 60 | 10 | |
| 61 | 10 | |
+-------------+-----------+------------------------------+
However I would like to run a query to create a table that looks like...
New Table
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| ContactId | AttributeValue_Title | AttributeValue_ForeName |AttributeValue_Initial | AttributeValue_Surname |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 5 | Lady | Elizabeth | E | Anson |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 10 | Mr | John | J C | Beveridge |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
I am sure there is a very simple answer but I have spent hours looking. Can anyone help?
The above is only a small extract of my table, I have 750,000 contacts. In addition I would like the final table to have more columns than I have described above but they will come from different Attributes with the existing table.
Thank you very much in advance.
try this
SELECT ContactId ,
max(CASE when attributeId = 1 then AttributeValue end) as AttributeValue_Title ,
max(CASE when attributeId = 2 then AttributeValue end )as AttributeValue_ForeName ,
max(CASE when attributeId = 3 then AttributeValue end )as AttributeValue_Initial ,
max(CASE when attributeId = 4 then AttributeValue end) as AttributeValue_Surname
from Table1
group by ContactId
DEMO HERE
if you want to make your result more longer for other attributeId then just add a case statment as in the code.
SELECT
t_title.AttributeValue AS title,
t_name.AttributeValue AS name,
...
FROM the_table AS t_title
JOIN the_table AS t_firstname USING(contact_id)
JOIN ...
WHERE
t_title.attributeId = 1 AND
t_firstname.attributeId = 2 AND
...
EAV "model" is an antipattern in most cases. Are you really going to have a variable number of attributes? If yes, then no-SQL solution might be more appropriate than a relational database.

MYSQL Group by with the minimum value

Hi I've been searching for this, and I've found solutions for other sample code, but I can't figure out how to implement for mine.
SELECT `gameDBGames`, `game_id`, MIN(`gamePrice`) AS `gamePrice`
FROM `games`
LEFT JOIN `platforms` ON `gamePlatform` = `platform_id`
LEFT JOIN `bundles` ON `gameBundle` = `bundle_id`
LEFT JOIN `currency` ON `bundleCurrency` = `currency_id`
WHERE `bundleEnd` > CURDATE() AND `bundleType` = "1" AND `gameDBGames` != "0"
GROUP BY `gameDBGames`
Here is my actual query. This returns the minimum price, but do not correspond to the game_id. How could I do that? I believe is doing a inner join like this:
SELECT `gameDBGames`, `game_id`, MIN(`gamePrice`) AS `gamePrice`
FROM `games`
LEFT JOIN `platforms` ON `gamePlatform` = `platform_id`
LEFT JOIN `bundles` ON `gameBundle` = `bundle_id`
INNER JOIN (....)
LEFT JOIN `currency` ON `bundleCurrency` = `currency_id`
WHERE b.`bundleEnd` > CURRDATE() AND b.`bundleType` = "1" AND g.`gameDBGames` != "0"
Thank you.
EDIT: Sorry I don't know what I was thinking not posting the table structure.
The game_id is the unique id (e.g. same game but with different prices) , and gameDBGames is an ID for a game (e.g. gameDBGames = 1, can have price 40 or 30) that's why I'm grouping by gameDBGames. An the aim is getting a unique gameDBGames with the minimum price.
What I have.
+--------------+---------+----+---------+
| table games | | | |
+--------------+---------+----+---------+
| game_id | int | AI | PRIMARY |
| gameDBGames | int | | |
| gamePrice | float | | |
| gamePlatform | tinyint | | |
| gameBundle | int | | |
+--------------+---------+----+---------+
+---------+-------------+-----------+--------------+------------+
| game_id | gameDBGames | gamePrice | gamePlatform | gameBundle |
+---------+-------------+-----------+--------------+------------+
| 1 | 1 | 20 | 1 | 1 |
| 2 | 2 | 20 | 2 | 1 |
| 3 | 2 | 15 | 2 | 1 |
| 4 | 3 | 17 | 1 | 1 |
| 5 | 3 | 20 | 1 | 1 |
| 6 | 3 | 15 | 1 | 1 |
| 7 | 4 | 16 | 2 | 2 |
| 8 | 5 | 18 | 2 | 2 |
| 9 | 5 | 14 | 2 | 2 |
| 10 | 6 | 15 | 1 | 2 |
+---------+-------------+-----------+--------------+------------+
What I get.
+---------+-------------+-----------+--------------+------------+
| game_id | gameDBGames | gamePrice | gamePlatform | gameBundle |
+---------+-------------+-----------+--------------+------------+
| 1 | 1 | 20 | 1 | 1 |
| 2 | 2 | 15 | 2 | 1 |
| 4 | 3 | 15 | 1 | 1 |
| 7 | 4 | 16 | 2 | 2 |
| 8 | 5 | 14 | 2 | 2 |
| 10 | 6 | 15 | 1 | 2 |
+---------+-------------+-----------+--------------+------------+
As you can see the game_id do not correspond to the gamePrice. It should be like this.
+---------+-------------+-----------+--------------+------------+
| game_id | gameDBGames | gamePrice | gamePlatform | gameBundle |
+---------+-------------+-----------+--------------+------------+
| 1 | 1 | 20 | 1 | 1 |
| 3 | 2 | 15 | 2 | 1 |
| 6 | 3 | 15 | 1 | 1 |
| 7 | 4 | 16 | 2 | 2 |
| 9 | 5 | 14 | 2 | 2 |
| 10 | 6 | 15 | 1 | 2 |
+---------+-------------+-----------+--------------+------------+
Hope you understand now, sorry for not been more explained before. If you need something else please ask. Thank you.
EDIT 2 (Mark)
The solution put by Mark gives me this errors.
There are gameDBGames repeated.
Some games are non shown.
I've updated the WHERE clause just in case is needed.
Try:
SELECT g.`gameDBGames`, g.`game_id`, g.`gamePrice`
FROM (SELECT `gameDBGames`, MIN(`gamePrice`) AS `minPrice`
FROM `games`
GROUP BY `gameDBGames`) mn
JOIN `games` g
ON mn.`gameDBGames`=g.`gameDBGames` and mn.`minPrice`=g.`gamePrice`
LEFT JOIN `platforms` p ON g.`gamePlatform` = p.`platform_id`
LEFT JOIN `bundles` b ON g.`gameBundle` = b.`bundle_id`
LEFT JOIN `currency` c ON b.`bundleCurrency` = c.`currency_id`
WHERE ....
The GROUP BY in the main query should not be required, due to the grouping in the subquery - however, more than one game_id will be returned for a given gameDBGames if there is more than one game_id with the same minimum price for a given gameDBGames.

Getting nested unread messages from within a message thread

I have two tables, message_threads and messages. When returning a results set of message_threads, I'm performing a JOIN on the two tables to see whether any message from the sender (to the receiver) within that thread is unread.
Below is my SQL...
SELECT mt.id AS thread_id, m.id AS message_id,
m.is_read, m.from_type, mt.company_id
FROM message_threads AS mt
LEFT JOIN messages AS m
ON m.thread_id = mt.id;
...the full results set from a given query...
+-----------+------------+---------+-----------+------------+
| thread_id | message_id | is_read | from_type | company_id |
+-----------+------------+---------+-----------+------------+
| 1 | 1 | N | company | 1 |
| 1 | 9 | N | company | 1 |
| 1 | 19 | N | company | 1 |
| 2 | 2 | Y | coder | 1 |
| 2 | 3 | N | company | 1 |
| 2 | 6 | N | company | 1 |
| 2 | 8 | N | company | 1 |
| 3 | 4 | N | company | 1 |
| 6 | 13 | N | company | 1 |
| 6 | 14 | N | coder | 1 |
| 6 | 15 | N | company | 1 |
| 8 | 20 | N | company | 1 |
| 8 | 21 | N | coder | 1 |
| 4 | 5 | N | company | 2 |
| 4 | 7 | N | company | 2 |
| 4 | 22 | N | coder | 2 |
| 5 | 10 | N | company | 8 |
| 5 | 11 | N | coder | 8 |
| 5 | 12 | N | company | 8 |
| 7 | 16 | N | company | 18 |
| 7 | 17 | N | coder | 18 |
| 7 | 18 | N | company | 18 |
+-----------+------------+---------+-----------+------------+
...and the desired result set:
+-----------+------------+---------+-----------+------------+
| thread_id | message_id | is_read | from_type | company_id |
+-----------+------------+---------+-----------+------------+
| 1 | 19 | N | company | 1 |
| 2 | 2 | Y | coder | 1 |
| 3 | 4 | N | company | 1 |
| 6 | 14 | N | coder | 1 |
| 8 | 21 | N | coder | 1 |
| 4 | 22 | N | coder | 2 |
| 5 | 11 | N | coder | 8 |
| 7 | 17 | N | coder | 18 |
+-----------+------------+---------+-----------+------------+
How can I perform this query? I've tried GROUP BY and DISTINCT, and neither quite do what I want. I also can't do this using a WHERE clause to filter my data, because I need all the threads regardless of the is_read flag.
Thanks!
There are a lot of ways of doing this. If your looking to see the number of unread messages from a thread you may want to join an inner query.
Example:
SELECT
mt.id AS thread_id,
mt.company_id,
CASE WHEN m_c.m_unread IS NOT NULL THEN m_c.m_unread ELSE 0 END AS unread
FROM message_threads AS mt
LEFT JOIN
(
SELECT
thread_id,
COUNT(*) AS m_unread
FROM messages
WHERE
is_read ='N'
GROUP BY 1
)m_c
ON mt.thread_id = m_c.thread_id