I have following tables with data as:
1.Table follow_up as :
mysql> select * from follow_up;
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
| follow_up_id | feedback_close | feedback_open | is_email_required | is_Open | reminder_date | client_id | conclusion_id | stage_id |
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
| 1 | NULL | dsffsdfsdfsd | 1 | 1 | 2017-09-20 | 101 | 96 | 72 |
| 2 | NULL | FSGDFHFGHFG | 1 | 1 | 2017-09-28 | 101 | 251 | 72 |
| 3 | NULL | Tender stage fb | 0 | 1 | NULL | 101 | 98 | 163 |
| 4 | NULL | Call back tender stage update date from 28 to 30 | 1 | 1 | 2017-09-28 | 101 | 96 | 163 |
| 5 | NULL | Metting follow up for next meeting | 1 | 1 | 2017-10-02 | 101 | 96 | 73 |
+--------------+----------------+--------------------------------------------------+-------------------+---------+---------------+-----------+---------------+----------+
2. Table logs as :
mysql> SELECT * from logs where transaction = 'FLWUP';
+---------+---------+---------------------+---------+-------------+
| user_id | menu_id | logs_time | tran_id | transaction |
+---------+---------+---------------------+---------+-------------+
| 84 | 69 | 2017-09-19 19:31:04 | 1 | FLWUP |
| 84 | 69 | 2017-09-19 19:31:25 | 2 | FLWUP |
| 84 | 69 | 2017-09-20 19:10:41 | 2 | FLWUP |
| 84 | 69 | 2017-09-21 12:35:01 | 3 | FLWUP |
| 84 | 69 | 2017-09-21 12:35:26 | 4 | FLWUP |
| 84 | 69 | 2017-09-21 12:36:16 | 4 | FLWUP |
| 84 | 69 | 2017-09-21 12:38:30 | 5 | FLWUP |
+---------+---------+---------------------+---------+-------------+
7 rows in set (0.00 sec)
3. table allcode as :
mysql> select * from allcode where code_type like 'MARK%';
+------------------+---------+------+----------------------+
| code_type | code_id | srno | code_name |
+------------------+---------+------+----------------------+
| MARKETING_STAGES | 72 | 1 | Enquiry |
| MARKETING_STAGES | 73 | 3 | Meeting |
| MARKETING_STAGES | 74 | 4 | Presentation |
| MARKETING_STAGES | 163 | 2 | Tender |
+------------------+---------+------+----------------------+
11 rows in set (0.00 sec)
I have invoked a query and got result as :
mysql> select f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
where l.transaction='FLWUP' and f.client_id = 101
group by ast.code_name order by ast.srno;
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
| follow_up_id | feedback_open | feedback_close | reminder_date | stage | conclusion | max(l.logs_time) |
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
| 1 | dsffsdfsdfsd | NULL | 2017-09-20 | Enquiry | Call Back | 2017-09-20 19:10:41 |
| 3 | Tender stage fb | NULL | NULL | Tender | Next | 2017-09-21 12:36:16 |
| 5 | Metting follow up for next meeting | NULL | 2017-10-02 | Meeting | Call Back | 2017-09-21 12:38:30 |
+--------------+------------------------------------+----------------+---------------+---------+------------+---------------------+
3 rows in set (0.00 sec)
But I want result as :
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
| follow_up_id | feedback_open | feedback_close | reminder_date | stage | conclusion | max(l.logs_time) |
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
| 2 | FSGDFHFGHFG | NULL | 2017-09-20 | Enquiry | Call Back | 2017-09-20 19:10:41 |
| 4 | Call back tender stage update date from 28 to 30 | NULL | NULL | Tender | Next | 2017-09-21 12:36:16 |
| 5 | Metting follow up for next meeting | NULL | 2017-10-02 | Meeting | Call Back | 2017-09-21 12:38:30 |
+--------------+-----------------------------------------------------+----------------+---------------+---------+------------+---------------------+
3 rows in set (0.00 sec)
I'm not able to JOIN and group by to get required result.
column conclusion_id and stage_id of table follow_up are referring to code_id of table allcode.
Question :
the result I want is to be
group by stage_id,
order by srno of allcode and
last/recent follow_up_id of follow_up table
DEMO Includes my answer, original question with full group by needed, and Reupal's answer in demo. You were missing the values in your sample data for conclusionID so I just created them based on ID (now updated to ISO, Callback but missing 98.)
and my results don't match yours in this column; but I believe your expected results are in error.
Seems like you want the max follow_up_ID for each stage_ID when multiple stage_ID's exist
This can be handled by a derived table/inline view getting that max follow_UP_ID grouped by the stage_ID and a joining it back to your set. to limit results to include only the max follow_Up_ID by stage_Id.
I'm also not a fan of mySQL's extended group by and prefer including all columns not aggregated in the select in the group by. Using the extended group by tends to hide potential problems. In this case grouping by just the ast.code_name allowed the engine to select a non distinct value from the other columns. You ended up not getting the desired results and furthermore it hide the fact you would get multiple records in your query were it not for the extended group by use/misuse.
SELECT f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
JOIN SELECT max(follow_up_ID) MFID, stage_ID
FROM follow_up
GROUP BY stage_ID) Z
on f.follow_up_ID = Z.MFID
and F.Stage_ID = Z.Stage_ID
WHERE l.transaction='FLWUP' and f.client_id = 101
GROUP BY f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name , ac.code_name
ORDER BY ast.srno;
Try below, notice ordering and group by sequence.
select f.follow_up_id,f.feedback_open, f.feedback_close, f.reminder_date,
ast.code_name as stage, ac.code_name as conclusion, max(l.logs_time)
from follow_up f
join logs l on l.tran_id = f.follow_up_id
join allcode ast on ast.code_id = f.stage_id
join allcode ac on ac.code_id = f.conclusion_id
where l.transaction='FLWUP' and f.client_id = 101
group by follow_up.stage_id order by ast.srno, follow_up.follow_up_id DESC;
This should works, and if its not then you should search like how to set ordering on multiple column.
Ref. article- SQL multiple column ordering
Related
I read that the output of the subquery doesn't matter and only its existence matter. But, when I change the code in the subquery, why is my output changing?
These are the tables:
mysql> select * from boats;
+------+-----------+-------+
| bid | bname | color |
+------+-----------+-------+
| 101 | Interlake | blue |
| 102 | Interlake | red |
| 103 | Clipper | green |
| 104 | Marine | red |
+------+-----------+-------+
mysql> select * from sailors;
+------+---------+--------+------+
| sid | sname | rating | age |
+------+---------+--------+------+
| 22 | Dustin | 7 | 45 |
| 29 | Brutus | 1 | 33 |
| 31 | Lubber | 8 | 55.5 |
| 32 | Andy | 8 | 25.5 |
| 58 | Rusty | 10 | 35 |
| 64 | Horatio | 7 | 35 |
| 71 | Zorba | 10 | 16 |
| 74 | Horatio | 9 | 40 |
| 85 | Art | 3 | 25.5 |
| 95 | Bob | 3 | 63.5 |
+------+---------+--------+------+
10 rows in set (0.00 sec)
mysql> select * from reserves;
+------+------+------------+
| sid | bid | day |
+------+------+------------+
| 22 | 101 | 1998-10-10 |
| 22 | 102 | 1998-10-10 |
| 22 | 103 | 1998-10-08 |
| 22 | 104 | 1998-10-08 |
| 31 | 102 | 1998-11-10 |
| 31 | 103 | 1998-11-06 |
| 31 | 104 | 1998-11-12 |
| 64 | 101 | 1998-09-05 |
| 64 | 102 | 1998-09-08 |
| 74 | 103 | 1998-09-08 |
+------+------+------------+
select sname from sailors s where exists(select * from reserves r where r.bid=103);
+---------+
| sname |
+---------+
| Dustin |
| Brutus |
| Lubber |
| Andy |
| Rusty |
| Horatio |
| Zorba |
| Horatio |
| Art |
| Bob |
+---------+
10 rows in set (0.00 sec)
mysql> select sname from sailors s where exists(select * from reserves r where r.bid=103 and r.sid=s.sid);
+---------+
| sname |
+---------+
| Dustin |
| Lubber |
| Horatio |
+---------+
Also, I am not able to understand what r.sid=s.sid is doing here. All the sid in reserves are already from sailors table. Please someone explain it to me.
The EXISTS is a Boolean Operator which indicates that if there is ANY row in the sub-query you passed to it. When you execute this:
EXISTS(SELECT * FROM reserves r WHERE r.bid=103)
It will return TRUE after finding the FIRST row which has the condition bid = 103 in Reserves table. The first part of the query doesn't matter, it does not matter what you SELECT in Exists and MySQL engine will ignore it, just the WHERE clause is the part which makes the difference, you can use Exists even like this:
EXISTS(SELECT 1 FROM reserves r WHERE r.bid=103)
In the query above, nothing depends on the values in main query, nothing depends on Sailors table, and if there is ANY row in the Reserves table with bid = 103, then it always will return TRUE.
In the second sub-query with EXISTS, you have a different WHERE clause, and it depend on the value of the fields of the main Query, so it will have different result per each row:
EXISTS(SELECT * FROM reserves r WHERE r.bid=103 AND r.sid=s.sid)
In the above query, per each row in Sailors table, MySQL uses sid value to produce the WHERE condition of the sub-query in EXISTS operator, so it will returns TRUE for a row in Sailors table if there are ANY rows in Reserves table which has a bid = 103 and sid = Sailors.sid, and it will returns False for those that has not such a record in Reserves table, and finally you will get a different result
I think I got that. Exists is used to check if the subquery is existing for the main query. I didn't give any link for the main query and subquery in the first query.
For every name in sailors, independently, the subquery is existing. Hence, I got all the names. In the second query, I added s.sid=r.sid which links the main query and subquery. It checks if for a sname, if bid=103, and also, if s.sid=r.sid.
Please comment if I got that right.
I have a table called related_clues which lists the id's of pairs of clues which are related
| id | clue_id | related_clue_id | relatedness |
+----+---------+-----------------+-------------+
| 1 | 1 | 232 | 1 |
| 2 | 1 | 306 | 1 |
| 3 | 1 | 458 | 1 |
| 4 | 2 | 620 | 1 |
| 5 | 2 | 72 | 1 |
| 6 | 3 | 212 | 1 |
| 7 | 3 | 232 | 1 |
| 8 | 3 | 412 | 1 |
| 9 | 3 | 300 | 1 |
+----+---------+-----------------+-------------+
Eventually after a while we may reach two id's such as:
+--------+---------+-----------------+-------------+
| id | clue_id | related_clue_id | relatedness |
+--------+---------+-----------------+-------------+
| 121267 | 1636 | 38 | 1 |
| 121331 | 1636 | 38 | 1 |
+--------+---------+-----------------+-------------+
So in this case, for two distinct id values, we have the same (clue_id, related_clue_id) pair
In this case I would like the relatedness value to be updated to 2, signalling that there are two examples of this (clue_id, related_clue_id) pair. Like so:
+--------+---------+-----------------+-------------+
| id | clue_id | related_clue_id | relatedness |
+--------+---------+-----------------+-------------+
| 121267 | 1636 | 38 | 2 |
| 121331 | 1636 | 38 | 2 |
+--------+---------+-----------------+-------------+
So essentially I would like to run some SQL that sets the relatedness value to the number of times a (clue_id, related_clue_id) pair appears.
When I have no relatedness column present, and I simply run the SQL:
SELECT id, clue_id, related_clue_id, COUNT(*) AS relatedness
FROM `related_clues`
GROUP BY clue_id, related_clue_id
It gives me the required result, but of course this doesn't store the relatedness column, it simply shows the column if I run this select. So how do I permanently have this relatedness column?
You could use a update with join
Update related_clues a
INNER JOIN (
SELECT clue_id, related_clue_id, COUNT(*) AS relatedness
FROM `related_clues`
group by clue_id, related_clue_id
having count(*) = 2
) t on t.clue_id = a.clue_id
and t.related_clue_id = a.related_clue_id
set a.relatedness = t.relatedness
I would approach this as an update/join but filter out rows that don't need to be updated:
update related_clues rc join
(select clue_id, related_clue_id, COUNT(*) AS cnt
from `related_clues`
group by clue_id, related_clue_id
) t
on t.clue_id = rc.clue_id and
t.related_clue_id = rc.related_clue_id
set rc.relatedness = t.relatedness
where rc.relatedness <> t.relatedness;
I have a table with the following columns that I am trying to create a view from in order to create a report, I need to get the sum of completed hours for a particular class but with a specific filter:
| PK_CLASS_DAYS_ID | FK_MAIN_ID | FK_CLASS_ID | CLASS_DAY | OUTCOME | CLASS_DATE | HOURS |
|------------------|------------|-------------|-----------|---------|------------|-------|
| 1 | 27452 | 137 | 1 | *15 | 2015-11-15 | 8 |
| 2 | 27452 | 137 | 2 | *15 | 2015-11-16 | 8 |
| 3 | 27452 | 137 | 4 | *15 | 2015-11-18 | 8 |
| 4 | 27452 | 137 | 5 | BS15 | 2015-11-19 | 8 |
| 5 | 27452 | 2 | 1 | *16 | 2001-01-01 | 8 |
| 6 | 27452 | 48 | 1 | *16 | 2016-01-12 | 8 |
| 7 | 27452 | 48 | 2 | *16 | 2016-02-27 | 4 |
| 8 | 27452 | 2 | 1 | *17 | 2017-07-01 | 8 |
| 9 | 27452 | 137 | 1 | *16 | 2016-07-16 | 8 |
I need to find the SUM of hours completed for each class (FK_CLASS_ID) for every student in my table (currently I have filtered it to ID 27452 for testing purposes) while applying the following filter for each class (FK_CLASS_ID):
(1)CLASS_DAY must be distinct
(2)CLASS_OUTCOME must begin with "*"
(3)CLASS_DATE must be the most recent, while still having the previous two conditions. The resulting view should be as follows:
| PK_CLASS_DAYS_ID | FK_MAIN_ID | FK_CLASS_ID | Hrs |
|------------------|-------------|--------------|------|
| 1 | 27452 | 137 | 32 |
| 2 | 27452 | 2 | 8 |
| 3 | 27452 | 48 | 12 |
The furthest I've gotten with trying to accomplish this, is the following select statement:
SELECT
t1.CLASS,
SUM(class_hours) as Hrs,
GROUP_CONCAT('D',classes_days.class_day) as DaysList,
main.FULLNAME
FROM
classes t1
INNER JOIN classes_days ON classes_days.FK_CLASS_ID = t1.CLASS_ID
INNER JOIN main ON main.PK_MAIN_ID = classes_days.FK_MAIN_ID
WHERE
main.PK_MAIN_ID = 27452
GROUP BY FK_CLASS_ID
ORDER BY CLASS
For what you're wanting to accomplish, you would need to filter your joins with the desired summation queries and provide the joining on the desired criteria from the retrieved recordset.
Basing it off your provided query and desired results, it should look like:
SELECT
`t1`.`CLASS`,
SUM(`class_hours`.`HOURS`) AS `Hrs`,
GROUP_CONCAT('D', `class_hours`.`CLASS_DAY` ORDER BY `class_hours`.`CLASS_DAY`) AS `DaysList`,
`main`.`FULLNAME`
FROM `classes` AS `t1`
INNER JOIN (
#Filter the total hours by student, class, and day
SELECT `class_dates`.`FK_MAIN_ID`, `class_dates`.`CLASS_DAY`, `class_dates`.`FK_CLASS_ID`, SUM(`class_dates`.`HOURS`) as `HOURS`
FROM (
#Filter Most Recent Days beginning with star, by most recent date
SELECT `classes_days`.*
FROM `classes_days`
WHERE `classes_days`.`OUTCOME` LIKE '*%'
ORDER BY `CLASS_DATE` DESC
) AS `class_dates`
GROUP BY `class_dates`.`FK_MAIN_ID`, `class_dates`.`CLASS_DAY`, `class_dates`.`FK_CLASS_ID`
) AS `class_hours`
ON `class_hours`.`FK_CLASS_ID` = `t1`.`CLASS_ID`
INNER JOIN `main`
ON `main`.`PK_MAIN_ID` = `class_hours`.`FK_MAIN_ID`
GROUP BY `class_hours`.`FK_MAIN_ID`, `class_hours`.`FK_CLASS_ID`
ORDER BY `FULLNAME`, `CLASS`;
Resulting In:
| CLASS | Hrs | DaysList | FULLNAME |
|---------|-----|-------------|----------|
| History | 8 | D1 | Joe |
| Math | 32 | D1,D2,D4 | Joe |
| Science | 12 | D1,D2 | Joe |
| Math | 10 | D1,D2 | Mike |
Example: http://sqlfiddle.com/#!9/d5828/1
Original query of table is the top query. The subquery join example is the bottom query result. Removed the PK_MAIN_ID criteria to show it working against multiple entries
Do keep in mind that MySQL GROUP BY + ORDER BY does not always yield the desired results, and should be filtered using a subquery, which is demonstrated in the join subquery in order to get the most recent dates that begin with *.
I have 3 tables , structures are given below
table_incident
+-------+-------------+------------+-----------------------------------+
| id(PK) | incident_display_id | account_id | customized_fields_id |
+-------+-------------+------------+-----------------------------------+
| 47614 | 33 | 394 | 1285,1286,1287,1288 |
+-------+-------------+------------+-----------------------------------+
table_customized_fields_data
+------+------------+----------+-------------+--------------------+
| id | account_id | field_id(FK) | incident_id(FK) | value |
+------+------------+----------+-------------+--------------------+
| 1285 | 394 | 49 | 47614 | Nikon 5MP |
| 1286 | 394 | 50 | 47614 | CDMA |
| 1287 | 394 | 51 | 47614 | Yes |
| 1288 | 394 | 84 | 47614 | 9317001007 |
+------+------------+----------+-------------+--------------------+
table_customized_fields
+----+------------+------------+---------------------+------+-------------+
| id | account_id | field_type | label | name | field_lable |
+----+------------+------------+---------------------+------+-------------+
| 49 | 394 | text_field | Camera | | |
| 50 | 394 | checkbox | Cellphone | | CDMA, GSM |
| 51 | 394 | radio | Sunglasses | | Yes, No |
| 52 | 394 | textarea | Credit Card | | 5 |
| 83 | 394 | radio | Cowboy Hat | | Yes,No |
| 84 | 394 | text_field | Emergency Contact # | | |
+----+------------+------------+---------------------+------+-------------+
Now i want to select only those label and regarding value of that lable which exists in table_incident,
i fired below query
SELECT ti.id IncID,tcf.id labelID,tcfd.id dataID,tcf.label, tcfd.value
FROM table_customized_fields_data tcfd
INNER JOIN table_incident ti ON (ti.id = tcfd.incident_id)
INNER JOIN table_customized_fields tcf ON (tcf.id = tcfd.field_id)
WHERE tcfd.id IN (ti.customized_fields_id)
AND ti.id=47614
+-------+---------+--------+--------+-----------+
| IncID | labelID | dataID | label | value |
+-------+---------+--------+--------+-----------+
| 47614 | 49 | 1285 | Camera | Nikon 5MP |
+-------+---------+--------+--------+-----------+
but only one row is returned, will you all please tell me what wrong i m doing though each subquery working perfect individually.
Note: although this query retruns the desired data:
SELECT tcf.id AS labelID,tcf.label,tcfd.id AS dataID, tcfd.value
FROM table_customized_fields_data tcfd
JOIN table_customized_fields tcf ON tcf.id=tcfd.field_id
JOIN table_incident ti ON ti.id=tcfd.incident_id
WHERE ti.id=47614
but i think there must be some optimized way, please share your idea.
Thanks
Your second query IS the optimized way. Just make sure you have an index on table_customized_fields_data.incident_id.
Also I think you have misunderstood how IN works, quote from the manual:
expr IN (value,...)
Returns 1 if expr is equal to any of the values in the IN list, else returns 0...
so your
tcfd.id IN (ti.customized_fields_id)
is equivalent to:
tcfd.id = ti.customized_fields_id
ti.customized_fields_id is a string, not an array. It's not matching, but you're getting one row because it's a left join and it's mysql.
What you want is something like this:
SELECT ti.id IncID,tcf.id labelID,tcfd.id dataID,tcf.label, tcfd.value
FROM table_customized_fields_data tcfd
INNER JOIN table_incident ti ON ti.id = tcfd.incident_id
AND concat(',', customized_fields_id, ',') like concat('%,', tcfd.id, ',%')
INNER JOIN table_customized_fields tcf ON tcf.id = tcfd.field_id
WHERE ti.id=47614
which says tcfd.id is in the string customized_fields_id when delimited by commas
First off, sorry if this is a near enough duplicate. I've found this question, which nearly does what I want, but I couldn't wrap my head around how to alter it to my needs.
I've got these 3 tables:
cs_Accounts:
+----+-----------------------------+-------------+
| id | email | username |
+----+-----------------------------+-------------+
| 63 | jamasawaffles#googlil.com | jamwaffles2 |
| 64 | jamwghghhfles#goomail.com | jamwaffles3 |
| 65 | dhenddfggdfgetal-pipdfg.com | dhendu9411 |
| 60 | jwapldfgddfgfffles.co.uk | jamwaffles |
+----+-----------------------------+-------------+
cs_Groups:
+----+-----------+------------+-------------+
| id | low_limit | high_limit | name |
+----+-----------+------------+-------------+
| 1 | 0 | 0 | admin |
| 2 | 1 | 50 | developer |
| 3 | 76 | 100 | reviewer |
| 4 | 51 | 75 | beta tester |
| 5 | 1 | 50 | contributor |
+----+-----------+------------+-------------+
cs_Permissions:
+----+---------+----------+
| id | user_id | group_id |
+----+---------+----------+
| 4 | 60 | 4 |
| 3 | 60 | 1 |
| 5 | 60 | 2 |
| 6 | 62 | 1 |
| 7 | 62 | 3 |
+----+---------+----------+
I've been wrestling with a 3 way join for hours now, and I can't get the results I want. I'm looking for this behaviour: a row will be returned for every user from cs_Accounts where there is a row in cs_Permissions that contains their ID and the ID of a group from cs_Groups, as well as the group with the group_id has a high_lmiit and low_limit in a range I can specify.
Using the data in the tables above, we might end up with something like this:
email username cs_Groups.name
----------------------------------------------------------
jwapldfgddfgfffles.co.uk jamwaffles admin
jwapldfgddfgfffles.co.uk jamwaffles developer
jwapldfgddfgfffles.co.uk jamwaffles beta tester
dhenddfggdfgetal-pipdfg.com dhendu9411 admin
dhenddfggdfgetal-pipdfg.com dhendu9411 reviewer
There is an extra condition, however. This condition is where rows are only selected if the group the user belongs to has a high_limit and low_limit with values I can specify using a WHERE clause. As you can see, the table above only contains users with rows in the permissions table.
This feels a lot like homework but with a name like James I'm always willing to help.
select a.email,a.username,g.name
from cs_Accounts a
inner join cs_Permissions p on p.user_id = a.id
inner join cs_Groups g on g.id = p.Group_id
where g.low_limit > 70
and g.high_limt < 120
This is the query
SELECT ac.email, ac.username, gr.name
FROM cs_Accounts AS ac
LEFT JOIN cs_Permissions AS per ON per.user_id = ac.id
INNER JOIN cs_Groups AS gr ON per.user_id = gr.id
You can add a WHERE clause to this query if you want