Coming up with MySQL query on multiple tables - mysql

I need to come up with a query which I am finding to be a bit complex.
This is my database structure:
The user will add/enter filters, and depending on the filters, there will be displayed correspondent Collections of magazines and newspapers.
The possible filters are:
Magazine/Newspaper name
fromDate
toDate
Select if the results are only from Magazine or Newspaper or both
If the Collection has been analysed or not
Well, suposing that the user has entered a name, both dates, both magazine and newspaper and only collections that have been analysed, how would the query look like? I would like to display the collectionDate, newspaper/magazine name, from and to dates.
I have been trying to figure out the query but I am struggling a bit.
Does anyone have suggestions? Hope that everything is clear :)
Thanks!

SELECT m.name as mag_name,
n.name as news_paper_name,
c.fromDate,
c.toDate
FROM Collections AS C
INNER JOIN Target AS T ON
C.TargetID = T.TargetID
INNER JOIN Magazine AS M ON
T.TargetID = M.id
INNER JOIN Newspaper AS N ON
T.TargerID = N.id
INNER JOIN Analysis AS A ON
C.CollectionID = A.CollectionID
This should work from a joins perspective. I am assuming that when something is anaylzed that there is a record under Analysis and not one when it has been anaylzed. Just add a where clause and whatever information you want to filter by.

Related

Single SQL query on many to many relationship

I have a simple database with few tables (and some sample columns):
game (gm_id , game_name , company , desc)
plateform_master (p_id , plateform_name)
plateform_details (pd_id, gm_id, p_id, release_date)
Is there a way to create single SQL query which will return all game details with multiple plateform
Your question is not very clear. To which posts and categories are you referring yourself?
Here an example based on what I understood: Select all records from plateform_master and, for each of them, get its details from plateform_details. You can uncomment the where part in order to get one master record with its details. ("--" means comment in SQL)
select
pm.p_id
pd.*
from plateform_master as pm
inner join plateform_details as pd on pm.p_id = pd.p_id
-- where pm.p_id = 123
Sorry, my bad, it should be left join, not inner join!
With your changed requirements try this (not tested): Select all games and, for each one, get it's plateform_details and plateform_master correspondent values:
select
gm.gm_id,
gm.game_name,
gm.company,
pd.release_date,
pm.plateform_name
from game as gm
left join plateform_details as pd on pd.gm_id = gm.gm_id
left join plateform_master as pm on (pm.p_id = pd.p_id AND pd.gm_id = gm.gm_id)
-- WHERE CONDITIONS
;
Thanks for voting. I also wanted to tell you, that you have no many-to-many relationships here. You have just one-to-many. Many-to-many means the use of a middle table between two other ones.

SQL Join with multiple conditions for one column

In my situation, I've two tables, a table with all institutions profile and a table which represents material studied by each of these institutions.
In a search, I want to look for institution's name which studied specifics type of material. So it's possible to look for institution which studied "Wood" and "Metal", just "Metal" etc...
I've tried the following query :
SELECT p.name
FROM q1_institution_profiles p
INNER JOIN q9_materials_studied_by_institution pf
ON pf.id_institution = p.id_institution
WHERE pf.id_material_studied = 10 AND pf.id_material_studied = 8
However result is empty because there is a problem when I have multiple conditions (here id_material_studied must be equal 8 and 10).
Is there a way to achieve this properly or do I have to do on join for each criteria ?
Thank you in advance for answer
I think this is what you want:
SELECT p.name
FROM q1_institution_profiles p INNER JOIN
q9_materials_studied_by_institution pf
ON pf.id_institution = p.id_institution
WHERE pf.id_material_studied IN (8, 10)
GROUP BY p.name
HAVING COUNT(DISTINCT pf.id_material_studied) = 2;
That is, get names where there are rows with each of the two values.
The WHERE clause selects only materials 8 and 10. The GROUP BY then groups these by p.name. The question is: Does a single name have both these materials? Well, that is what count(distinct) does.

Multitable counting and multiplying in same query

I have got a somewhat complicated problem. This is my situation (ERD).
For a dashboard i need to create a pivot table that shows me the total amount of competences used by the vacancies. Therefore I need to:
Count the amount of vacancies per template
Count the amount of templates per competence
and last: multiply these numbers to get the total amount of comps used.
I have the first query:
SELECT vacancytemplate_id, count(id)
FROM vacancies
group by vacancytemplate_id;
And the second query isn't that difficult either, but I don't know what the right solution will be. I'm literally brainstuck. My mind can't comprehend how I can achieve the next step and put it down in a query. Please kind stranger, help me out :)
EDIT: my desired result is something like this
NameOfComp, NrOfTimesUsed
Leading, 17
Inspiring, 2
EDIT2: the meta query it should look like:
SELECT NameOfComp, (count of the competences used by templates) * (number of vacancies per template)
EDIT3: http://sqlfiddle.com/#!9/2773ca SQLFiddle
Thanks a lot!
If I am understanding your request correctly, you are wanting a count of competences per vacancy. This can be done very simply due to your table structure:
Select v.ID, count(*) from vacancy as v inner join CompTemplate_Table as CT
on v.Template_ID = CT.Template_ID group by v.ID;
The reason you can do only one join is because there will be a record in the CompTemplate_Table for every competency in each template. Additionally, the same key is used to join vacancy to templates as is used to join templates to CompTemplate_Table, so they represent the same key value (and you can skip joining the Templates table if you don't need data from there).
If you are wanting to add this data to a pivot table, I will leave that exercise to you. There are a number of tutorials available if you do a quick google search and it should not be that hard.
UPDATE: For the second query you are looking at something like:
Select cp.NameOfComp, count(*) from vacancy as v inner join CompTemplate_Table as CT
on v.Template_ID = CT.Template_ID inner join competencies as CP
on CP.ID = CT.Comp_ID
group by CP.NameOfComp
The differences here are you are adding in the comptetencies table, as you need data from that, and grouping by the CP.NameOfComp instead of the vacancy id. You can also restrict this to specific templates, competencies, or vacancies by adding in search conditions (e.g. where CP.ID = 12345)

Efficiently selecting from many-to-many relation in H2

I'm using H2, and I have a database of books (table Entries) and authors (table Persons), connected through a many-to-many relationship, itself stored in a table Authorship.
The database is fairly large (900'000+ persons and 2.5M+ books).
I'm trying to efficiently select the list of all books authored by at least one author whose name matches a pattern (LIKE '%pattern%'). The trick here is that the pattern should severly restrict the number of matching authors, and each author has a reasonably small number of associated books.
I tried two queries:
SELECT p.*, e.title FROM (SELECT * FROM Persons WHERE name LIKE '%pattern%') AS p
INNER JOIN Authorship AS au ON au.authorId = p.id
INNER JOIN Entries AS e ON e.id = au.entryId;
and:
SELECT p.*, e.title FROM Persons AS p
INNER JOIN Authorship AS au ON au.authorId = p.id
INNER JOIN Entries AS e ON e.id = au.entryId
WHERE p.name like '%pattern%';
I expected the first one to be much faster, as I'm joining a much smaller (sub)table of authors, however they both take as long. So long in fact that I can manually decompose the query into three selects and find the result I want faster.
When I try to EXPLAIN the queries, I observe that indeed they are very similar (a full join on the tables and only then a WHERE clause), so my question is: how can I achieve a fast select, that relies on the fact that the filter on authors should result in a much smaller join with the other two tables?
Note that I tried the same queries with MySQL and got results in line with what I expected (selecting first is much faster).
Thank you.
OK, here is something that finally worked for me.
Instead of running the query:
SELECT p.*, e.title FROM (SELECT * FROM Persons WHERE name LIKE '%pattern%') AS p
INNER JOIN Authorship AS au ON au.authorId = p.id
INNER JOIN Entries AS e ON e.id = au.entryId;
...I ran:
SELECT title FROM Entries e WHERE id IN (
SELECT entryId FROM Authorship WHERE authorId IN (
SELECT id FROM Persons WHERE name LIKE '%pattern%'
)
)
It's not exactly the same query, because now I don't get the author id as a column in the result, but that does what I wanted: take advantage of the fact that the pattern restricts the number of authors to a very small value to search only through a small number of entries.
What is interesting is that this worked great with H2 (much, much faster than the join), but with MySQL it is terribly slow. (This has nothing to do with the LIKE '%pattern%' part, see comments in other answers.) I suppose queries are optimized differently.
SELECT * FROM Persons WHERE name LIKE '%pattern%' will always take LONG on a 900,000+ row table no matter what you do because when your pattern '%pattern%' starts with a % MySql can't use any indexes and should do a full table scan. You should look into full-text indexes and function.
Well, since the like condition starts with a wildcard it will result in a full table scan which is always slow, no internal caching can take place.
If you want to do full text searches, mysql is not the best bet you have. Look into other software (solr for instance) to solve this kind of problems.

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