I've searched a bit but haven't found exactly what I'm looking for, so far. Basically I have a MySQL database with a couple tables (keywords, company and link tables). I want to be able to supply an array of keywords and find the associated companies. When I run the second query without the WHERE IN clause, it works. When I supply an array, it doesn't.
select keywords.idkeywords into #keyId
from keywords where
keywords.keyword IN ('Entertainment', 'Public');
select distinct company.company_name
from keywords, keyword_to_company, company
where keyword_to_company.keywordId = #keyId
and keyword_to_company.compId = company.idcompany;
Your query just doesn't make sense. First, you are trying to put multiple values in #keyid, but you can't do that. And, MySQL doesn't have the concept of table variables. You could use a temporary table.
Then the second query is worse. Does this query work for you?
select distinct c.company_name
from keywords k natural join
keyword_to_company k2c natural join
company c
where k.keyword IN ('Entertainment', 'Public') and
k2c.compId = company.idcompany;
I'm only using natural join because you don't specify the join keys. In general, you should always specify the columns being joined, using either on or using.
Thanks...
Your query didn't work..
This query does work however. Althought #keyId returns multiple rows, the query succeeds and results in a listing of associated companies. i agree that it shouldn't work, but it does.
select keywords.idkeywords into #keyId from keywords where
keywords.keyword = 'Entertainment'
and
keywords.keyword = 'Public';
select distinct company.company_name from keywords, keyword_to_company, company
where
keyword_to_company.keywordId = #keyId
and
keyword_to_company.compId = company.idcompany;
Related
Running an INNER JOIN type of query, i get duplicate column names, which can pose a problem. This has been covered here extensively and i was able to find the solution to this problem, asides from it being fairly logical, by SELECTing only the columns i need.
However, i would like to know how i could run such a query without actually returning any of the columns from the joined table.
This is my MySQL query
SELECT * FROM product z
INNER JOIN crosslink__productXmanufacturer a
ON z.id = a.productId
WHERE
(z.title LIKE "%search_term%" OR z.search_keywords LIKE "%search_term%")
AND
z.availability = 1
AND
a.manufacturerId IN (22,23,24)
Question
How would i modify this MySQL query in order to return only columns from product and none of the columns from crosslink__productXmanufacturer?
Add the table name to the *. Replace
SELECT * FROM product z
with
SELECT z.* FROM product z
Often when you are doing this, the intention may be clearer using in or exists rather than a join. The join is being used for filtering, so putting the condition in the where clause makes sense:
SELECT p.*
FROM product p
WHERE (p.title LIKE '%search_term%' OR p.search_keywords LIKE '%search_term%') AND
p.availability = 1 AND
exists (SELECT 1
FROM pXm
WHERE pXm.productId = p.id AND pxm.manufacturerId IN (22, 23, 24)
);
With the proper indexes, this should run at least as fast as the join version (the index is crosslink__productXmanufacturer(productId, manufacturerId). In addition, you don't have to worry about returning duplicate records, if there are multiple matches in crosslink__productXmanufacturer.
You may notice two other small changes I made to the query. First, the table aliases are abbreviates for the table names, making the logic easier to follow. Second, the string constants use single quotes (the ANSI standard) rather than double quotes. Using single quotes only for string and date constants helps prevent inadvertent syntax errors.
This is really a two-part question, but in order not to mix things up, I'll divide into two actual questions. This one is about creating the correct SQL statement for selecting a row based on values in a many-to-many related table:
Now, the question is: what is the absolute simplest way of getting all resources where e.g metadata.category = subject AND where that category's corresponding metadata.value ='introduction'?
I'm sure this could be done in a lot of different ways, but I'm a novice in SQL, so please provide the simplest way possible... (If you could describe briefly what the statement means in plain English that would be great too. I have looked at introductions to SQL, but none of those I have found (for beginners) go into these many-to-many selections.)
The easiest way is to use the EXISTS clause. I'm more familiar with MSSQL but this should be close
SELECT *
FROM resources r
WHERE EXISTS (
SELECT *
FROM metadata_resources mr
INNER JOIN metadata m ON (mr.metadata_id = m.id)
WHERE mr.resource_id = r.id AND m.category = 'subject' AND m.value = 'introduction'
)
Translated into english it's 'return me all records where this subquery returns one or more rows, without returning the data for those rows'. This sub query is correlated to the outer query by the predicate mr.resource_id = r.id which uses the outer row as the predicate value.
I'm sure you can google around for more examples of the EXIST statement
In my rails app I have three tables to deal with the many-to-many relationship between courses and categories
courses
course_categories_courses
course_categories
I have groups of categories, and I want to allow filtering of the listing of the courses by categories through an interface like:
Location
very near
near
far
Type
short
medium
long
To search for medium types either near or far I had thought of using:
SELECT distinct courses.*
FROM `courses`
inner join course_categories on
course_categories_courses.course_category_id = course_categories.id
and (
course_categories.id in ('medium')
and course_categories.id in ('near', 'far')
)
but that's not working. Anyone able to point me in the right direction please?
You generally want to put to 'relationships' as join conditions, and values (literals) to filter by in the where clauses
ex. join condition - on course_categories_courses.course_category_id = course_categories.id
ex. where clause - where course_categories.id in ('near', 'far')
So you might want to rewrite your query that way. But more than that, how can the both of these possible be true?
(course_categories.id in ('medium') and course_categories.id in ('near', 'far'))
Is that what you intend?
You have to join both tables and you are using id field where type and location (according to your description) should be used
SELECT distinct courses.* FROM `courses`
inner course_categories_courses on course_categories_courses.course_category_id = courses.course_category_id
inner join course_categories on
course_categories.id = course_categories_courses.id
where (course_categories.type in ('medium') and course_categories.location in ('near', 'far'))
Syntactaclly the query you wrote is fine. There are two unusal things about it as other's have noted
Filter on a Inner join. (Typically done in the where but perhaps you have your reasons)
course_categories.id in ('medium'). Most would expect a number here but perhaps this is just for demonstration purposes.
The reason for it "not working"
You're getting an error that you've swept under the rug in your ruby that you're not sharing
Its working there simply are no records matching your criteria.
Ways to debug.
Run it in Workbench (or some other client) and check the results
If there are no errors and no record are returned run some queries in workbench
Select count(*) from course_categories where course_categories.id in ('medium')
Select count(*) from course_categories where course_categories.id in ('near', 'far')
If you got the results you wanted in Workbench then its probably your client (Ruby) code, bad connection string perhaps?
My mysql query looks like this:
SELECT pages.*,
showcase.*,
project.*
FROM pages
INNER JOIN showcase ON showcase.pid = pages.uid AND showcase.deleted != 1
INNER JOIN project ON FIND_IN_SET(project.uid, showcase.projects)
WHERE pages.deleted != 1
AND pages.pid = 14
AND pages.dokType = 150
The problem is the second INNER JOIN - it uses FIND_IN_SET because a showcase (= collection of projects) stores its projects as a comma separated list in the field showcase.projects. FIND_IN_SET cant use indexes as far as I know, so the second join requires a full table scan of the project table. Any possibility to use an index without changing the database scheme?
You searching for a 'string' inside another string. You'll have to do a scan regardless. The other optimized way to do this using indexes is to use a join table. Create a new table showcase_projects that has columns of project_id and showcase_id. This will then have a record for every association between the two.
Now this answer is based on my primitive understanding of your datastructure from this query.
Is showcase.projects a VARCHAR? Then you could use REGEXP like this:
...
INNER JOIN project ON
showcase.projects REGEXP CONCAT('[[:<:]]', project.uid, '[[:>:]]')
WHERE ...
This expression looks for whole words only (the bracket constructions mark left and right word boundaries, respectively. However, I'm not sure this will be able to make use of an index, maybe someone can come up with something smarter.
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).