check if the column value exists in subquery - sql

i have 3 tables Product Category and ProductCategory.
Product table:
ProductID ProductName
1 P1
2 P2
3 P3
Category table:
CategoryID CategoryName
1 C1
2 C2
3 C3
ProductCategory:
ProductID CategoryID
1 1
1 2
1 3
2 3
3 1
3 2
I need a query which returns products which fall under more than 1 categories. Based on the table data above the result would be:
ProductID ProductName
1 P1
3 P3
So i wrote a query to fetch all the ProductID's which have more than one CategoryID's as below:
select ProductID,count(CategoryID)
from ProductCategory
group by Productid
having count(CategoryID)>1)
But when i try to display product details using the below query i get an error:
select *
from Product
where ProductID in (
select ProductID,count(CategoryID)
from ProductCategory
group by Productid
having count(CategoryID)>1))
Is my query wrong? How do i get the required product details which fall in more than one categories?

Remove the COUNT() in the subquery. The result of the subquery when used on IN clause must have only one returned column.
SELECT *
FROM Product
WHERE ProductID IN
(
SELECT ProductID
FROM ProductCategory
GROUP BY Productid
HAVING count(CategoryID) > 1
)
SQLFiddle Demo
or by using JOIN
SELECT a.*
FROM Product a
INNER JOIN
(
SELECT ProductID
FROM ProductCategory
GROUP BY Productid
HAVING count(CategoryID) > 1
) b ON a.ProductID = b.ProductID
SQLFiddle Demo

You can try use CROSS APPLY Operator in SQL Server
SELECT DISTINCT C.ProductID,C.ProductName,A.CategoryID,A.Total
FROM Product C
CROSS APPLY (
Select CA.CategoryID,Total=COUNT(*)
From ProductCategory CA
Where C.ProductID=CA.ProductID
Group By CA.CategoryID Having COUNT(*)>1
) AS A
ORDER BY A.Total DESC
Take a look: http://explainextended.com/2009/07/16/inner-join-vs-cross-apply/

Related

JOIN Products that are IN another table

I tried to join some Products based on if they're in a table or not. I'm using MSSQL and I'm trying to do this to see if the category has some products.
simplified Query:
SELECT c.CategoryID, c.Name, p.ProductID
FROM Category AS c
JOIN Product AS p ON p.ProductID IN (
SELECT PrductID FROM exampleTable
)
ProductTable:
ProductID
CategoryID
1
1
2
1
3
2
4
4
The output I receive:
CategoryID
Name
ProductID
1
Cat1
1
1
Cat1
2
2
Cat2
3
4
Cat4
4
The expected output:
CategoryID
Name
ProductID
1
Cat1
1
2
Cat2
3
4
Cat4
4
I'm trying to only join a product if it's in the select statement and not join all products which have the same category id.
In pseudo code I'm trying to achive this:
JOIN Product AS p IF p.ProductID IN (Subquery)
Is this somehow possible?
Ed banga's answer is IMHO more elegant and perfoment but to be closer to what you proposed in your question, you can simply use a where clause.
SELECT c.CategoryID, c.Name, p.ProductID
FROM Category AS c
JOIN Product AS p ON p.CategoryID = c.CategoryID
WHERE p.ProductID IN (
SELECT PrductID FROM exampleTable
)

Group by having count less than n or null should return true

I want this query to return 1 also in the case when no record exists. With a count less than 2 or even if no records exist it should return 1.
SELECT 1
FROM dbo.Product BS
WHERE BS.ASIN = '072142452X'
GROUP BY ASIN
HAVING COUNT(1) < 2
The result should be 1 in the case when no record exists in the table.
List of All Products In table
Product
Id ASIN
1 12A
2 12B
3 1AC
4 123
5 12D
List of Accounts
Account
Id Name
1 A
2 B
3 C
Now Listing happens in various accounts
List of products Listed in various accounts (Product Id and Account combination is unique. Means one product will be listed in one account only once)
ProductListing
Id ProductId AccountId
1 2 1
2 1 1
3 4 1
4 2 2
5 4 2
6 5 3
I want if a product is already listed in one account or in two different accounts it should not come in my query of listing product
Query of Listing product in Account 3. So products which are already listed in 3 and in more than 2 accounts should not come
Select P.Id, P.ASIN
FROM dbo.Product P
WHERE NOT EXISTS (
SELECT 1
FROM dbo.ProductListing PL
WHERE P.Id = PL.ProductId AND AccountId = 3
) AND EXISTS (
SELECT 1
FROM dbo.ProductListing PL
WHERE P.Id = PL.ProductId
GROUP BY PL.ProductId
HAVING COUNT(1) < 2
)
You can use a CASE expression on the number of rows returned as follows.
DECLARE #Asin varchar(10) = '072142452X';
SELECT
CASE WHEN (
SELECT COUNT(*)
FROM dbo.Product BS
WHERE BS.[ASIN] = #Asin
) < 2 THEN 1 ELSE 0 END;
Note: there is no reason to GROUP BY when you are already filtering down to 1 ASIN.
Based on your edit you can modify your query to use the same sub-query as I have already show as follows:
SELECT P.Id, P.[ASIN]
FROM dbo.Product P
WHERE NOT EXISTS (
SELECT 1
FROM dbo.ProductListing PL
WHERE PL.ProductId = P.Id AND AccountId = 3
) AND (
SELECT COUNT(*)
FROM dbo.ProductListing PL
WHERE PL.ProductId = P.Id
) < 2;
Drop your GROUP BY line.
Consider how this will always give a row:
SELECT COUNT(1)
FROM dbo.Product BS
WHERE BS.ASIN = '072142452X'
But GROUP BY means you return a row per group, and you don't have any groups because you're filtering out the rows that would've made one.
So this is what you want.
SELECT 1
FROM dbo.Product BS
WHERE BS.ASIN = '072142452X'
Having Count(1) < 2
ADDENDUM: I wrote it this way because it's the closest to the OP's original form. But another option, which doesn't get caught up in the nuances of GROUP BY and HAVING is:
SELECT 1
WHERE (
SELECT COUNT(1)
FROM dbo.Product BS
WHERE BS.[ASIN] = '072142452X'
) < 2

Select all column values as total and multiply it

This is my DB structure:
Invoice
ID
Company
InvoiceLine
LineID
Quantity
Price
These are my rows:
Invoice
ID Company
1 XYZ
2 ZYX
InvoiceLine
LineID InvoiceID quantity price
1 1 1 10
2 1 5 10
3 2 1 20
What I'm trying to generate is to select my total invoice price dynamically:
IDEAL RESULT
1 XYZ 60
2 ZYX 20
I use the following query for this:
select ID, cil.quantity * cil.unitPrice as invoiceTotal from Invoice ci
join InvoiceLine as cil on ci.invoiceID = cil.invoiceID
Problem is that this query returns 2 rows for the first invoice.
Why is this and what how could I select ALL values of my invoice lines?
You want group by:
select ci.id, sum(cil.quantity * cil.unitPrice) as invoiceTotal
from Invoice ci join
InvoiceLine cil
on ci.invoiceID = cil.invoiceID
group by ci.id;
However, you don't even need the join:
select cil.id, sum(cil.quantity * cil.unitPrice) as invoiceTotal
from InvoiceLine cil
group by cil.id;
select Invoice.id,Invoice.Company sum(InvoiceLine.quantity * InvoiceLine.unitPrice) as TotalSum
from Invoice join
InvoiceLine cil
on Invoice.invoiceID = InvoiceLine.invoiceID
group by Invoice.id,Invoice.Company
order by Invoice.Company ;

How to write Group by in Inner Join

Here I Have Two tables:
Orders
OrderId OrderName
1 Apple
2 Mango
Cust
Id Name OrderId Price
1 John 1 50
2 John 1 100
3 Mic 1 10
4 Mic 2 10
Sql Join Query:
SELECT Orders.CustName,Items.IteamName,Orders.Price
FROM Orders JOIN Items ON Items.Id = Orders.Id
Group By
SELECT Orders.CustName, SUM(Price) FROM Orders GROUP BY Orders.CustName
How can I Write Group by in Join?
You can do like this:
Select Orders.CustName, Items.IteamName, SUM(Orders.Price)
FROM
Orders JOIN Items ON Items.Id=Orders.Id
GROUP BY
Orders.CustName, Items.IteamName
The fields that you doesn't aggregate you put at the GROUP BYclause.

How to select rows from a group using a condition

I have a table which contains data in this format.
ProductID ShipId
11 1
11 2
11 3
22 1
22 2
33 1
33 2
Now I want only the distinct product ids where ship id 3 is not associated.
Output should be
22,33 only.
I have used this query but it throws error.
Select distinct productid from X_product_ship group by productid having shipid <> 3
Please help.
Use a subquery in the where clause to exclude the products that has a shipid of three.
select distinct P1.ProductID
from dbo.X_product_ship as P1
where P1.ProductID not in (
select P2.ProductID
from dbo.X_product_ship as P2
where P2.ShipId = 3
)
SQL Fiddle
Or you could get creative in the having clause using a case statement.
select P.ProductID
from dbo.X_product_ship as P
group by P.ProductID
having max(case when P.ShipId = 3 then 1 else 0 end) = 0
SQL Fiddle