I'm coding a movies web app, for showing some tags I'm trying to write a query that returns the movie name and the count of how many categorys has assigned it. I'm trying to add a filter that for example: if X movie contains a "Comedy" category this movie doesn't even need to be consider in my query.
At the moment this is the query that I have:
SELECT A.*, B.*, C.* -- A.name, count(C.name) [Categories]
FROM movies A
INNER JOIN moviesGenres B ON A.id = B.movieId
INNER JOIN genres C ON B.genreId = C.id
WHERE C.name <> 'Comedy'
-- group by A.name, C.name
-- having count(C.name) > 2
At the moment this query is working to return the expected output. But if you run that query with the data in this SQL Fiddle you'll see that is considering the movie "Bad Boys" but this movie has assigned one "Comedy" category so any data from this movie should't be considered.
You need to put that condition in the having clause wich is the where clause of a group.
SELECT m.name, count(g.name) [Genres]
FROM movies m
INNER JOIN moviesGenres mg ON m.id = mg.movieId
INNER JOIN genres g ON mg.genreId = g.id
GROUP BY m.name
HAVING sum(case when g.name = 'Comedy' then 1 else 0 end) = 0
If you really want to select columns from [moviesGenres] and [genres], go for juergen's answer. If you don't need them, anti join them:
SELECT A.*, B.*, C.* -- A.name, count(C.name) [Categories]
FROM
movies A
WHERE NOT EXISTS
(
SELECT 1
FROM
moviesGenres Bneg
inner join genres Cneg ON Bneg.genreId = Cneg.id
WHERE
A.id = Bneg.movieId
and Cneg.name = 'Comedy'
)
Related
Working on a somewhat large query trying to improve performance. One thing I found is that there is a spot with a subquery, but the inner query has the same table as the outer query, with the same alias. Is this defined behavior?
select * from documents d
left join ( select distinct d.id, 'Yes' as 'IsCertainType' from documents d
left join documentAttributes da on d.id = da.id where da.Description like '%CertainType%'
) #certainType
on d.Id = #certainType.Id
It is fine as the inner and outer queries have different scopes
I see no reason for the subquery at all. Just do the Left JOIN and be done with it.
select distinct d.id, 'Yes' as 'IsCertainType'
from documents d
left join documentAttributes da
on d.id = da.id
where da.Description like '%CertainType%'
OR As stated in my comment if other rows with the same id in table documents has different values than the returned row by distinct, you can do GROUP BY
select d.id, d.someColum, etc. 'Yes' as 'IsCertainType'
from documents d
left join documentAttributes da
on d.id = da.id
where da.Description like '%CertainType%'
GROUP BY d.id, d.someColumn, etc..
I have a list of IDs, and each has multiple [Code] values (in the Code table)
I want to narrow down to IDs that have the following SUBSTRING(Code, LEN(Code) - 3, 4) <> '.003'. My query below:
SELECT m.ID, c.Code FROM MatchID m
LEFT JOIN Code c WITH(NOLOCK)
ON c.ID = m.ID
GROUP BY m.ID, c.Code HAVING SUBSTRING(c.Code, LEN(c.Code) - 3, 4) <> '.003'
ORDER BY m.ID
In this case the GROUP BY HAVING isn't narrowing down to the IDs, but instead just it's rows that don't have Codes ending in '.003'
How do I modify this so it pulls IDs that have no .003 Codes at all?
You must group by ID and put the condition in the HAVING clause:
SELECT m.ID
FROM MatchID m LEFT JOIN Code c
ON c.ID = m.ID
GROUP BY m.ID
HAVING SUM(CASE WHEN RIGHT(c.Code, 4) = '.003' THEN 1 ELSE 0 END) = 0
ORDER BY m.ID
I used the function RIGHT() which is simpler.
Since you have a LEFT JOIN this code will also return unmatched IDs.
If you want only the matched IDs change it to INNER JOIN.
Why you are using group by without any aggregate function in select clause?
Why you are filtering rows with HAVING clause?
Where clause is used to filter rows.
SELECT m.ID, c.Code
FROM MatchID m
LEFT JOIN Code c WITH(NOLOCK) ON c.ID = m.ID
WHERE c.Code NOT LIKE '%.003%'
ORDER BY m.ID
I'm working with the northwind database and my exercise is:
Which suppliers offer two products in the same category? Show company name, category and the both product names
My code:
SELECT DISTINCT
c.CategoryID, s.CompanyName, p1.ProductName, p2.ProductName
FROM
Suppliers s
INNER JOIN
Products p1 ON s.SupplierID = p1.SupplierID
INNER JOIN
Products p2 ON p1.CategoryID = p2.CategoryID
AND p1.ProductID <> p2.ProductID
INNER JOIN
Categories c ON p2.CategoryID = c.CategoryID
GROUP BY
c.CategoryID,s.CompanyName, p1.ProductName, p2.ProductName`
How can I filter that with COUNT() I tried to do it with HAVING but I failed.
I'll appreciate some help, which bringing me back on the right way.
Building on Gordon's answer the code below will get all the data you need. If you absolutely have to have both products in the same row, you can use pivot:
select s.CompanyName
,p.ProductName
from Suppliers s
-- This join filters your Suppliers table to only those with two Products in the same Category
inner join (select SupplierID
,CategoryID
from Products
group by SupplierID
,CategoryID
having count(1) = 2
) pc
on(s.SupplierID = pc.SupplierID)
-- This join returns the two products in the Category returned in the join above.
inner join Products p
on(s.SupplierID = p.SupplierID
and pc.CategoryID = p.CategoryID
)
You can get the list of suppliers/categories with exactly two products using a query like this:
select supplierId, categoryId
from products
group by supplierId, categoryId
having count(*) = 2;
Then, write a query to show the supplier and products names, and use the above to filter the results from that query. You can use either exists or an additional join.
I'm trying to display the data so that only one line is displayed per customer, i'm having trouble with trying to achieve that with my code as its returning all records, can anyone help
SELECT customerOrdrs.NAME AS 'Name',
customerOrdrs.currentbalance -
Sum(COALESCE(customerOrdrs.revisedbalance, 0)) AS 'RevisedBalance',
sold AS 'NumberOfItemsSold'
FROM customers,
(SELECT c.NAME AS NAME,
c.balance AS CurrentBalance,
i.qty AS RevisedBalance,
( Min(s.price) * i.qty ) AS Sold
FROM customers c
INNER JOIN sales o
ON c.NAME = o.custname
INNER JOIN purchases i
ON i.orderno = o.orderno
INNER JOIN contracters s
ON i.item = s.item
GROUP BY c.NAME,
c.balance,
i.qty) customerOrdrs
GROUP BY customerOrdrs.NAME,
customerOrdrs.currentbalance,
sold
I'm not sure how your data looks but I have reformatted the query and there are a few things I've noticed off the bat.
I have removed the subquery as I don't believe it is necessary - in addition your original query is referring to customer table twice without defining a join
Select [C].[Name] As [Name]
, [CurrentBalance] = [C].[Balance]
, [RevisedBalance] = [C].[Balance] - Sum([P].[Qty])
, [Sold] = ( Min([CO].[Price]) * sum([P].[Qty]) )
From [CUSTOMERS] [C]
Inner Join [Sales] [s]
On [C].[Name] = [s].[custName]
Inner Join [Purchases] [P]
On [P].[OrderNo] = [s].[OrderNo]
Inner Join [Contracters] [CO]
On [P].[Item] = [CO].[Item]
Group By [C].[Name]
, [C].[Balance];
Im trying to select from 2 tables that have the same columns, but both tables have an inner join -
select e.ID,
c.FullName,
car.VIN,
car.Registration,
e.Telephone,
e.Mobile,
e.Email,
e.EstimateTotal,
e.LastUpdated,
e.LastUpdateBy from (select id from Estimates UNION ALL select id from PrivateEstimates) e
inner join Customers c on c.ID = e.CustomerID
inner join Cars car on car.ID = e.CarID
where e.Status = 0
The trouble is, it can't find e.CustomerID, e.CarID or e.Status on the inner join? Any ideas?
Your subquery
select id from Estimates
union all
select id from PrivateEstimates
returns only a single id column. Include necessary columns in the subquery if you want to use those columns in JOIN statements