prevent duplicate result when using self-join in mysql - 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.

Related

MYSQL If at least one result does not match, remove it from

I need to do a very complex search
Context: I have 3 tables: rooms, reservation, and room_reservation. The first one have all info of the rooms in a hotel, the second have info of all reservation and the third is because a reservation have multiple rooms.
I need know what rooms are available in a expecific date, I really close, I need that if at least one data does not match the condition of the search then the related room is not shown.
This is my query:
SELECT DISTINCT hab.* FROM habitacion hab
LEFT OUTER JOIN habitacion_reserva habr ON habr.id_habitacion = hab.id
LEFT OUTER JOIN reserva res ON res.id = habr.id_reserva
WHERE hab.tipo = 1
AND (((( '2018-06-10' not between res.fecha_ingreso and res.fecha_salida) AND ( '2018-06-17' not between res.fecha_ingreso and res.fecha_salida))
OR (res.fecha_ingreso is null OR res.fecha_salida is null)) OR ((( '2018-06-09' between res.fecha_ingreso and res.fecha_salida) OR ( '2018-06-17' between res.fecha_ingreso and res.fecha_salida)) AND res.estado = 4))
In theory the query works but when I have many reservation, the query simply return all rooms.
Try something like this (simplified)
Select * FROM rooms
LEFT JOIN room_reservation ON (rooms.id = rooms_reservation.id)
LEFT JOIN reservations ON (rooms_reservation.id = reservation.id)
reservation
WHERE 1=1
...
AND whateverfield = 1
....
AND ('2018-06-09' NOT BETWEEN reservation.start AND reservation.end)
AND ('2018-06-17' NOT BETWEEN reservation.start AND reservation.end)
GROUP BY rooms

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

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

Cross Referencing Multiple Tables

What I was trying to do is to get data from multiple tables, supposed that I have the following results in my query:
The numbers in the column ticket_item_type represents certain table. For example, 2 is for tbl_company and 3 is for tbl_lease. Then the details represents the id of a certain record in that table.
Suppose that I want to get the title of those records using ticket_item_type and details. Is it possible to embed it to the results? Or should I make separate queries for each.
I know JOIN, but I is it only for single table?
Here's my MYSQL query for the image above:
SELECT *
FROM
(SELECT *
FROM ticket_items
WHERE hs_customer = 1
AND ticket IN
(SELECT id
FROM tickets
WHERE hs_customer='1'
AND ticket_status = 'dispatch_reviewed')
AND ticket IN
(SELECT ticket
FROM ticket_items
WHERE ticket_item_type = 5
AND details = '159')) AS TB1
WHERE ticket_item_type IN (3,
2,
8)
You could try something like this:
SELECT
TB1.*,
CASE
WHEN TB1.ticket_item_type = 2 THEN t2.title
WHEN TB1.ticket_item_type = 3 THEN t3.title
WHEN TB1.ticket_item_type = 8 THEN t8.title
ELSE 'NA'
END as title
FROM
(
SELECT *
FROM ticket_items
WHERE hs_customer = 1
AND ticket IN (SELECT id FROM tickets WHERE hs_customer='1' AND ticket_status = 'dispatch_reviewed')
AND ticket IN (SELECT ticket FROM ticket_items WHERE ticket_item_type = 5 AND details = '159')
) AS TB1
LEFT JOIN tbl_company t2 ON TB1.details = t2.id
LEFT JOIN tbl_lease t3 ON TB1.details = t3.id
LEFT JOIN tbl_next t8 ON TB1.details = t8.id
WHERE ticket_item_type IN (3, 2, 8)
However, this is not a design that I would prefer. Without looking at details of your database it's going to be hard to write a query to cover multiple types of ticket_item_type. I hope this query works for you, though.

How to left join or inner join a table itself

I have this data in a table, for instance,
id name parent parent_id
1 add self 100
2 manage null 100
3 add 10 200
4 manage null 200
5 add 20 300
6 manage null 300
How can I left join or inner join this table itself so I get this result below?
id name parent
2 manage self
4 manage 10
6 manage 20
As you can I that I just want to query the row with the keyword of 'manage' but I want the column parent's data in add's row as the as in manage's row in the result.
Is it possible?
EDIT:
the simplified version of my actual table - system,
system_id parent_id type function_name name main_parent make_accessible sort
31 30 left main Main NULL 0 1
32 31 left page_main_add Add self 0 1
33 31 left page_main_manage Manage NULL 0 2
my actual query and it is quite messy already...
SELECT
a.system_id,
a.main_parent,
b.name,
b.make_accessible,
b.sort
FROM system AS a
INNER JOIN -- self --
(
SELECT system_id, name, make_accessible, sort
FROM system AS s2
LEFT JOIN -- search --
(
SELECT system_id AS parent_id
FROM system AS s1
WHERE s1.function_name = 'page'
) AS s1
ON s1.parent_id = s2.parent_id
WHERE s2.parent_id = s1.parent_id
AND s2.system_id != s1.parent_id
ORDER BY s2.sort ASC
) b
ON b.system_id = a.parent_id
WHERE a.function_name LIKE '%manage%'
ORDER BY b.sort ASC
result I get currently,
system_id main_parent name make_accessible sort
33 NULL Main 0 1
but I am after this,
system_id main_parent name make_accessible sort
33 self Main 0 1
You just need to reference the table twice:
select t1.id, t1.name, t2.id, t2.name
from TableA t1
inner join TableA t2
on t1.parent_id = t2.Id
Replace inner with left join if you want to see roots in the list.
UPDATE:
I misread your question. It seems to me that you always have two rows, manage one and add one. To get to "Add" from manage:
select system.*, (select parent
from system s2
where s2.parent_id = system.parent_id
and s2.name = 'add')
AS parent
from system
where name = 'manage'
Or, you might split the table into two derived tables and join them by parent_id:
select *
from system
inner join
(
select * from system where name = 'add'
) s2
on system.parent_id = s2.parent_id
where system.name = 'manage'
This will allow you to use all the columns from s2.
Your data does not abide to a child-parent hierarchical structure. For example, your column parent holds the value 10, which is not the value of any id, so a child-parent association is not possible.
In other words, there's nothing that relates the record 2,manage,null to the record 1,add,self, or the record 4,manage,null to 3,add,10, as you intend to do in your query.
To represent hierarchical data, you usually need a table that has a foreign key referencing it's own primary key. So your column parent must reference the column id, then you can express a child-parent relationship between manage and add. Currently, that's not possible.
UPDATED: Joining by parent_id, try:
select m.id, m.name, a.parent
from myTable m
join myTable a on m.parent_id = a.parent_id and a.name = 'add'
where m.name = 'manage'
Change the inner join to a left join if there may not be a corresponding add row.

I need to finalize this MySQL multiple table JOIN

I have entires, equipments, brands, times and seasons.
entries:
id
time
equipment_1
equipment_2
equipments:
id
id_brand
brands:
id
name
times:
id
id_season
seasons:
id
name
My actual SQL query is:
SELECT entries.*, times.id_season AS id_season
FROM entries, seasons
WHERE entries.time = times.id
But in the final query I need the next information that I don't know how to obtain it:
The name for each entries.equipment_ as equipment_1_name and equipment_2_name which is set in brands.name.
The name of the season as season_name.
Thank you in advance!
Assuming you have normalized data. This avoid costly cartesian joins. I never use cartesian joins myself, although there are some cases where they are useful. Not here, though.
SELECT
entries.*,
times.id_seasons AS id_season,
b1.name AS equipment_1_name,
b2.name AS equipment_2_name,
seasons.name AS season_name
FROM entries
LEFT JOIN equipments AS equipments_1
ON equipments_1.id = entries.equipment_1
LEFT JOIN brands AS brands_1
ON brands_1.id = equipments_1.id_brand
LEFT JOIN equipments AS equipments_2
ON equipments_2.id = entries.equipment_2
LEFT JOIN brands AS brands_2
ON brands_2.id = equipments_2.id_brand
LEFT JOIN times
ON times.id = entries.time
LEFT JOIN seasons
ON seasons.id = times.id_season;