SQL: 4 Tables to 1 table with counts, groups and deductions?! - mysql

I have a project with lost, found and matched luggage on airports. I made it in Java(FX) and mySQL.
This is what I have:
I have 4 tables:
1 table Airports with 2 columns:
Airport_id & Airport_name
1 table Found with 3 columns:
Found_id & Found_AirportID & Matched
1 table Lost with 3 columns:
Lost_id & Lost_AirportID & Matched
1 table Match with 3 columns:
Match_id & Match_LostID & Match_FoundID & Match_AirportID
Whenever a match is made, the Match table gets a new row with the Match_LostID (from the Lost_id) & Match_FoundID (from the Found_id) and the Match_AirportID (Found_AirportID)
The Matched (in both Found & Lost) get both set to 1, instead of NULL
All the AirportID's are linked to the Airport table.
What I want;
For each and every airport I want the count of the lost items, the count of found items and the count of matched items. BUT when a item is 'matched' it may not appear in the count of the lost and found.
So I want a table with 4 columns:
Airportname, Count of Found, Count of Lost, Count of Matched.
I've made the following Query:
SELECT vv.Airport_name,
COUNT(DISTINCT gb.Found_id) countFound,
COUNT(DISTINCT vb.Lost_id) countLost,
COUNT(DISTINCT kt.Match_id) countMatch
FROM Airports vv
LEFT JOIN Found gb ON vv.Airport_id = gb.Found_AirportID
LEFT JOIN Lost vb ON vv.Airport_id = vb.Lost_AirportID
LEFT JOIN Match kt ON vv.Airport_id = kt.Match_AirportID
WHERE vb.Matched IS NULL OR gb.Matched IS NULL
GROUP BY vv.Airport_name
I manage to get all the count items for Found, Lost and Match.
e.g. New York has 2 found, 2 lost and 1 match.
This is displayed correctly in the table.
But as I said, if there is a match it should be removed from found and lost. It should be:
New York has 1 found, 1 lost and 1 match.
I tried a lot of things, 1 time I manage to do it but then a Airport is missing or it gets deducted from Found but not for lost...
I do not know what the solution is, can someone explain / give it to me?
Thanks in advance,
LTKort

Put the Matched IS NULL checks in the ON conditions of the LEFT JOIN, not WHERE.
SELECT vv.Airport_name,
COUNT(DISTINCT gb.Found_id) countFound,
COUNT(DISTINCT vb.Lost_id) countLost,
COUNT(DISTINCT kt.Match_id) countMatch
FROM Airports vv
LEFT JOIN Found gb ON vv.Airport_id = gb.Found_AirportID AND gb.Matched IS NULL
LEFT JOIN Lost vb ON vv.Airport_id = vb.Lost_AirportID AND vb.Matched IS NULL
LEFT JOIN Match kt ON vv.Airport_id = kt.Match_AirportID
GROUP BY vv.Airport_name
The problem with doing it in WHERE is that you're only getting the results where either the Lost or Found item was matched.

Alternatively, consider joining derived tables of aggregates to avoid many-to-many joins during the COUNT() evaluations:
SELECT a.AirportName, ftbl.countFound, lbtl.countLost, mtbl.countMatched
FROM Airports a
LEFT JOIN
(SELECT f.Found_AirportID, COUNT(f.Found_id) AS countFound
FROM Found f
WHERE f.Matched IS NULL
GROUP BY f.Found_AirportID) As ftbl
ON a.Airport_id = ftbl.Found_AirportID
LEFT JOIN
(SELECT l.Lost_AirportID, COUNT(l.Lost_id) AS countLost
FROM Lost l
WHERE l.Matched IS NULL
GROUP BY l.Lost_AirportID) As ltbl
ON a.Airport_id = ltbl.Lost_AirportID
LEFT JOIN
(SELECT m.Match_AirportID, COUNT(m.Match_id) AS countMatched
FROM Matched m
GROUP BY m.Match_AirportID) As mtbl
ON a.Airport_id = mtbl.Match_AirportID

Related

prevent duplicate result when using self-join in mysql

I have some hotel with different hotel view for each room , and my end user when want 3 rooms for example each the room is 2 bed I have to combination the record for that , some times some fields may be duplicated and It's not important , and the user may have different room type in this situation I make example to each room have 2 bed number :
I have problem when get result :
I use self-join in "mysql" and all thing are true but some problem is have same row in this situation this is my code:
SELECT
table1.id,
table2.id,
table3.id,
table1.num_bed,
table2.num_bed,
table3.num_bed
LEFT JOIN
tour_package table2
ON table1.tour_id = table2.tour_id
AND table1.hotel_id = table2.hotel_id
AND table1.start_date = table2.start_date
LEFT JOIN
tour_package table3
ON table2.tour_id = table3.tour_id
AND table2.hotel_id = table3.hotel_id
AND table2.start_date = table3.start_date
WHERE
table1.num_bed = 2
AND table2.num_bed = 2
AND table3.num_bed = 2
the result is :
Please attention to id , the one is table1.id, two is table2.id and three is table3.id
In the result we have some result like :
1-2-1
1-1-2
and etc
I want to prevent this and have one of them , please help me
I assume the goal of the query is to list up to 3 rooms per record in the same hotel, for the same tour and dates that have 2 bed.
(I honestly do not get the point of the query because it will list all 2-bed rooms in the tour_package table.)
This means that not only 1-1-2 and 1-2-1 are duplicates, but also the 2nd 1 is a redundant information. In hotel no. 7 there are only 2 rooms that satisfy this criteria: 1 and 2.
In the join criteria I would require that a record with different id should be returned from each of the tables. This would force the query to return list of unique ids in a record.
SELECT
table1.id,
table2.id,
table3.id,
table1.num_bed,
table2.num_bed,
table3.num_bed
FROM tour_package table1
LEFT JOIN
tour_package table2
ON table1.tour_id = table2.tour_id
AND table1.hotel_id = table2.hotel_id
AND table1.start_date = table2.start_date
AND table1.id<table2.id
LEFT JOIN
tour_package table3
ON table2.tour_id = table3.tour_id
AND table2.hotel_id = table3.hotel_id
AND table2.start_date = table3.start_date
AND table2.id<table3.id
WHERE
table1.num_bed = 2
AND table2.num_bed = 2
AND table3.num_bed = 2
However, the above query can potentially still return redundant data if there are at least 2 rooms with the above criteria in a hotel. Assuming the 2 rooms have the ids 1 and 2, the query would return 1, 2, null and 2, null, null. To overcome this issue, I would just simply write:
select id, hotel_id from tour_package
where tour_package.num_bed=2
order by tour_id, hotel_id, start_date
The reason: even your on query will show all 2-bed rooms in your tour_package table.

MySQL - unique rows, corresponding to one of 3 tables only

The following query pulls data correctly as expected, however the left join with lnk_cat_isrc table and through that to catalogue table, brings back repeated data if there is more than one item in catalogue which has the same isrcs from isrc table:
SELECT
isrc.ISRC,
isrc.Track_Name,
isrc.ArtistName,
isrc.TitleVersion,
isrc.Track_Time,
`isrc_performer`.`PerformerName` ,
`performer_category`.`PerformerCategory` ,
`isrc_performer`.`PerformerRole` ,
`isrc`.`isrc_ID`,
`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer` ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category` ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
LEFT JOIN `lnk_cat_isrc` ON (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
LEFT JOIN `catalogue` ON (catalogue.ID = lnk_cat_isrc.cat_id)
ORDER BY isrc_ID desc LIMIT 0 , 10
";
I cannot use group by on isrc, because the isrc_performer table can have more than one performer to an isrc.
So the relations are like this:
Few items from catalogue table can have several identical items from isrc table. In turn, each isrc can have more than one entry in isrc_performer table.
What I want is to display all corresponding data from isrc_performer in relation to each isrc, but not repeating it for each item from catalogue table.
I also want to display all the rest "empty" isrcs (those which don't have any data in isrc_performer table)
Can you give me any ideas?
P.S. despite I'm not pulling any data from catalogue table itself, I'm using it to search by a catalogue number, when user defines search criteria for $where_condition variable, hence I need to keep it in the query.
i.e. $where_condition = "catalogue.Catalogue LIKE '%test%' OR ISRC LIKE '%test%' OR Track_Name LIKE '%test%' OR ArtistName LIKE '%test%' OR TitleVersion LIKE '%test%' OR PerformerName LIKE '%test%' OR PerformerCategory LIKE '%test%' OR PerformerRole LIKE '%test%'";
------UPD:
trying to graphically represent possible variation in these 3 tables relations:
cat1 - isrc1 - performer1
isrc2 - performer1
- performer2
- performer3
cat2 - isrc2 - performer1
- performer2
- performer3
- isrc3 - performer2
- performer4
cat3 - isrc4
- isrc1 - performer1
UPD (pics added)
Here are screen prints. As you can see on picture 1 there are 9 rows with same isrc number, however there are 3 repeated performers Jason, David, Paul.
This is because 3 different catalogue items have this exact isrc with 3 different performers as per pic 2
= 1(isrc) * 3(catalogue) * 3(performers) = 9 row on output
All I want is that Performers grid would only display 3 rows of this isrc for each performer.
---Rearrange the answer to put the "best" option up top.. .but is all of this for naught.. w/o any data from lnk_cat_isrc or catalogue being returned, why does filtering on catalog make a difference? we're returning all isrc regardless of any filtering because it's a left join...
So this brings into question given sample data what are the expected results.
Possibly more elegant... (but not sure if it would be faster) moving away from exists and simply using a distinct in a subquery so catalog queries always return 1 row per isrc; solving the 1-M problem keeping the left join thereby keeping the isrc records not in the catalog limits.
Return all isrc information performer information if it exists, performer category info if it exists and catalogue information If, and only if it matches the catalog filters.
SELECT isrc.ISRC
, isrc.Track_Name
, isrc.ArtistName
, isrc.TitleVersion
, isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON isrc.isrc_ID = isrc_performer.isrc_ID
LEFT JOIN `performer_category`
ON performer_category.PerfCat_ID = isrc_performer.PerfCat_ID
LEFT JOIN (SELECT distinct lnk_cat_isrc.isrc_ID
FROM `lnk_cat_isrc`
INNER JOIN `catalogue`
ON catalogue.ID = lnk_cat_isrc.cat_id
WHERE...) DCat
ON Dcat.isrc_ID = isrc.isrc_ID
ORDER BY isrc_ID desc
LIMIT 0 , 10;
As you pointed out the join is causing the problem. So eliminate the join and use the exists notation. Distinct would also work since you're not selecting any values from catalog; though exists should be faster.
Fast but doesn't include all isrc records... (not sure why the or not exists should bring them back in...)
SELECT isrc.ISRC
, isrc.Track_Name
,isrc.ArtistName
,isrc.TitleVersion
,isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category`
ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
WHERE EXISTS (SELECT *
FROM `lnk_cat_isrc`
INNER JOIN `catalogue`
ON catalogue.ID = lnk_cat_isrc.cat_id
--and your other criteria
WHERE (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
)
OR NOT EXISTS (SELECT *
FROM `lnk_cat_isrc`
WHERE lnk_cat_isrc.isrc_ID = isrc.isrc_ID
ORDER BY isrc_ID desc
LIMIT 0 , 10
Or using select distinct simple straight forward; but slow
SELECT isrc.ISRC
, isrc.Track_Name
,isrc.ArtistName
,isrc.TitleVersion
,isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category`
ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
LEFT JOIN `lnk_cat_isrc`
ON (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
LEFT JOIN `catalogue`
ON (catalogue.ID = lnk_cat_isrc.cat_id)
--AND (other criteria on catalog here, cause in a where clause you left joins will behave like inner joins)
ORDER BY isrc_ID desc
LIMIT 0 , 10;

Trying to building optimised Query for Group and Subgroup for a user

i am trying to write the Query for three things .My table structure is like that
You can see Schema at http://sqlfiddle.com/#!2/56c2d/1
I am trying to write the query in MYSQL
user:- table
user_id
user_fname
This is User tabke which will save User Information
group:- "group" and "subgroup" is maintain in same table using column "group_parent_group_id"
group_id
group_title
group_parent_group_id(INT)
This is group table which will save Group and Subgroups
user_group: table
user_group_id
user_group_user_id
user_group_group_id
This ill store both User and Group relation using their Id
I am trying to write the Query for three things. Fetching Users Groups, Subgroups
1) Query to fetch list of All Groups for User Register. Query is gelow and is giving error
Query:
select user.id, user.user_fname, group.group_id, group.group_title
from `user`
inner join user_group on user_group.user_group_user_id = user.user_id
inner join group on group.group_id = user_group.user_group_group_id
where user_group.user_group_user_id = 1 and user_group.group_parent_group_id = 0
2) I am Looking the query to fetch all subgroups(For Whom user is already Register) for Group Id 1,2 or 1
3) I am Looking the query to fetch all subgroups(For Whom user is Not Register yet) for Group Id 1,2 or 1. Ideal is for giving him randomly suggest a subgroup to add
Please Help. I am a newbie in DB :(
Your query is probably failing as you have a table called group, which is a reserved word. You can use back tics to delimit the name to get away with this (as follows) but it would be a better idea to change the table name.
SELECT user.id, user.user_fname, `group`.group_id, `group`.group_title
FROM `user`
INNER JOIN user_group ON user_group.user_group_user_id = user.user_id
INNER JOIN `group` ON `group`.group_id = user_group.user_group_group_id
WHERE user_group.user_group_user_id = 1
AND user_group.group_parent_group_id = 0
EDIT updated for queries I think the OP requires.
First query will get a list of all the groups (ones that have no parent group id) that a user (in this case id 28) is a member of
SELECT y2m_user.user_id, y2m_user.user_first_name, y2m_group.group_id, y2m_group.group_title
FROM y2m_user
INNER JOIN y2m_user_group ON y2m_user_group.user_group_user_id = y2m_user.user_id
INNER JOIN y2m_group ON y2m_group.group_id = y2m_user_group.user_group_group_id
WHERE y2m_user.user_id = 28
AND y2m_group.group_parent_group_id = 0
This query will get a list of all the sub groups (ones where the parent group id is greater than 0) that a user (in this case id 28) is a member of
SELECT y2m_user.user_id, y2m_user.user_first_name, y2m_group.group_id, y2m_group.group_title
FROM y2m_user
INNER JOIN y2m_user_group ON y2m_user_group.user_group_user_id = y2m_user.user_id
INNER JOIN y2m_group ON y2m_group.group_id = y2m_user_group.user_group_group_id
WHERE y2m_user.user_id = 28
AND y2m_group.group_parent_group_id > 0
This query will get a list of all the sub groups (ones where the parent group id is greater than 0) that a user (in this case id 28) is NOT a member of
SELECT y2m_user.user_id, y2m_user.user_first_name, y2m_group.group_id, y2m_group.group_title
FROM y2m_user
CROSS JOIN y2m_group
LEFT OUTER JOIN y2m_user_group ON y2m_user_group.user_group_user_id = y2m_user.user_id AND y2m_group.group_id = y2m_user_group.user_group_group_id
WHERE y2m_user.user_id = 28
AND y2m_group.group_parent_group_id > 0
AND y2m_user_group.user_group_id IS NULL
Please excuse any typos as not tested (with your test data there are no sub groups).

MySQL join & search

I have a problem with joining some tables, heres my structure:
tbl_imdb:
fldID fldTitle fldImdbID
1 Moviename 0000001
tbl_genres:
fldID fldGenre
1 Action
2 Drama
tbl_genres_rel:
fldID fldMovieID fldGenreID
1 1 1
2 1 2
What I’m trying to do is a query that will find all movies that is both an action movie and drama, is this possible to do without a subquery, if so, how?
What I'm trying right now is:
SELECT tbl_imdb.*
FROM tbl_imdb
LEFT JOIN tbl_imdb_genres_rel ON ( tbl_imdb.fldID = tbl_imdb_genres_rel.fldMovieID )
LEFT JOIN tbl_imdb_genres ON ( tbl_imdb_genres_rel.fldGenreID = tbl_imdb_genres.fldID )
WHERE tbl_imdb_genres.fldGenre = 'Drama'
AND tbl_imdb_genres.fldGenre = 'Action';
But this dosnt work, however it does work if I only keep one of the two WHERE's, but thats not what I want.
Two ways to do it:
1
SELECT tbl_imdb.*
FROM tbl_imdb
INNER JOIN tbl_genres_rel rel_action
ON tbl_imdb.fldID = rel_action.fldMovieID
INNER JOIN tbl_genres genre_action
ON rel_action.fldGenreId = genre_action.fldID
AND 'Action' = genre_action.fldGenre
INNER JOIN tbl_genres_rel rel_drama
ON tbl_imdb.fldID = rel_drama.fldMovieID
INNER JOIN tbl_genres genre_drama
ON rel_drama.fldGenreId = genre_drama.fldID
AND 'Drama' = genre_drama.fldGenre
This method is on the same path as your original solution. 2 differences:
The join should be inner, not left because you're trying to get movies that certainly have the corresponding genre entry
Since you want to find 2 different generes, you'll have to do the join with tbl_genres_rel and tbl_genres twice, once for each particular genre you're interested in.
2
SELECT tbl_imdb.*
FROM tbl_imdb
INNER JOIN tbl_genres_rel
ON tbl_imdb.fldID = tbl_genres_rel.fldMovieID
INNER JOIN tbl_genres
ON tbl_genres_rel.fldGenreId = tbl_genres.fldID
AND tbl_genres.fldGenre IN ('Action', 'Drama')
GROUP BY tbl_imdb.fldID
HAVING COUNT(*) = 2
Again, the basic join plan is the same. Difference here is that we join to the tbl_genres_rel and tbl_genres path just once. This on itself fetches all genres for one film, and then filters for the one's you're interested in. The ones that qualify will now have 2 rows for each distinct value of tbl_imdb.fldId. The GROUP BY aggregates on that, flattening that into one row. By asserting in the HAVING clause that we have exactly 2 rows, we ensure that we keep only those rows that have both the genres.
(Note that this assumes that there is a unique constraint on tbl_genres_rel over {fldMovieID, fldGenreID}. If such a constraint is not present, you should consider adding it.)
LEFT JOIN is not applicable in your case because records should exist on both tables. And you need to count the instances of the movie
SELECT *
FROM tbl_imdb a
INNER JOIN tbl_genres_rel b
on a.fldID = fldMovieID
INNER JOIN tbl_genres c
on c.fldGenreID = b.fldID
WHERE c.fldGenre IN ('Drama', 'Action')
GROUP BY a.Moviename
HAVING COUNT(*) > 1

Mysql scenario - Get all tasks even if there is no entry?

I have three tables
Tasks with columns Taskid, Taskname
TaskAllocations with columns Taskid, EmpNum
TaskEntries with columns TaskId, EmpNum, WorkedDate, Hoursspent
Now I want to get all the task entries along a particular week. Here my problem is even if there is no Taskentry for a particular task I should get atleast a row with that TaskId, and Taskname with Hoursspent as Null in the query's resultset. I have been trying to get this with the below query.
SELECT A.TaskId,
B.TaskName,
SUM( C.HoursSpent ) as TotalHours ,
C.WorkedDate, C.Comments
FROM TaskAllocations A
LEFT OUTER JOIN TaskEntries C
ON A.TaskId = C.TaskId
AND A.EmpNum = C.EmpNum
INNER JOIN Tasks B
ON A.TaskId = B.TaskId
WHERE A.EmpNum =123456
AND C.WorkedDate
IN ('2010-01-17','2010-01-18','2010-01-19',
'2010-01-20','2010-01-21','2010-01-22','2010-01-23' )
GROUP BY A.TaskId, C.WorkedDate
ORDER BY A.TaskId,C.WorkedDate ASC ';
What I am getting for this SQL piece is if and only if there is an entry for a particular task id, then only i am getting a row for that. but what I want is to get atleast a row for each and every task that is available to a EmpNum. Even if I get one row for each TaskId and WorkedDate combination no issues. Please help me with this. Actual intention of this is to build a HTML two dimensional table with each task entry against date and task as shown below.
---------------------------------------------------------
TaskId TaskName Sun Mon Tue Wed Thu Fri Sat
---------------------------------------------------------
18 name1 2 3 4:30 3:30
19 name2
20 name3 4 2:30
22 name4 2:30
23 name5
24 name6 1:30 6
---------------------------------------------------------
So that this can be updated by the user for each year week. First I thought of group_concat but because of performance I am using normal group by query.
Note: for a particular taskid and workeddate there will be only one entry of hoursspent.
I have almost built the frontend. Please help me to get all task ids as above even if there is no entry. Do I need to use subquery.
don't user an inner join, use a left or right join, depending which values from which tables you want.
so:
SELECT *
FROM tasks t
LEFT JOIN taskentries te
ON t.id = te.id
which is the same statement as:
SELECT *
FROM tasksentries te
RIGHT JOIN tasks t
ON te.id = t.id
will get you all tasks, even if there is no taskentry
an inner join will only select rows when there are rows in both tables, left join selects all rows from the left (first) table and matching from the other row (if there is no such row, null will be the value of all columns). right join will do the oposite: select all rows from right (second) table and matching from left.
a LEFT JOIN b is the same as b RIGHT JOIN a
After rigorous testing of different options I came up with the below solution which will give the required results for me.
SELECT Final.TaskId,
Final.TaskName,
Tmp.HoursSpent AS TotalHours,
Tmp.WorkedDate
FROM (
SELECT A.TaskId, B.TaskName, A.EmpNum
FROM TaskAllocations A
INNER JOIN
Tasks B
ON ( A.TaskId = B.TaskId )
WHERE a.empnum = "333"
)Final
LEFT OUTER JOIN (
SELECT New.TaskId, New.EmpNum, New.WorkedDate, New.HoursSpent
FROM TaskEntries New
WHERE New.WorkedDate
IN
('2010-01-17','2010-01-18','2010-01-19',
'2010-01-20','2010-01-21','2010-01-22','2010-01-23' )
OR New.WorkedDate IS NULL
AND New.EmpNum = "333"
)Tmp
ON Tmp.TaskId = Final.TaskId
AND Tmp.EmpNum = Final.EmpNum
ORDER BY Final.TaskId, Tmp.WorkedDate ASC ;
The first query of mine in the question was not working as I was putting a condition on right table's column while doing Left Outer Join. Thanks to all for the support.