MySQL JOIN based on dynamic LIKE statement between multiple tables - mysql

I have a table called faq. This table consists from fields faq_id,faq_subject.
I have another table called article which consists of article_id,ticket_id,a_body and which stores articles in a specific ticket. Naturally there is also a table "ticket" with fields ticket_id,ticket_number.
I want to retrieve a result table in format:
ticket_number,faq_id,faq_subject.
In order to do this I need to search for faq_id in the article.a_body field using %LIKE% statement.
My question is, how can I do this dynamically such that I return with SQL one result table, which is in format ticket_number,faq_id,faq_subject.
I tried multiple configurations of UNION ALL, LEFT JOIN, LEFT OUTER JOIN statements, but they all return either too many rows, or have different problems.
Is this even possible with MySQL, and is it possible to write an SQL statement which includes #variables and can take care of this?

First off, that kind of a design is problematic. You have certain data embedded within another column, which is going to cause logic as well as performance problems (since you can't index the a_body in such a way that it will help the JOIN). If this is a one-time thing then that's one issue, but otherwise you're going to have problems with this design.
Second, consider this example: You're searching for faq_id #123. You have an article that includes faq_id 4123. You're going to end up with a false match there. You can embed the faq_id values in the text with some sort of mark-up (for example, [faq_id:123]), but at that point you might as well be saving them off in another table as well.
The following query should work (I think that MySQL supports CAST, if not then you might need to adjust that).
SELECT
T.ticket_number,
F.faq_id,
F.faq_subject
FROM
Articles A
INNER JOIN FAQs F ON
A.a_body LIKE CONCAT('%', F.faq_id, '%')
INNER JOIN Tickets T ON
T.ticket_id = A.ticket_id
EDIT: Corrected to use CONCAT

SELECT DISTINCT t.ticket_number, f.faq_id, f.faq_subject
FROM faq.f
INNER JOIN article a ON (a.a_body RLIKE CONCAT('faq_id: ',faq_id))
INNER JOIN ticket t ON (t.ticket_id = a.ticket_id)
WHERE somecriteria

Related

2 Queries vs inner join with where

I have 2 tables: Articles and Comments;
"Comments.articleID" is a foreign key.
I want to query the database to compose a website that shows the article text of a certain article (given an articleID) and all the article's comments.
I can think of 2 ways to query the data:
Use 2 separate queries:
SELECT articles.text FROM articles where id = givenArticleID
SELECT comments.* FROM comments where comments.articleID = givenArticleID
Use an Inner join:
SELECT articles.text, comments.*
FROM articles
INNER JOIN comments on articles.id = comments.articleID
WHERE articles.id = givenArticleID
The first option only returns the data I am interested in - that is good.
The second option returns all data I am interested in, but much more data than necessary. Every row in the result set contains the article.text column, that could be a lot of (unnecessary) data.
I think that the join would be better for certain queries, that do not require a WHERE condition (thus containing different articles).
Which way would you generally prefer in the situation above?
Or is there an even better alternative...?
Option 2 is probably better, because it is only one client-server round trip.
Also don't forget that each query has to be parsed by the database server.
I'd recommend that you benchmark both versions and see which one performs better.

JOINING a query from 2 tables - Multiple Results

Could someone please tell explain to me how to properly process a query that collects information from 2 tables, i thought I had this figured out until I added more records. Please look at the image I have below:enter image description here
(The last record should not have the name "Thomas Murray" in it)
Then there is the query I am processing:
"select a.*, b.forenames, b.surname FROM playerSkills a, playerdb b GROUP BY sheetNo"
What I was hoping to do is collect all from the playerSkills database (which it does) and only bring over the names from the second database (playerdb) that matched with the playerID but as I want to return more than one result so I don't know what to do as it returns the whole column and just pastes the one name into every field.
Though I am sure a JOIN is to be inserted here, I am not sure which or at all.
I am not experienced with SQL but trying to wrap my head around it. I have experimented with the JOIN clauses but didn't get far probably due to a syntax.
How can join the names to the playerID so they appear in the appropriate fields?
You need columns to join on . . . and proper join syntax:
select ps.*, p.forenames, p.surname
FROM playerSkills ps JOIN
playerdb p
ON ps.playerId = p.playerId;
Notes:
Your query does not require GROUP BY.
Your query does require JOIN conditions.
Kudos for using table aliases. They should be abbreviations for the table name.
You want to always use explicit JOIN syntax. No commas in the FROM clause.

Display default record when query dont have any rows

I am trying to display a default record in a simple query but my attempt doesn't work:
SELECT
COALESCE(suppliers.supplier_name, 'No records') AS supplier_name
FROM suppliers
LEFT JOIN suppliers_purchases USING(supplier_id)
LEFT JOIN suppliers_purchases_articles USING(supplierpurchase_id)
WHERE suppliers_purchases_articles.article_id = 150
ORDER BY suppliers_purchases.supplierpurchase_id DESC
LIMIT 1
As the query returns no rows the coalesce never kicks in - there's no value to act on, let alone NULL.
While technically it is possible to solve your problem in SQL, it would become an awfully large, ugly, unmaintainable piece of SQL. This is because you are trying to solve an issue in SQL that it was never meant to do - a display problem. SQL is meant to control absolute and strict data sets, not default to informational messages based on the lack of a result set. No records is not the name of any supplier in your database, so don't list it as one.
Long story short: don't solve presentational issues in your data layer. Your front end code should handle the lack of results and fall back to properly displaying No records instead, where it's localizable, controllable, and expected by the developer after you.
While I agree this is a presentation logic issue, I have come across times where I had to control it from the database as I couldn't alter the UI.
If that is the case, you have a couple different options. One of them is to introduce a new temporary table and use another outer join:
SELECT
COALESCE(suppliers.supplier_name, 'No records') AS supplier_name
FROM (SELECT 1 as FakeCol) t
LEFT JOIN suppliers ON suppliers_purchases_articles.article_id = 150
LEFT JOIN suppliers_purchases USING(supplier_id)
LEFT JOIN suppliers_purchases_articles USING(supplierpurchase_id)
ORDER BY suppliers_purchases.supplierpurchase_id DESC
LIMIT 1
Condensed Fiddle Demo
Note I've moved the where criteria to the join. This isn't completely necessary, I just prefer the way it reads as such. If you have to leave where criteria, you don't want to negate your outer join, so you'll need to add corresponding is null checks as well.

Need help querying unused id's in relational table

I'm trying to query my database for unused listId's but can't quite seem to get the query syntax correct. Here is what I've tried:
SELECT DISTINCT pkListId FROM tblList
INNER JOIN InUseLists ON
tblList.pkListId NOT LIKE InUseLists.fkListId
Where tblList holds all List data, and InUseLists is a view holding all distinct listId's from my relational table.
Currently my tblList holds listId 1-9
my relational table has fkListId 1,8,9 (used more than once)
So my view gets distinct values from the relational table - 1,8,9.
What is wrong with my query syntax?
It sounds like you want a left join (which can succeed whether or not it finds a match in the "right" table), and then to select those rows where the join, in fact, failed to work.
Something like:
SELECT DISTINCT pkListId FROM tblList
LEFT JOIN InUseLists ON
tblList.pkListId LIKE InUseLists.fkListId
WHERE InUseLists.fkListId is null
(If LIKE is the right operator here. Most joins use a simple equality check)
If this still isn't right, it might be better if you edited your question to include the sample data and expected results in a tabular form. At the moment, I'm still not sure what the contents of your tables are, and what you're aiming for.
Try:
SELECT DISTINCT tblList.pkListId, FROM tblList, InUseLists WHERE
tblList.pkListId NOT IN(InUseLists.fkListId)

Rewriting query in order to remove FIND_IN_SET?

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.