SQL/PHP find_in_set - mysql

I'm trying to pull results where 1 row from tableA (profiles.category) matches 1 row from tableB (projects.categorysecond) however I'm not getting results.
*IMPORTANT projects.categorysecond will vary between having only 1 category to several category deliminated by ; EXAMPLE: OTTAWA OR OTTAWA;TORONTO;MONTREAL OR OTTAWA;MONTREAL OR TORONTO;MONTREAL
profiles.category will always only have 1 category, never deliminated.
I need to make sure that regardless if I have OTTAWA OR OTTAWA;TORONTO;MONTREAL in profiles.category it PULLS results as long as 1 word matches.
I'm currently trying the following query:
SELECT p.*, up.* FROM users_profiles up INNER JOIN projects p ON find_in_set(up.category, p.categorysecond) > 0

FIND_IN_SET() only understands comma as a separator. Read https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_find-in-set
So you could substitute ; with , and then do the comparison:
SELECT p.*, up.*
FROM users_profiles up
INNER JOIN projects p
ON FIND_IN_SET(up.category, REPLACE(p.categorysecond, ';', ',')) > 0
I have to comment that this is not a good way to store data if you want to write expressions that find individual words in your semicolon-separated string. See my answer to Is storing a delimited list in a database column really that bad?
You should store one project category per row in a project_categories table. Then your query would be easier:
SELECT p.*, up.*
FROM users_profiles up
INNER JOIN project_categories pc
ON up.category = pc.category
INNER JOIN projects p
ON p.project = pc.project;
With a compound index on project_categories(category,project), this query should be optimized pretty well.

Related

MySQL: Optimizing Sub-queries

I have this query I need to optimize further since it requires too much cpu time and I can't seem to find any other way to write it more efficiently. Is there another way to write this without altering the tables?
SELECT category, b.fruit_name, u.name
, r.count_vote, r.text_c
FROM Fruits b, Customers u
, Categories c
, (SELECT * FROM
(SELECT *
FROM Reviews
ORDER BY fruit_id, count_vote DESC, r_id
) a
GROUP BY fruit_id
) r
WHERE b.fruit_id = r.fruit_id
AND u.customer_id = r.customer_id
AND category = "Fruits";
This is your query re-written with explicit joins:
SELECT
category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN
(
SELECT * FROM
(
SELECT *
FROM Reviews
ORDER BY fruit_id, count_vote DESC, r_id
) a
GROUP BY fruit_id
) r on r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id
CROSS JOIN Categories c
WHERE c.category = 'Fruits';
(I am guessing here that the category column belongs to the categories table.)
There are some parts that look suspicious:
Why do you cross join the Categories table, when you don't even display a column of the table?
What is ORDER BY fruit_id, count_vote DESC, r_id supposed to do? Sub query results are considered unordered sets, so an ORDER BY is superfluous and can be ignored by the DBMS. What do you want to achieve here?
SELECT * FROM [ revues ] GROUP BY fruit_id is invalid. If you group by fruit_id, what count_vote and what r.text_c do you expect to get for the ID? You don't tell the DBMS (which would be something like MAX(count_vote) and MIN(r.text_c)for instance. MySQL should through an error, but silently replacescount_vote, r.text_cbyANY_VALUE(count_vote), ANY_VALUE(r.text_c)` instead. This means you get arbitrarily picked values for a fruit.
The answer hence to your question is: Don't try to speed it up, but fix it instead. (Maybe you want to place a new request showing the query and explaining what it is supposed to do, so people can help you with that.)
Your Categories table seems not joined/related to the others this produce a catesia product between all the rows
If you want distinct resut don't use group by but distint so you can avoid an unnecessary subquery
and you dont' need an order by on a subquery
SELECT category
, b.fruit_name
, u.name
, r.count_vote
, r.text_c
FROM Fruits b
INNER JOIN Customers u ON u.customer_id = r.customer_id
INNER JOIN Categories c ON ?????? /Your Categories table seems not joined/related to the others /
INNER JOIN (
SELECT distinct fruit_id, count_vote, text_c, customer_id
FROM Reviews
) r ON b.fruit_id = r.fruit_id
WHERE category = "Fruits";
for better reading you should use explicit join syntax and avoid old join syntax based on comma separated tables name and where condition
The next time you want help optimizing a query, please include the table/index structure, an indication of the cardinality of the indexes and the EXPLAIN plan for the query.
There appears to be absolutely no reason for a single sub-query here, let alone 2. Using sub-queries mostly prevents the DBMS optimizer from doing its job. So your biggest win will come from eliminating these sub-queries.
The CROSS JOIN creates a deliberate cartesian join - its also unclear if any attributes from this table are actually required for the result, if it is there to produce multiples of the same row in the output, or just an error.
The attribute category in the last line of your query is not attributed to any of the tables (but I suspect it comes from the categories table).
Further, your code uses a GROUP BY clause with no aggregation function. This will produce non-deterministic results and is a bug. Assuming that you are not exploiting a side-effect of that, the query can be re-written as:
SELECT
category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN Reviews r
ON r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id
ORDER BY r.fruit_id, count_vote DESC, r_id;
Since there are no predicates other than joins in your query, there is no scope for further optimization beyond ensuring there are indexes on the join predicates.
As all too frequently, the biggest benefit may come from simply asking the question of why you need to retrieve every single row in the tables in a single query.

MYSQL server: Write a query to display cars which was not taken on rent

Use Cars and Rentals tables to retrieve records.
CARS(car_id, car_name, car_type)
RENTALS(rental_id, cust_id, car_id, pickup_date, km, fare)
SELECT c.car_id, c.car_name, c.car_type
FROM cars as c, rentals as r
WHERE c.car_id=r.car_id and r.pickup_date=null
ORDER BY c.car_id;
I've tried this but output is NO ROWS SELECTED
I would recommend NOT EXISTS for this query:
select c.*
from cars c
where not exists (select 1 from rentals r where r.car_id = c.car_id);
That said, your query has multiple errors:
The comma in the FROM clause is very last-century. Use JOIN.
= NULL is always going to filter out all rows. Almost all comparisons to NULL return NULL which is treated as "false". The correct comparison is IS NULL, but I don't think that is needed.
You can specify equivalent logic using LEFT JOIN, but I think NOT EXISTS is closer to the statement of the question.
Your original intent was to join the two tables and filters on rows that did not match on the right side of the join. This technique is sometimes called an anti left join.
Your attempt failed because you need a left join rather than an (implicit) inner join, and because you did not properly check for nullity (this requires operator is null).
The left join solution phrases as:
select c.*
from cars c
left join rentals r on r.car_id = c.car_id
where r.car_id is null
order by c.car_id
Note that I check for nullity on column car_id rather than pickup_date - any column that is not nullable can do, however I find the intent clearer when using the joining column.
A simple way to do this is with NOT IN:
SELECT car_id, car_name, car_type
FROM cars
WHERE car_id NOT IN (SELECT car_id FROM rentals)
ORDER BY car_id;

Mysql - Display Team name and Count of Team members from 2 tables

The following query works great
SELECT
t.name,
t.id
FROM
team t,
member m
WHERE
m.team_id = t.id
and shows multiple results
what I am stuck with it is with how to modify the query about to display the team name and the number of team members in that team so, for example, Team A has 50 team members, Team B has 20 members and so on.
The problem is that the member.team_id has Comma separated values
My table structure for team table
My table structure for member table
Not a duplicate question at all
Use the technique in sql join tables where 1 column has comma to join the tables, then use COUNT(*) to get the member counts.
SELECT t.name, COUNT(*)
FROM team t
JOIN member m
ON FIND_IN_SET( m.team_id, t.id ) > 0
GROUP BY t.name
To get the number of members from comma separated list try using length() and replace()
select
(LENGTH(team_ids) - LENGTH(REPLACE(team_ids, ',', '')))+1
from MyTable
By removing the commas the length is reduced by the number of those, and ou need 1 more because there isn't a trailing comma at the end.

Joining Results From Another Table

I'm dealing with a large query that maps data from one table into a CSV file, so it essentially looks like a basic select query--
SELECT * FROM item_table
--except that * is actually a hundred lines of CASE, IF, IFNULL, and other logic.
I've been told to add a "similar items" line to the select statement, which should be a string of comma-separated item numbers. The similar items are found in a category_table, which can join to item_table on two data points, column_a and column_b, with category_table.category_id having the data that identifies the similar items.
Additionally, I've been told NOT to use a subquery.
So I need to join category_table and group_concat item numbers from that table having the same category_id value (but not having the item number of whatever the current record would be).
If I can only do it with a subquery regardless of the instructions, I will accept that, but I want to do it with a join and group_concat as instructed if possible--I just can't figure it out. How can I do this?
You can make use of a mySQL "feature" called hidden columns.
I am going to assume you have an item id in the item table that uniquely identifies each row. And, if I have your logic correct, the following query does what you want:
select i.*, group_concat(c.category_id)
from item_table i left outer join
category_table c
on i.column_a = c.column_a and
i.column_b = c.column_b and
i.item_id <> c.category_id
group by i.item_id
I think this is what you're looking for, although I wasn't sure what uniquely identified your item_table so I used column_a and column_b (those may be incorrect):
SELECT
...,
GROUP_CONCAT(c.category_id separator ',') CategoryIDs
FROM item_table i
JOIN category_table ct ON i.column_a = ct.column_a AND
i.column_b = ct.column_b
GROUP BY i.column_a, i.column_b
I've used a regular INNER JOIN, but if the category_table might not have any related records, you may need to use a LEFT JOIN instead to get your desired results.
Maybe something like this?
SELECT i.*, GROUP_CONCAT(c.category_id) AS similar_items
FROM item_table i
INNER JOIN category_table c ON (i.column_a = c.column_a AND
i.column_b = c.column_b)
GROUP BY i.column_a, i.column_b

mysql query with multiple left join in 3 tables?

i have a search query which will retrieve information from 3 tables i made the query so it retrieve the information from 2 tables and i don't know if i can combine the third one or not
SELECT *
FROM articles
INNER JOIN terms
ON articles.ArticleID = terms.RelatedID AND terms.TermType = 'article'
the third query is
SELECT * FROM categories where CategoryID in (something)
something is a filed in the articles tables which have value like '3,5,8'
i want do this 2 queries into 1 query and i don't know if it can be done by 1 query or not
without looking at your schema (which would be helpful) and some sample data try this query
SELECT *
FROM categories,articles
INNER JOIN terms
ON (articles.ArticleID = terms.RelatedID AND terms.TermType = 'article')
WHERE
FIND_IN_SET(categories.CategoryID,articles.categories)
here is the definition for FIND_IN_SET()
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
If i understand you correctly. Looks like you have multiple categories for each article with the Category IDs all stored as a concatenated string.
SELECT A.*
FROM articles A
INNER JOIN terms T on A.ArticleID = T.RelatedID AND T.TermType = 'article'
LEFT JOIN categories C on C.CategoryID IN (3,5,8 OR A.CategoryIDs)
GROUP BY C.CategoryName
You want to LEFT JOIN since you may or may not have multiple categories, you can group by Categories to get disticnt category article pairs and CONCAT() to recombine article records as needed.