I've got 1 SQL command to select data from 2 different tables.
First table is "sections" (id, title, text) and second table is "sections_multimedia" (id, section_id, filename)
I use GROUP_CONCAT to get all the sections of the page and each one's multimedia (photos)
My command is the following:
SELECT s.*, GROUP_CONCAT(CONCAT_WS(':', sm.id, sm.filename) SEPARATOR ',') AS multimedia
FROM sections AS s, sections_multimedia AS sm
WHERE s.id = sm.section_id
GROUP BY sm.section_id
ORDER BY s.id ASC;
Table "sections" and table "sections_multimedia" are connected via "section_id" field of "sections_multimedia" table.
The above SQL command works perfectly, the only problem is:
When a row from "sections" table has nothing in table "sections_multimedia" it is not showed in the results.
What can i do to grab all data from table "sections" no matter if they have or not any data in "sections_multimedia" table?
Thank you!
Never use commas in the FROM clause. Always use proper, explicit JOIN syntax. In your case, you want a LEFT JOIN:
SELECT s.*,
GROUP_CONCAT(CONCAT_WS(':', sm.id, sm.filename) SEPARATOR ',') AS multimedia
FROM sections s LEFT JOIN
sections_multimedia sm
ON s.id = sm.section_id
GROUP BY s.id
ORDER BY s.id ASC;
Related
Such is my MySQL query and the order by keyword isnt working here. Cannot figure out what's wrong with it.
insert into leaderboard
select student.student_name as name , sum(marks) as total
from marks inner join student on student.student_id = marks.student_id
group by marks.student_id order by total desc;
leaderboard table output image
Your current insert is not far off, though as a matter of practice, you should always explicitly list out the target columns for insertion, i.e. use this version:
INSERT INTO leaderboard (name, total) -- or whatever the column names are called
SELECT s.student_name, SUM(m.marks)
FROM marks m
INNER JOIN student s ON s.student_id = m.student_id
GROUP BY s.student_id;
Regarding the order you do or don't perceive in the leaderboard table, appreciate that SQL tables are modeled after unordered sets of data. That is, there is not really any inherent order in a SQL table. If you want to view your data in a certain order, then use an ORDER BY clause when you query (not when you insert):
SELECT name, total
FROM leaderboard
ORDER BY total DESC;
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.
have 3 following tables:
users (id, name)
projects (id, name)
user_to_project (user_id, project_id)
Every user can be assigned to more than one project and this is stored in the user_to_project table. I want to get a user name and all the projects he's assigned to in one field separated with commas. I tried something like this:
SELECT
users.id AS 'ID',
users.name AS 'Name',
(SELECT GROUP_CONCAT (projects.name SEPARATOR ', ')
FROM user_to_project
INNER JOIN projects ON (projects.id = user_to_project.project_id)
INNER JOIN users ON (users.id = user_to_project.user_id)) AS 'Projects'
FROM users
It gets me all assigned projects in every row which is not that I want. How to fix this?
You can do this with a subquery, but you want a correlation clause:
SELECT u.id, u.name,
(SELECT GROUP_CONCAT(p.name SEPARATOR ', ')
FROM user_to_project tup INNER JOIN
projects p
ON p.id = utp.project_id
WHERE u.id = utp.user_id
) as Projects
FROM users u;
Notes:
Use table aliases. They make a query easier to write and to read.
Don't use single quotes for column aliases. Only use single quotes for string and column names (and your column aliases don't require any escape character).
This is different from a version using INNER JOIN, because this will keep all users, even those with no projects.
I didn't see any reason for this correlated query, and you were missing a condition inside to relate it to the outer query. You also needed a group by clause.
This query should give you all the projects for each ID :
SELECT users.id, users.name ,
GROUP_CONCAT (projects.name SEPARATOR ', ')
FROM user_to_project
INNER JOIN projects ON (projects.id = user_to_project.project_id)
INNER JOIN users ON (users.id = user_to_project.user_id)
GROUP BY users.id,users.name
Note: To make your query work, all you need is to drop the users table from the inner query, and keep the condition.
I have 4 tables in an existing mysql database of a directory type site.
Table mt_links contains basic info for each listing
Table mt_cl contains which listing above is in what category (I only want cat_id=1)
Table mt_cfvalues contains more details for each listing It Can have repeated values
Table mt_images contains image names for each listing.
I want all records from mt_links where the mt_cl cat_id=1, and for each of those records, I need all records in mt_cfvalues and cf_images matching the link_id.
I set up a select with Group_Concat and left joins, but ended up with repeating values in my results. I added Distinct, which cured the repeating values, but mt_cfvalues can have records with the same value, so now I'm missing a value I should have.
SELECT a.link_id,
a.link_name,
a.link_desc,
GROUP_CONCAT(DISTINCT b.value ORDER BY b.cf_ID) AS details,
GROUP_CONCAT(DISTINCT c.filename ORDER BY c.ordering) AS images
FROM mt_links a
LEFT JOIN mt_cfvalues b ON a.link_id = b.link_ID
LEFT JOIN mt_images c ON b.link_id = c.link_ID
LEFT JOIN mt_cl d ON a.link_id = d.link_ID WHERE d.cat_ID = '1'
GROUP BY a.link_id
I put together a SQLFiddle here: http://www.sqlfiddle.com/#!2/f39e9/1
Is there an easier way? How do I fix the repeating / no repeating issue?
Here is one way of accomplishing what you seek. Because the two subqueries return independent results, you can't combine the GROUP BY, which is why you were getting duplicates.
SELECT a.link_id,
a.link_name,
a.link_desc,
cvf.details,
imgs.images
FROM mt_links a
LEFT JOIN (
SELECT link_ID, GROUP_CONCAT(value ORDER BY cf_ID) AS details
FROM mt_cfvalues
GROUP BY link_ID
) cvf ON cvf.link_ID = a.link_id
LEFT JOIN (
SELECT link_ID, GROUP_CONCAT(filename ORDER BY ordering) AS images
FROM mt_images
GROUP BY link_ID
) imgs ON imgs.link_ID = a.link_id
INNER JOIN mt_cl d ON a.link_id = d.link_ID
WHERE d.cat_ID = '1'
I'm running this SQL query
$sql = "select images.image, images.comment as feedDescription,
customers.fullName, CONCAT('[', GROUP_CONCAT(DISTINCT likes.uid),']') as likes,
CONCAT('[', GROUP_CONCAT(DISTINCT CONCAT('{\"userid\":\"', comments.fid, '\", \"comment\":\"', comments.comment, '\"}') separator ','),']') as comments
FROM images
LEFT JOIN customers on images.client_id = customers.client_id
LEFT JOIN likes on images.image = likes.image
LEFT JOIN comments on images.image = comments.image
WHERE images.fid=:userID
ORDER BY images.image LIMIT $offset,$limit";
the only problem is that I am getting only the first row ...
I have images table, customers table (taking the name of the customer by the id i got in the images), likes table (people who did "like" on the image) and comments (people who wrote "comments" on the table)
You are using an aggregation function on a query, so MySQL is automatically returning only one row -- the aggregation of all the data.
In other databases, this would produce an error, because you have a mixture of aggregated and non-aggregated columns. This is a (mis)feature of MySQL called "hidden columns".
Add a group by to your query to fix the problem:
group by images.image, images.comment, customers.fullName
Be sure to add this after the WHERE clause and before the ORDER BY.