I have 3 tables the first one is tests which contains tested brand_id and model_id and brands which
contains car brands and lastly models which contains brand_id.
I want to show all brands and models and number of tests for respective model. If no tests than it should be zero.
This is what I achieved so far;
select
b.id,
b.brand_name,
m.model_name,
(select sum(coalesce(count(m.id), 0)) from test where test.brand_id = b.id and test.model_id = m.id) model_count
from
brands b
left join models m on
m.brand_id = b.id
left join test t on
t.brand_id = b.id
group by
b.brand_name,
m.model_name
Thanks for any help
You can use group by as follows:
select
b.id,
b.brand_name,
m.model_name,
count(t.brand_id) model_count
from brands b join models m on m.brand_id = b.id
left join test t on t.brand_id = b.id and t.model_id = m.id
group by b.id, b.brand_name, m.model_name
A correlated subquery is a perfectly reasonable way to solve this problem. But . . . you don't don't test in the outer query and the subquery can be much simplified:
select b.id, b.brand_name, m.model_name,
(select count(*)
from test t
where t.brand_id = b.id and t.model_id = m.id
) as model_count
from brands b left join
models m
on m.brand_id = b.id;
Under many circumstances, this will have better performance than the equivalent aggregation query because it avoids aggregation over the entire data set.
Related
We are maintaining a history of Content. We want to get the updated entry of each content, with create Time and update Time should be of the first entry of the Content. The query contains multiple selects and where clauses with so many left joins. The dataset is very huge, thereby query is taking more than 60 seconds to execute. Kindly help in improving the same. Query:
select * from (select * from (
SELECT c.*, initCMS.initcreatetime, initCMS.initupdatetime, user.name as partnerName, r.name as rightsName, r1.name as copyRightsName, a.name as agelimitName, ct.type as contenttypename, cat.name as categoryname, lang.name as languagename FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
left join Age a on c.ageLimit = a.id
left outer join (
SELECT contentId, createTime as initcreatetime, updateTime as initupdatetime from ContentCMS cms where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId WHERE c.deleted='0' order by c.id DESC
) as temp group by contentId) as c where c.editedBy='0'
Any help would be highly appreciated. Thank you.
Just a partial eval and suggestion because your query seems non properly formed
This left join seems unuseful
FROM ContentCMS c
......
left join (
SELECT contentId
, createTime as initcreatetime
, updateTime as initupdatetime
from ContentCMS cms
where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId
same table
the order by (without limit) in a subquery in join is unuseful because join ordered values or unordered value produce the same result
the group by contentId is strange beacuse there aren't aggregation function and the sue of group by without aggregation function is deprecated is sql
and in the most recente version for mysql is not allowed (by deafult) if you need distinct value or just a rows for each contentId you should use distinct or retrive the value in a not casual manner (the use of group by without aggregation function retrive casual value for not aggregated column .
for a partial eval your query should be refactored as
SELECT c.*
, c.initcreatetime
, c.initupdatetime
, user.name as partnerName
, r.name as rightsName
, r1.name as copyRightsName
, a.name as agelimitName
, ct.type as contenttypename
, cat.name as categoryname
, lang.name as languagename
FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
WHERE c.deleted='0'
) as temp
for the rest you should expiclitally select the column you effectively need add proper aggregation function for the others
Also the nested subquery just for improperly reduce the rows don't help performance ... you should also re-eval you data modelling and design.
I want to fetch one product information using joins from different tables. I have three additional tables (review,thread,award) and I'd like to check whether records exist relating to this specific product. If they exist, return a non-null value, otherwise null. There is a possibility that more of this type of checking will be added to the query in the future.
Which query would you prefer performance wise to test if records exists?
Using exists with multiple subqueries:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
EXISTS(SELECT 1 FROM review WHERE product_id = :id) AS has_review,
EXISTS(SELECT 1 FROM thread WHERE product_id = :id) AS has_thread,
EXISTS(SELECT 1 FROM award WHERE product_id = :id) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
WHERE p.product_id = :id
LIMIT 1";
$dbh->prepare($sql);
Using multiple left joins:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
(t.product_id is not null) AS has_thread,
(r.product_id is not null) AS has_review,
(a.product_id is not null) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
LEFT JOIN review r ON re.product_id = p.product_id
LEFT JOIN thread t ON t.product_id = p.product_id
LEFT JOIN award a ON a.product_id = a.product_id
WHERE p.product_id = :id
LIMIT 1";
The first is much preferable.
For performance, for either version, you want indexes on review(product_id), thread(product_id), and award(product_id).
Why is using EXISTS better? When no matching rows exist in the three tables, then the two versions should be equivalent (minus the typo in the last on clause on the second query). However, when rows do exist, then the second version will create cartesian products of those rows, throwing off both the results and performance.
Note: I would be inclined to write the EXISTS clause using correlated subqueries, so the parameter is only referenced once:
EXISTS (SELECT 1 FROM review r WHERE r.product_id = p.product_id) AS has_review,
EXISTS (SELECT 1 FROM thread t WHERE t.product_id = p.product_id) AS has_thread,
EXISTS (SELECT 1 FROM award a WHERE a.product_id = p.product_id) AS has_award,
I have three tables A,B,C.Their relation is A.id is B's foreign key and B.id is C's foreign key.I need to sum the value when B.id = C.id and A.id = B.id ,I can count the number by query twice. But now I need some way to count the summation just once time !
My inefficient solution
select count(C.id) from C,B where C.id = B.id; //return the value X
select count(A.id) from C,B where A.id = B.id; //return the value Y
select X + Y; // count the summation fo X and Y
How can I optimize ? Thks! :)
PS:
My question is from GalaXQL,which is a SQL interactive tutorial.I have abstract the problem,more detail you can check the section 17.SELECT...GROUP BY... Having...
You can do these things in one query. For instance, something like this:
select (select count(*) from C join B on C.id = B.id) +
(select count(*) from C join A on C.id = A.id)
(Your second query will not parse because A is not a recognized table alias.)
In any case, if you are learning SQL, the first thing you should learn is modern join syntax. The implicit joins that you are using were out of date 15 years ago and have been part of the ANSI standard for over 20 years. Learn proper join syntax.
Try Like This
select sum(cid) (
select count(*) as cid from C join B on C.id = B.id
union all
select count(*) as cid from A join B on A.id = B.id ) as tt
try this one:
select
(select count(*) from C join B on C.id = B.id)
union
(select count(*) from C join A on C.id = A.id)
I'm working with the following DB model
A client has asked me to make a few changes to their DB, I haven't played with relational databases in a few years, generally work with flat DB's
Could someone help me on my way with giving me an example of how the following query would work.
say if I wanted to
select all films with a title like '%Matrix%' under a certain genreID
Any assistance would be greatly appreciated
Use an inner join to join the three tables
SELECT F.title
FROM film F
INNER JOIN filmgenres FG
ON F.filmid = FG.film_filmid
INNER JOIN genres G
ON FG.genres_genreid = G.genreid
WHERE F.title LIKE '%Matrix%' AND G.genre = "Some Genre"
You need to join the tables first,
SELECT a.*, c.Genre
FROM Film a
INNER JOIN FilmGenres b
ON a.FilmID = b.Film_FilmID
INNER JOIN Genres c
ON b.Genre_GenreID
WHERE a.Title LIKE '%matrix%'
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
but if you want to search for a specific genre, then you might add a condition as well,
SELECT a.*, c.Genre
FROM Film a
INNER JOIN FilmGenres b
ON a.FilmID = b.Film_FilmID
INNER JOIN Genres c
ON b.Genre_GenreID
WHERE a.Title LIKE '%matrix%' AND c.GenreID = 10
SELECT Film.*
FROM Film f
JOIN FilmGenres fg ON g.Film_FilmID = f.FilmID
WHERE fg.Genres_GenreID = 3
AND Title LIKE '%Matrix%'
SELECT * FROM Film INNER JOIN FilmGenres ON Film.FilmID = FilmGenres.Film_FilmID
WHERE FilmGenres.Genres_GenreID = 1
AND
Film.Title like '%Matrix%'
It's a pretty simple join:
select
Film.*
from
Film
inner join FilmGenres on
Film.FilmID = FilmGenres.Film_FilmID
where
Film.Title like "%Matrix%" and
FilmGenres.Genres_GenreID = ?
Is it possible to INNER JOIN a MySQL query to achieve this result?
I have a table with Strategies and a table with Members. The Strategy table holds the ID of the author that corresponds to their ID in the Member table and the ID of an author that updated the existing author's work. Is it possible to grab a reference to both of these people at the same time? Something like the following, which returns no errors, but also no results...
SELECT * FROM Strategies
INNER JOIN Members AS a
INNER JOIN Members AS b
WHERE Strategies.ID='2'
AND Strategies.AuthorID = a.ID
AND Strategies.UpdateAuthorID = b.ID
Use a LEFT JOIN:
SELECT
s.*,
a.Name AS MemberName,
b.Name AS UpdatedMemberName
FROM Strategies AS s
LEFT JOIN Members AS a ON s.AuthorID = a.ID AND s.ID = 2
LEFT JOIN Members AS b ON s.UpdateAuthorID = b.ID AND s.ID = 2 ;
If you want them in one column use COALESCE:
SELECT
s.*,
COALESCE(a.Name, b.Name) AS MemberName
FROM Strategies AS s
LEFT JOIN Members AS a ON s.AuthorID = a.ID AND s.ID = 2
LEFT JOIN Members AS b ON s.UpdateAuthorID = b.ID AND s.ID = 2
SELECT toD.dom_url AS ToURL,
fromD.dom_url AS FromUrl,
rvw.*
FROM reviews AS rvw
LEFT JOIN domain AS toD
ON toD.Dom_ID = rvw.rev_dom_for
LEFT JOIN domain AS fromD
ON fromD.Dom_ID = rvw.rev_dom_from
if domain is table name