I have 2 tables: Equipment, and Equipment_Type:
+-------------------------------+
| EqId [PK] | ETId [FK] | EqNum |
+-------------------------------+
| 1 | 1 | ABC |
| 2 | 1 | DEF |
| 3 | 3 | GHI |
+-------------------------------+
| ETId [PK] | Code | Discipline |
+-------------------------------+
| 1 | MOT | ELEC |
| 2 | MOT | MECH |
| 3 | SW | ELEC |
So from this example, we can see that both of our equipment are electrical motors.
However, due to a misunderstanding in the initial population, all of the equipment types were identified a ELEC disciplines. Since then, the MECH equipment has been identified, and I have to find all of the equipment that has been duplicated in the Equipment_Type table, and change them to reference the MECH equipment types instead.
I tried this:
SELECT * FROM Equipment EQ
INNER JOIN Equipment_Type ET on ET.ETId = EQ.ETId
WHERE ET.Discipline = 'MECH';
Which (obviously) returns no results - as with all the other JOIN queries.
What I want to achieve is a search that will select only the Equipment that has an ELEC Equipment Type that is also a MECH equipment type. I realise this requires a nested query, but I'm not sure where to place it.
So the search should return:
+---------------------------+
| EqNum | ETId | Discipline |
+---------------------------+
| DEF | 1 | ELEC |
Because that entry needs to be changed to the MECH discipline (i.e. ETId = 2 instead of 1)
Here is one method that aggregates the types to get the codes that have both disciplines:
select e.*
from equipment e join
equipment_type et
on e.etid = et.etid join
(select et.code
from equipment_type et
group by et.code
having sum(discipline = 'MECH') > 0 and sum(discipline = 'ELEC') > 0
) ett
on ett.code = et.code;
Another method would use two joins:
select e.*
from equipment e join
equipment_type ete
on e.etid = ete.etid and ete.discipline = 'ELEC' join
equipement_type etm
on ete.code = etm.code and etm.discipline = 'MECH';
This version might be faster with the right indexes.
Do it like this:
select eq_id, eq_name,et_id,description from
(select eq_id,eq_name from equipment) as a
left join
(select et_id,description from equipment_type) as b
on a.eq_id = b.et_id where description = 'ELEC';
IF YOU WANT TO INCLUDE 'MECH';
select eq_id, eq_name,et_id,description from
(select eq_id,eq_name from equipment) as a
left join
(select et_id,description from equipment_type) as b
on a.eq_id = b.et_id where description = 'ELEC' or description = 'MECH';
CHANGE 'ELEC' TO 'MECH':
select eq_id, eq_name,et_id,
case when description = 'ELEC' then 'MECH' else description end as description,
'MECH' as MECH_FIELD from
(select eq_id,eq_name from equipment) as a
left join
(select et_id,description from equipment_type) as b
on a.eq_id = b.et_id where description = 'ELEC' or description = 'MECH';
Related
What is the right way to select films which labels are 'Action' AND 'Drama' using INNER JOIN ?
I've tried this query, the result must be 'Taken, The Godfather' but, no result returned.
SELECT
f.film_guid,
f.film_name
FROM
films as f
INNER JOIN
film_labels as l ON l.film_guid = f.film_guid
WHERE
l.label = 'Action' AND l.label = 'Drama'
Table: films
+------------+----------------+
| film_guid | film_name |
+------------+----------------+
| filmguid_1 | Taken |
| filmguid_2 | Matrix |
| filmguid_3 | The Godfather |
+------------+----------------+
Table: film_labels
+------------+----------------+
| film_guid | label |
+------------+----------------+
| filmguid_1 | Action |
| filmguid_1 | Drama |
| filmguid_1 | Family |
| filmguid_2 | Action |
| filmguid_3 | Action |
| filmguid_3 | Drama |
+------------+----------------+
You are looking for a rows in film_labels that contains both Action and Drama, which cannot happen. You need to search across labels that correspond to the given film, which suggest aggregation:
SELECT f.film_guid, f.film_name
FROM films as f
INNER JOIN film_labels as l ON l.film_guid = f.film_guid
WHERE l.label IN ('Action', 'Drama') -- either one, or the other
GROUP BY f.film_guid, f.film_name
HAVING COUNT(*) = 2 -- both match
Note that you could also use exists with correlated subquery. It is a bit longer to type but could be more efficient (with the right indexes in place), since it avoids the need for aggregation:
SELECT f.*
FROM films as f
WHERE
EXISTS (SELECT 1 FROM film_labels l WHERE l.film_guid = f.film_guid AND l.label = 'Action')
AND EXISTS (SELECT 1 FROM film_labels l WHERE l.film_guid = f.film_guid AND l.label = 'Drama')
For performance with the second query, you want an index on film_labels(film_guid , label).
I have two tables:
person
+-----+------------+---------------+
| id | name | address |
+-----+------------+---------------+
| 1 | John Smith | 123 North St. |
| 2 | Joe Dirt | 456 South St. |
+-----+------------+---------------+
person_fields
+-----+------------+-----------+-------+
| id | type | person_id | value |
+-----+------------+-----------+-------+
| 1 | isHappy | 1 | 1 |
| 2 | hasFriends | 1 | 1 |
| 3 | hasFriends | 2 | 1 |
I want to select all the people from person for whom isHappy AND hasFriends is TRUE. Here's what I have tried:
SELECT person.*
FROM person
INNER JOIN person_fields
ON person.id = person_fields.person_id
WHERE
(person_fields.type = 'isHappy' AND person_fields.value IS TRUE)
AND
(person_fields.type = 'hasFriends' AND person_fields.value IS TRUE)
Unfortunately, this does not work because you can't have a single record in person_fields that has type = 'isHappy' AND type = 'hasFriends'. I can't OR these two conditions because that would return both John Smith and Joe Dirt, but I only want John Smith because he is the only one who is happy and has friends at the same time.
Any suggestions? Thanks in advance!
The standard solution looks like this:
SELECT person_id
FROM person_fields
WHERE type IN ('ishappy','hasfriends')
GROUP
BY person_id
HAVING COUNT(1) = 2;
...where '2' is equal to the number of arguments in IN()
Note that this assumes that (person_id,type) is UNIQUE
By joining twice:
SELECT person.*
FROM person
INNER JOIN person_fields happy
ON person.id = happy.person_id AND happy.type='isHappy' AND happy.value
INNER JOIN person_fields friends
ON person.id = friends.person_id AND friends.type='hasFriends' AND friends.value
You can join person_fields in twice, once for isHappy and once forhasFriends.
SELECT p.*
FROM person p
INNER JOIN person_fields f1 ON p.id = f1.person_id
INNER JOIN person_fields f2 ON f1.person_id = f2.person_id
WHERE f1.type = 'isHappy' AND f2.type = 'hasFriends'
I'm not sure where the value field comes into this but you can throw an extra condition OR two in if you need it
AND f1.value = 1 AND f2.value = 1
If you always know how many conditions you want to check for, and you always want to only return the person records who have all conditions, then you can use a sub-query and modify the HAVING clause to select the maximum number of type from person_fields:
SELECT
p.id,
p.name,
(SELECT COUNT(DISTINCT pf.type) FROM person_fields AS pf WHERE pf.person_id = p.id and pf.value = true) AS types
FROM
person AS p
HAVING types = 2
Result:
id name types
1 John Smith 2
I'm stuck on this for hours, I'm trying to COUNT how many subscribers are there in Group A, Group B, Group C for this particular query:
SELECT rh.id_subscriber, rh.bill_month, rh.bill_year,
(
SELECT tbl_gen_info.gen_data_03
FROM tbl_subscriber
LEFT JOIN tbl_gen_info ON tbl_subscriber.bill_area_code = tbl_gen_info.gen_data_01
WHERE rh.id_subscriber = tbl_subscriber.id_subscriber
) AS group_area
FROM tbl_reading_head AS rh
WHERE rh.id_soa_head IS NULL
AND rh.read_status <> 'Beginning'
AND rh.rec_status = 'active'
ORDER BY rh.id_subscriber
The sub-query gets the Group area gen_data_03 from tbl_gen_info
Tables contain this information:
tbl_gen_info
--------------------------------------------
| gen_category | gen_data_01 | gen_data_03 |
--------------------------------------------
| Area Code | Camacho St. | Group A |
--------------------------------------------
tbl_subscriber
----------------------------------
| id_subscriber | bill_area_code |
----------------------------------
| 1 | Camacho St. |
----------------------------------
tbl_reading_head
----------------------------------------------------------------------
| id_subscriber | id_soa_head | read_status | bill_month | bill_year |
----------------------------------------------------------------------
| 1 | NULL | Metered | 10 | 2017 |
----------------------------------------------------------------------
Notice that each id_subscriber has two (2) rows (one for electric, one for water). After grouping by id_subscriber:
GROUP BY rh.id_subscriber
I got this:
I tried adding COUNT before the sub-query making it:
COUNT(SELECT tbl_gen_info.gen_data_03 ...) AS group_area
but that doesn't work.
Use a subquery:
SELECT rh.group_area, COUNT(*)
FROM (SELECT rh.id_subscriber, rh.bill_month, rh.bill_year,
(SELECT tbl_gen_info.gen_data_03
FROM tbl_subscriber LEFT JOIN
tbl_gen_info
ON tbl_subscriber.bill_area_code = tbl_gen_info.gen_data_01
WHERE rh.id_subscriber = tbl_subscriber.id_subscriber
) as group_area
FROM tbl_reading_head rh
WHERE rh.id_soa_head IS NULL AND
rh.read_status <> 'Beginning' AND
rh.rec_status = 'active'
) rh
GROUP BY rh.group_area;
I want to join 5 tables through book_id. I tried:
SELECT booked_room_info . * , booking_info . * , hotel_info.name as
hotelname,hotel_info.address as hoteladdress,hotel_info.contact as
hotelcontact , personal_info . *,room_registration.room_name as
roomname,room_registration.price as roomprice,room_registration.price as
roomprice,room_registration.description as
roomdescription,room_registration.image as roomimage
FROM booked_room_info
JOIN booking_info ON booking_info.booking_id = booked_room_info.booking_id
JOIN hotel_info ON hotel_info.id = booking_info.hotel_id
JOIN personal_info ON personal_info.booking_id = booked_room_info.booking_id
JOIN room_registration ON room_registration.hotel_id = booking_info.hotel_id
WHERE booked_room_info.booking_id= 1
But if i have 2 booking on booking_id = 1 but it fetched 10 result. I think I should make a if condition like. If (booked.room_type && book_info.hotel_id) is same then only fetch rows from room_registration but how can I aacomplish it.
booked_room_info
------
id | booking_id | room_type | check_in | check_out
1 | 1 | delux | 2015/1/2 | 2015/1/5
booking_info
---------
id | booking_id | hotel_id | user_id
1 | 1 | 2 | 1
hotel_info
----------
id | name | address | user_id
2 |palm hotel | newyork | 1
personal_info
-------------
id |full_name | address | nationality | booking_id
1 | sushil stha | new york | smth | 1
room_registration
-----------------
id | room_name | price | image | hotel_id | user_id
1 | delux | 1000 |room.jpg | 2 | 1
There is an error in your last join:
JOIN room_registration ON room_registration.hotel_id = booking_info.hotel_id
You are joining these tables using hotel_id column. There are probably 5 rows in room_registration table with hotel_id=2. So after joining you receive 2*5=10 rows instead of expected 2.
So you should join with this table in different way.
If you want to join every row with exacly one row from room_registration table then you have to specify more precise join condition.
To join room_registration table properly you may add room_registration_id column to booking_info table. Then you can join using:
JOIN room_registration ON room_registration.id = booking_info.room_registration_id
Use INNER JOIN's, removed the duplicate field.
SELECT
bri.*,
bi.*,
hi.name AS hotelname, hi.address as hoteladdress, hi.contact AS hotelcontact,
pi.*,
rr.room_name AS roomname, rr.price AS roomprice, rr.description AS roomdescription, rr.image AS roomimage
FROM booked_room_info bri
INNER JOIN booking_info bi ON bri.booking_id = bi.booking_id
INNER JOIN hotel_info hi ON bi.hotel_id = hi.id
INNER JOIN personal_info pi ON bri.booking_id = pi.booking_id
INNER JOIN room_registration rr ON bi.hotel_id = rr.hotel_id
WHERE bri.booking_id = 1
So I want to do the following for a project.
I have 3 tables. First two concern us now (the third is for your better understanding):
author {id, name}
authorship {id, id1, id2}
paper {id, title}
authorship connects author with paper and authorship.id1 refers to author.id, authorship.id2 refers to paper.id.
What I want to do is make a graph with a node for each author and edge that is determined by the amount of common papers between two authors.
w=1 - union_of_common_papers/intersection_of_common_papers
So what I have built (with some help from stackoverflow) an sql script that returns all couples of co-authors plus the amount of union and intersection of common papers. After that I will use the data with java. It's the following:
SELECT DISTINCT a1.name, a2.name, (
SELECT concat(count(a.id2), ',', count(DISTINCT a.id2))
FROM authorship a
WHERE a.id1=a1.id or a.id1=a2.id) as weight
FROM authorship au1
INNER JOIN authorship au2 ON au1.id2 = au2.id2 AND au1.id1 <> au2.id1
INNER JOIN author a1 ON au1.id1 = a1.id
INNER JOIN author a2 ON au2.id1 = a2.id;
this does my job and returns a list like:
+-----------------+---------------------+---------+
| name | name | weight |
+-----------------+---------------------+---------+
| Kurt | Michael | 161,157 |
| Kurt | Miron | 138,134 |
| Kurt | Manish | 19,18 |
| Roy | Gregory | 21,20 |
| Roy | Richard | 74,71 |
....
where in weight I can see 2 numbers a,b where b is intersection an b-a is the union of the common papers.
but this takes a lot of time.
And all the overhead is by this extra subselect
(SELECT concat(count(a.id2), ',', count(DISTINCT a.id2))
FROM authorship a
WHERE a.id1=a1.id or a.id1=a2.id) as weight
without this line all records (1M+) were returned in less than 2mins.
with this line 50 records need more than 1.5mins
I use mysql on linux through command line
Any ideas how I can optimize it?
author has ~130,000 records
authorship ~1,300,000 records
query should return ~1,200,000 records
This is what explain returns for this query. don't know how to use it.
+----+--------------------+-------+--------+---------------------+-----------+---------+--------------+---------+-----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+---------------------+-----------+---------+--------------+---------+-----------------+
| 1 | PRIMARY | a1 | ALL | PRIMARY | NULL | NULL | NULL | 124768 | Using temporary |
| 1 | PRIMARY | au1 | ref | NewIndex1,NewIndex2 | NewIndex1 | 5 | dblp.a1.ID | 4 | Using where |
| 1 | PRIMARY | au2 | ref | NewIndex1,NewIndex2 | NewIndex2 | 5 | dblp.au1.id2 | 1 | Using where |
| 1 | PRIMARY | a2 | eq_ref | PRIMARY | PRIMARY | 4 | dblp.au2.id1 | 1 | |
| 2 | DEPENDENT SUBQUERY | a | ALL | NewIndex1 | NULL | NULL | NULL | 1268557 | Using where |
+----+--------------------+-------+--------+---------------------+-----------+---------+--------------+---------+-----------------+
You should be able to get your data directly from the joins in the outer query.
You can count the number of papers in common by counting the distinct id2 where that is the same for both authors.
You can count the total number of papers as the number of distinct papers for each author minus the ones in common (because otherwise, these would be counted twice):
SELECT a1.name, a2.name,
COUNT(distinct case when au1.id2 = au2.id2 then au1.id2 end) as CommonPapers,
COUNT(distinct au1.id2) + COUNT(distinct au2.id2) - COUNT(distinct case when au1.id2 = au2.id2 then au1.id2 end) as TotalPapers
FROM authorship au1 INNER JOIN
authorship au2
ON au1.id2 = au2.id2 AND au1.id1 <> au2.id1 INNER JOIN
author a1
ON au1.id1 = a1.id INNER JOIN
author a2
ON au2.id1 = a2.id
group by a1.name, a2.name;
In your data structure, id1 and id2 are lousy names. Have you considered something like idauthor and idpaper or something like that?
The above query counts the intersection correctly, but not the total, because of the initial inner join. one way around this is a full outer join, but that is not allowed in MySQL. We can do this with additional subqueries:
SELECT a1.name, a2.name,
COUNT(distinct case when au1.id2 = au2.id2 then au1.id2 end) as CommonPapers,
(ap1.NumPapers + ap2.NumPapers - COUNT(distinct case when au1.id2 = au2.id2 then au1.id2 end)
) as TotalPapers
FROM authorship au1 INNER JOIN
authorship au2
ON au1.id2 = au2.id2 AND au1.id1 <> au2.id1 INNER JOIN
author a1
ON au1.id1 = a1.id INNER JOIN
author a2
ON au2.id1 = a2.id inner join
(select au.id1, count(*) as numpapers
from authorship au
) ap1
on ap1.id1 = au1.id1 inner join
(select au.id1, count(*) as numpapers
from authorship au
) ap2
on ap2.id1 = au2.id1 inner join
group by a1.name, a2.name;