MySQL limit most recent year and use JOIN - mysql

I have the following code and I would like to select only the most recent record (hence why I have written max(a.fiscal_year) except the query returns every fiscal year. How can I adjust the following query to return only the most recent (maximum) fiscal year.
SELECT count(*), b.auditor_name, c.pubco_name, max(a.fiscal_year), a.month
FROM a_fees_view a, a_auditor b, a_pubco c
WHERE a.auditor_id = b.auditor_id AND a.pubco_id = c.pubco_id
GROUP BY a.auditor_id, a.pubco_id, a.fiscal_year
ORDER BY b.auditor_name, c.pubco_name, a.fiscal_year
Furthermore, I would like to select ALL pubcos from the a_pubco table and not just the one's that are also present in the a_fees_view table. How can I adjust the query above to include all the pubco's that are in both the a_fees_view table and the a_pubco table.

The first part of this problem is a Top-N (or Groupwise-max) query. The usual syntax is as follows:
SELECT x.*
FROM my_table x
JOIN (SELECT grouping_id, MAX(other_field) max_other_field FROM my_table GROUP BY grouping_id) y
ON y.grouping_id = x.grouping_id
AND y.max_other_field = x.other_field;
Consider the following example (inspired by a question asked elsewhere):
I have a table of results from showjumping events. I want to find out in which event each horse did best (and what result they achieved).
SELECT * FROM events;
+----------+----------+-------+
| Event_id | Horse_id | Place |
+----------+----------+-------+
| 601 | 101 | 1 |
| 601 | 102 | 2 |
| 601 | 201 | 3 |
| 601 | 301 | 4 |
| 602 | 201 | 2 |
| 603 | 201 | 3 |
| 701 | 101 | 1 |
| 801 | 301 | 2 |
| 901 | 102 | 7 |
+----------+----------+-------+
From inspection, I can see that horse 101's best result was '1st' which she achieved in two events, so I want both of those rows to be returned.
Horse 102's best result was '2nd', as was horse 201's and horse 301's. But how to construct a query that tells us that? Here's how:
SELECT x.*
FROM events x
JOIN (SELECT horse_id,MIN(place) min_place FROM events GROUP BY horse_id) y
ON y.horse_id = x.horse_id AND y.min_place = x.place;
+----------+----------+-------+
| Event_id | Horse_id | Place |
+----------+----------+-------+
| 601 | 101 | 1 |
| 601 | 102 | 2 |
| 602 | 201 | 2 |
| 701 | 101 | 1 |
| 801 | 301 | 2 |
+----------+----------+-------+

Related

SQL Query not having a value

I have a table as follows:
------+-------------+------------+----------------+------------------------------------------------+
| id | customer_id | date | action_type_id | details |
+------+-------------+------------+----------------+------------------------------------------------+
| 4225 | 324 | 2015-09-07 | 1 | Sent mail Malcolm Murrey |
| 6320 | 324 | 2017-05-08 | 3 | quotes for price. |
| 156 | 326 | 2013-07-25 | 3 | Site visit to price job |
| 943 | 326 | 2013-10-23 | 1 | Arranged visit for snags on panel |
| 1135 | 326 | 2013-11-28 | 1 | Arranged visit for site mod |
| 1930 | 326 | 2014-04-15 | 2 | Quoted for new HMI |
| 2644 | 326 | 2014-10-20 | 2 | Sent email about pending quote for HMI |
| 2821 | 326 | 2014-11-25 | 1 | Screen problem |
| 2184 | 328 | 2014-07-21 | 1 | Sent email detailing services |
+------+-------------+------------+----------------+------------------------------------------------+
I'm trying to find the customer_id of customers that don't have an action_type_id of 2. In this case 324 and 328 but just can't seem to figure it out.
I was thinking I could do something like group by customer_id not having action_type_id = 2.... any help much appreciated.
I would use aggregation:
select customerid
from t
group by customerid
having sum(action_type_id = 2) = 0;
I find the use of group by and having for this type of query to be very flexible on the conditions you might have (say you want 2 and 3, or not 2 but either 3 or 4).
You can do this by a left join to itself on records with an id of 2, then filtering on those without a match.
Something along the lines of
SELECT DISTINCT customer_id
FROM customer a
LEFT JOIN customer b
ON a.customer_id = b.customer_id
AND b.action_type_id = 2
WHERE b.customer_id IS NULL
Why not just use not exist:
select *
from table t
where not exists (select 1 from table
where customer_id = t.customer_id and
action_type_id = 2);
Why not this,
select distinct customer_id from customer where action_type_id <> 2
This should work:
SELECT customer_id FROM customers WHERE action_type_id <> 2

Select on row in left with one to many relation

I have table obs
+--------+-------+
| obs_id | name |
+--------+-------|
| 101 | mics |
| 102 | jan |
+--------+-------+
I have table monitoring
+--------+--------+---------+
| mon_id | obs_id | code_id |
+--------+--------+---------+
| 1 | 101 | 201 |
| 2 | 101 | 201 |
| 3 | 101 | 202 |
| 4 | 102 | 201 |
| 5 | 102 | 202 |
+--------+--------+---------+
I have table code
+--------+-----------+
|code_id | code_name |
+--------+-----------|
| 201 | node |
| 202 | java |
| 203 | c++ |
+--------+-----------+
Query result
+--------+--------+---------+-----------+
| obs_id | name | code_id | code_name |
+--------+--------+---------+-----------+
| 101 | mics | 201 | node |
| 102 | jan | 201 | node |
+--------+--------+---------+-----------+
Can someone give me a proper mysql query to come up my result.
select A.obs_id, A.name, M.code_id, C.code_name from obs as A
left join monitoring as M on M.obs_id = A.obs_id
left join code as C on C.code_id = M.code_id
The return of my query is more than 2 or it is not what I want as a result.
As per your result ,its look like you want to results for only 'node' .
Then query will be look like below :
CREATE PROCEDURE GETDATA
AS
#CodeId int=0
BEGIN
select DISTINCT A.obs_id, A.name, M.code_id, C.code_name from obs as A
left join monitoring as M on M.obs_id = A.obs_id
left join code as C on C.code_id = M.code_id WHERE c.code_id=#CodeId
END
Now ,you need to only pass codeID into stored Procedure ,it will be return an output based on CodeID . Its look like dynamic .
The above query will give result as you required.
If you want to get distinct data for all code, then just remove where condition.
Thanks .

Get Sum, Multiple Group By with Filter

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 *.

solve mysql query

Today I have been asked a question by an interviewer that stated
we have three tables named as table A, B, and C.
Those tables are like this
A B C
------------------ -------------------------- ----------------------------
| ID | ProjectID | | ID | LocationID | aID | | ID | points | LocationID |
------------------ -------------------------- ----------------------------
| 1 | 15 | | 1 | 131 | 1 | | 1 | 123333 | 131 |
| 2 | 15 | | 2 | 132 | 1 | | 2 | 123223 | 132 |
| 3 | 15 | | 3 | 133 | 1 | | 3 | 522 | 211 |
| 4 | 12 | | 4 | 134 | 2 | | 4 | 25 | 136 |
------------------ | 5 | 136 | 2 | | 5 | 25 | 133 |
| 6 | 137 | 3 | | 6 | 25 | 134 |
| 7 | 138 | 1 | | 7 | 25 | 135 |
-------------------------- ----------------------------
now he told me to write a query that sums the points of those locations whose project is 15.
First i wrote the query to get ID's from table A like this
SELECT ID from A where projectID = 15
then i pass this result in table b query just like this
SELECT LocationID FROM B WHERE aID IN ( SELECT ID from A where projectID = 15 )
Then i calculate the sum of these locations just like this
SELECT SUM(points) from C where LocationID IN(SELECT LocationID FROM B WHERE aID IN ( SELECT ID from A where projectID = 15))
My Result is fine and query is correct. But he rejected my answer by saying that this nested IN Clause will slow down the whole process as when we have thousands of records.
Then he gave me another chance to review my answer but i couldn't figure it out.
Is there anyway to optimize this or is there some other way to do the same.
Any help? Thanks
Try this it may solve your problem.
Select SUM(C.points) FROM C JOIN B ON C.LocationID = B.LocationID JOIN A ON B.aID = A.ID where A.ProjectID = 15 GROUPBY A.ProjectID
Try with this....i hope it will work
select sum(c.points) as sum_points
from A a,B b,C c where
a.ID=b.aID and
b.LocationID=c.LocationID
and a.projectID=15

Joining 3 tables

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