MySQL Query Join 2 tables with 2 relationship tables - mysql

I have a real mindbender of a MySQL problem which I am now thinking there is no answer to. Please help me, you are my only hope!
Stripping it down to the basics, I have two tables, "People" and "Activity". It is possible (long story and lots of data involved) for these two tables to be joined by two different relationship tables: people_activity and entity_activity
I need to do a query on the activity table which gets the people record/s linked to activity records based on both relationship tables.
This is what I have, but it is massively slow on lots of data:
select * from activity
left join peopleactivity on peopleactivity.activityid = activity.activityid
left join entityactivity on entityactivity.activityid = activity.activityid
left join people on (peopleactivity.peopleid = people.peopleid OR
entityactivity.entityid = people.peopleid)
Some more notes - I have also tried creating a view to combine the results of the two relationship tables and instead joining people and activity via this view. This also works, but is also still massively slow
Changing how the relationship/s work to consolodate to one table is a major headache
I have also tried a union -like this -
select * from activity
left join peopleactivity on peopleactivity.activityid = activity.activityid
left join people on (peopleactivity.peopleid = people.peopleid)
union
select * from activity
left join peopleactivity on peopleactivity.activityid = activity.activityid
left join people on (entityactivity.entityid= people.peopleid)
which also works, but for other reasons causes me problems. I really need to do this in one query without changing too much underlying.
Has anyone got any super amazing ideas that I have missed??!

You may try to replace OR with IN
left join people on people.peopleid IN (peopleactivity.peopleid, entityactivity.entityid)

1.) Try setting the id of the tables as the primary key on each table
2.) Use inner joins instead of left joins. Not sure why you are using left joins here as you will get all the results of the other tables left joined on the activity table and get basically all records whether or not they have a join value in another table. I think this might also help you. Can you post a describe of your tables.

I think you should keep the UNION query but making those INNER joins. Do you really need LEFT joins?
You could also change it into UNION ALL, which will have some performance gain:
SELECT activity.*, people.*, 'PA' AS joining_table
FROM activity
JOIN peopleactivity ON peopleactivity.activityid = activity.activityid
JOIN people ON peopleactivity.peopleid = people.peopleid
UNION ALL
SELECT activity.*, people.*, 'EA'
FROM activity
JOIN entityactivity ON entityactivity.activityid = activity.activityid
JOIN people ON entityactivity.entityid = people.peopleid

Thanks for the comments. I had tried various incarnations of the above. My answer was to set up a new table, copy all the existing links into that table, and then use triggers to add/remove links to that table whenever the links were added removed in the two separate link tables. This works well and also allows me to use indexes on this new table to keep things nice and snappy. Many thanks for those that took the time to post the ideas though!

Related

mysql - How to perform joining of of two junctional tables in case where one of them has foreign key of another?

I am new in the database design, so I am still learning, so sorry for maybe inappropriate terms using, so I will try to explain on common language what problem I have. I learned how to join two tables (getting result) over junction table which is in between, but I got into problem when I want to join one "regular" table and one junction table over another junction table.
I have a relational database which has tables and relations between them like this:
I know how to join hgs_transliterations, hgs_gardiners, hgs_meanings, hgs_word_types using hgs_translations, but what I don't know how to do is how to join those 4 tables and the hgs_references table.
This is my code for joining lower 4 tables:
SELECT hgs_transliterations.transliteration, hgs_gardiners.gardiners_code, hgs_meanings.meaning, hgs_word_types.word_type
FROM hgs_translations
JOIN hgs_transliterations ON hgs_translations.transliteration_id = hgs_transliterations.id
JOIN hgs_gardiners ON hgs_translations.gardiners_id = hgs_gardiners.id
JOIN hgs_meanings ON hgs_translations.meaning_id = hgs_meanings.id
JOIN hgs_word_types ON hgs_translations.word_type_id = hgs_word_types.id
I read some tutorials on this subject which mention AS, INNER JOIN, OUTER JOIN, but I didn't quite understand terminology and how I can use this to create what I need. Sorry for maybe basic questions, but as I say, I am just a beginner and I am trying to understand something deeply so I can use it appropriately. Thank you in advance.
P.S. If someone thinks that this is not good database design (design of relations between tables), I would like to hear that.
Just add two more joins:
SELECT hgs_transliterations.transliteration, hgs_gardiners.gardiners_code,
hgs_meanings.meaning, hgs_word_types.word_type,
hgs_references.reference
FROM hgs_translations
JOIN hgs_transliterations ON hgs_translations.transliteration_id = hgs_transliterations.id
JOIN hgs_gardiners ON hgs_translations.gardiners_id = hgs_gardiners.id
JOIN hgs_meanings ON hgs_translations.meaning_id = hgs_meanings.id
JOIN hgs_word_types ON hgs_translations.word_type_id = hgs_word_types.id
JOIN junc_translation_reference ON junc_translation_reference.translation_id = hgs_translations.id
JOIN hgs_references ON hgs_references.id = junc_translation_reference.reference_id

comparing two table columns in mysql results in duplicate

I have two tables, I've been trying to print the result from each but they are being duplicated. These are the two MySQL tables and the result. Notice the duplication.
The sql code for the project is:
SELECT * FROM savings,savtype WHERE cust_id=".$_SESSION['user']
I'm also looking for a work around this, in the meantime, id appreciate any assistance on this.
because you are not specifying how the two tables are related. You need to add that, either via an explicit ... JOIN ... (USING|ON)
SELECT
*
FROM
savings JOIN savtype USING (savtype_id)
WHERE
cust_id = ".$_SESSION['user']
or by providing the criteria in the where clause.
SELECT
*
FROM
savings, savtype
WHERE
savings.savtype_id = savtype.savtype_id AND
cust_id = ".$_SESSION['user']
As I understand from the screenshot you added, it makes joint between those tables, and what you probably want it left join from savings and savtype tables.
SELECT *
FROM `savings`
LEFT JOIN `savtype`
ON savings.savtype_id=savtype.savtype_id
where cust_id=".$_SESSION['user'] .";
Update if this did the trick,
You can learn more about left join here: https://www.w3schools.com/sql/sql_join_left.asp

MySQL - how to speed up or change this query

I did not write this query. I am working on someone else's old code. I am looking into changing what is needed for this query but if I could simply speed up this query that would solve my problem temporarily. I am looking at adding indexes. when I did a show indexes there are so many indexes on the table orders can that also slow down a query?
I am no database expert. I guess I will learn more from this effort. :)
SELECT
orders.ORD_ID,
orders.ORD_TotalAmt,
orders.PAYMETH_ID,
orders.SCHOOL_ID,
orders.ORD_AddedOn,
orders.AMAZON_PurchaseDate,
orders.ORDSTATUS_ID,
orders.ORD_InvoiceNumber,
orders.ORD_CustFirstName,
orders.ORD_CustLastName,
orders.AMAZON_ORD_ID,
orders.ORD_TrackingNumber,
orders.ORD_SHIPPINGCNTRY_ID,
orders.AMAZON_IsExpedited,
orders.ORD_ShippingStreet1,
orders.ORD_ShippingStreet2,
orders.ORD_ShippingCity,
orders.ORD_ShippingStateProv,
orders.ORD_ShippingZipPostalCode,
orders.CUST_ID,
orders.ORD_ShippingName,
orders.AMAZON_ShipOption,
orders.ORD_ShipLabelGenOn,
orders.ORD_SHIPLABELGEN,
orders.ORD_AddressVerified,
orders.ORD_IsResidential,
orderstatuses.ORDSTATUS_Name,
paymentmethods.PAYMETH_Name,
shippingoptions.SHIPOPT_Name,
SUM(orderitems.ORDITEM_Qty) AS ORD_ItemCnt,
SUM(orderitems.ORDITEM_Weight * orderitems.ORDITEM_Qty) AS ORD_ItemTotalWeight
FROM
orders
LEFT JOIN orderstatuses ON
orders.ORDSTATUS_ID = orderstatuses.ORDSTATUS_ID
LEFT JOIN orderitems ON
orders.ORD_ID = orderitems.ORD_ID
LEFT JOIN paymentmethods ON
orders.PAYMETH_ID = paymentmethods.PAYMETH_ID
LEFT JOIN shippingoptions ON
orders.SHIPOPT_ID = shippingoptions.SHIPOPT_ID
WHERE
(orders.AMAZON_ORD_ID IS NOT NULL AND (orders.ORD_SHIPLABELGEN IS NULL OR orders.ORD_SHIPLABELGEN = '') AND orderstatuses.ORDSTATUS_ID <> 101 AND orderstatuses.ORDSTATUS_ID <> 40)
GROUP BY
orders.ORD_ID,
orders.ORD_TotalAmt,
orders.PAYMETH_ID,
orders.SCHOOL_ID,
orders.ORD_AddedOn,
orders.ORDSTATUS_ID,
orders.ORD_InvoiceNumber,
orders.ORD_CustFirstName,
orders.ORD_CustLastName,
orderstatuses.ORDSTATUS_Name,
paymentmethods.PAYMETH_Name,
shippingoptions.SHIPOPT_Name
ORDER BY
orders.ORD_ID
One simple thing you should consider is whether you really need to use left joins or you would be satisfied using inner joins for some of the joins. the new query would not be the same as the original query, so you would need to think carefully about what you really want back. If your foreign key relationships are indexed correctly, this could help substantially, especially between ORDERS and ORDERITEMS, because I would imagine these are your largest tables. The following post has a good explanation: INNER JOIN vs LEFT JOIN performance in SQL Server. There are lots of other things that can be done, but you will need to post the query plan so people can dive deeper.
It looks like just adding the index was all that was needed.
create index orderitems_ORD_ID_index on orderitems(ORD_ID);

How to select from database with relations?

I have database with schema on picture below and I need to select everything related to one row (one id) of [letaky]. That means the related [zamestnanci], every related [obsah] and every [knihy] in it.
This is the first time i used relations in database and i have no idea how to make such a select.
Use JOIN ... ON:
SELECT *
FROM zamestnanci
JOIN lekaty ON lekaty.zamestnanciid = zamestnanci.id
JOIN obsah ON obsah.idletaku = lekaty.id
JOIN knihy ON knihy.id = obsah.idknihy
WHERE letaky.id = 123
You may also want to consider whether you need INNER JOIN, LEFT JOIN or RIGHT JOIN for each of these joins. The difference between these JOINs is described in many other questions on StackOverflow, for example this one:
SQL Join Differences

MySQL -- joining then joining then joining again

MySQL setup: step by step.
programs -> linked to --> speakers (by program_id)
At this point, it's easy for me to query all the data:
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
Nice and easy.
The trick for me is this. My speakers table is also linked to a third table, "books." So in the "speakers" table, I have "book_id" and in the "books" table, the book_id is linked to a name.
I've tried this (including a WHERE you'll notice):
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
LIMIT 5
No results.
My questions:
What am I doing wrong?
What's the most efficient way to make this query?
Basically, I want to get back all the programs data and the books data, but instead of the book_id, I need it to come back as the book name (from the 3rd table).
Thanks in advance for your help.
UPDATE:
(rather than opening a brand new question)
The left join worked for me. However, I have a new problem. Multiple books can be assigned to a single speaker.
Using the left join, returns two rows!! What do I need to add to return only a single row, but separate the two books.
is there any chance that the books table doesn't have any matching columns for speakers.book_id?
Try using a left join which will still return the program/speaker combinations, even if there are no matches in books.
SELECT *
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
LEFT JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
LIMIT 5
Btw, could you post the table schemas for all tables involved, and exactly what output (or reasonable representation) you'd expect to get?
Edit: Response to op author comment
you can use group by and group_concat to put all the books on one row.
e.g.
SELECT speakers.speaker_id,
speakers.speaker_name,
programs.program_id,
programs.program_name,
group_concat(books.book_name)
FROM programs
JOIN speakers on programs.program_id = speakers.program_id
LEFT JOIN books on speakers.book_id = books.book_id
WHERE programs.category_id = 1
GROUP BY speakers.id
LIMIT 5
Note: since I don't know the exact column names, these may be off
That's typically efficient. There is some kind of assumption you are making that isn't true. Do your speakers have books assigned? If they don't that last JOIN should be a LEFT JOIN.
This kind of query is typically pretty efficient, since you almost certainly have primary keys as indexes. The main issue would be whether your indexes are covering (which is more likely to occur if you don't use SELECT *, but instead select only the columns you need).