Rewriting query in order to remove FIND_IN_SET? - mysql

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.

Related

SQL transform id and add where statement before join

I am pretty new to SQL. Here is an operation I am sure is simple for a lot of you. I am trying to join two tables across databases on the same server – dbB and dbA, and TableA (with IdA) and TableB (with IdB) respectively. But before doing that I want to transform column IdA into a number, where I would like to remove the “:XYZ” character from its values and add a where statement for another column in dbA too. Below I show my code for the join but I am not sure how to convert the values of the column. This allows me to match idAwith idB in the join. Thanks a ton in advance.
Select replace(idA, “:XYZ”, "")
from dbA.TableA guid
where event like “%2015”
left join dbB.TableB own
on guid.idA = own.idB
Few things
FROM, Joins, WHERE (unless you use subqueries) syntax order it's also the order of execution (notice select isn't listed as its near the end in order of operation but first syntactically!)
alias/fully qualify columns when multiple tables are involved so we know what field comes from what table.
order of operations has the SQL doing the from and JOINS 1st thus what you do in the select isn't available (not in scope yet) for the compiler, this is why you can't use select column aliases in the from, where or even group by as well.
I don't like Select * usually but as I don't know what columns you really need... I used it here.
As far as where before the join. most SQL compilers anymore use cost based optimization and figure out the best execution plan given your data tables and what not involved. So just put the limiting criteria in the where in this case since it's limiting the left table of the left join. If you needed to limit data on the right table of a left join, you'd put the limit on the join criteria; thus allowing it to filter as it joins.
probably need to cast IDA as integer (or to the same type as IDB) I used trim to eliminate spaces but if there are other non-display characters, you'd have issues with the left join matching)
.
SELECT guild.*, own.*
FROM dbA.TableA guid
LEFT JOIN dbB.TableB own
on cast(trim(replace(guid.idA, ':XYZ', '')) as int) = own.idB
WHERE guid.event like '%2015'
Or materialize the transformation first by using a subquery so IDA in its transformed state before the join (like algebra ()'s matter and get processed inside out)
SELECT *
FROM (SELECT cast(trim(replace(guid.idA, ':XYZ', '')) as int) as idA
FROM dbA.TableA guid
WHERE guid.event like '%2015') B
LEFT JOIN dbB.TableB own
on B.IDA = own.idB

Using an INNER JOIN without returning any columns from the joined table

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.

MySQL Where IN with an Array

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;

MySql Multiple Tables query

I wonder if any can help me understand something I'm trying to solve.
I'm working on a wordpress site but this is more a sql question as I'm just querying to get some results within a template file.
I have a gallery of pictures which are advert boxes, and I need to pull these in relation to a supplied movie name, to do this Im using some custom fields on the ad pic called 'adlink' (link off ad) and ad
I'm using the nextgen gallery plugin and querying those tables, and I have three tables in total that contain the data I need to query.
ngg_pictures, nggcf_field_values & nggcf_fields.
the nggcf tables are custom fields tables,
I have got so far I can get what I need in two seperate queries, but I can't combine these into one query as it means querying the nggcf_field_values table twice, which I can't seem to sort.
I have hardcoded the search criteria in for the mo, but the 'close-encounters' bit would be a passed var, and the '156' would be the pid from the first query.
SELECT `eg_ngg_pictures`.`filename`, `eg_nggcf_field_values`.`fid`, `eg_nggcf_field_values`.`pid`
FROM eg_ngg_pictures, eg_nggcf_field_values
WHERE ((`eg_nggcf_field_values`.`field_value` LIKE 'close-encounters') AND (`eg_nggcf_field_values`.`pid` = eg_ngg_pictures.pid))
SELECT `eg_nggcf_field_values`.`field_value`
FROM eg_nggcf_field_values, eg_nggcf_fields
WHERE ((`eg_nggcf_fields`.`field_name` = 'adlink') AND (`eg_nggcf_fields`.`id` = eg_nggcf_field_values.fid) AND (`eg_nggcf_field_values`.`pid` = '156'))
any help would be greatly appreciated, I can get the results with what I have, but I like to understand how to combine these two and write better SQl. Thanks MRO
After looking at the Wordpress extension, I think the eg_nggcf_fields is the table that contains the name for a custom field. The eg_nggcf_field_values table contains the values of that custom field per picture.
So if you're looking for two fields called moviename and adlink, you have to look up two rows in the field_values table. You can join a table twice if you give it a different alias:
select pic.filename
, pic.pid
, fv1.field_value as MovieName
, fv2.field_value as Adlink
from eg_ngg_pictures pic
inner join -- Find ID for the field called 'moviename'
eg_nggcf_fields f1
on f1.field_name = 'moviename'
inner join -- Find value for field moviename for this picture
eg_nggcf_field_values as fv1
on fv1.pid = pic.pid
and fv1.fid = f1.fid
inner join -- Find ID for the field called 'adlink'
eg_nggcf_fields f2
on f2.field_name = 'adlink'
inner join -- Find value for field adlink for this picture
eg_nggcf_field_values as fv2
on fv2.pid = pic.pid
and fv2.fid = f2.fid
where fv1.field_value like 'close-encounters'
First of all, I'd recommend sticking to modern ANSI syntax for JOINing tables, which means using the JOIN clause.
Instead of using:
FROM table1, table2 WHERE table1.id = table2.pid
use:
FROM Table 1 JOIN table2 ON table1.id = table2.id
For simplicity's sake, I'd also recommend you to alias tables, as that tends to make the code more readable. Instead of having to write out egg_ngg_pictures every time, you can simply refer to the alias you assign it instead.
Lastly, when you use a LIKE operator, you usually add a wild-card character (typically %. I.e. LIKE '%123' or LIKE '123%'). You seem to look only for complete matches, which means you can just stick to using =, as that should give you slightly better performance.
Now to rewrite your query, I'd use something like the following:
SELECT
pic.filename
, fieldval.fid
, fieldval.pid
, fieldval.field_value
FROM
eg_ngg_pictures pic
JOIN eg_nggcf_field_values fieldval ON fieldval.pid = pic.pid
JOIN eg_nggcf_fields fields ON fields.id = fieldval.fid
WHERE
((fieldval.field_value = 'close-encounters')
AND fields.field_name = 'ad_link'
Note that I am not able to test the query, as I do not have your schema. But by incorporating the two queries into a single query, the join on the field_Values.PID retreieved with the 'close_encounters' value should already exist.
If the query does not work, feel free to create a SQL fiddle with the relevant tables and some data, and I'll try and get it to work with that.

MySQL JOIN based on dynamic LIKE statement between multiple tables

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