I have a query like this:
SELECT
*
FROM Product.Stock AS PS
INNER JOIN Product.Product AS P ON PS.ProductId = P.ProductId
INNER JOIN Product.ProductDetail AS PD ON P.ProductId = PD.ProductId
INNER JOIN Product.ProductSize AS PSI ON P.ProductId = PSI.ProductId
I want to know how many ProductsId with same SizeId I have, for example:
in this case I have two products with same ProductId and SizeId, so I wish get: 2 because I have 2 products with sizeId 1
ProductId comes from table: Product.Product
SizeId comes from table: Product.ProductSize
How can I achieve it? Regards
SELECT
PSI.SizeId, COUNT(DISTINCT P.ProductId)
FROM Product.Stock AS PS
INNER JOIN Product.Product AS P ON PS.ProductId = P.ProductId
INNER JOIN Product.ProductDetail AS PD ON P.ProductId = PD.ProductId
INNER JOIN Product.ProductSize AS PSI ON P.ProductId = PSI.ProductId
GROUP BY PSI.SizeId
I'm assuming that the SizeId column is in the Product.ProductSize table.
Agree with #Jack's answer, still I think joins seem to be redundant here.
SELECT
SizeId, COUNT(DISTINCT ProductId)
FROM Product.ProductSize
GROUP BY SizeId
Use Having Count in order to just show sizes with multiple products and no need for inner joins, since you already have all the fields in one table:
SELECT SizeId, COUNT(Distinct ProductId)
FROM Product.ProductSize
GROUP BY SizeId
Having Count(Distinct ProductId)> 1
Related
SO i have two tables:
Products
Product ID | Quantity
OrdersLines
Product ID | Amount (multiple lines with same ID)
I want to join two tables. Result should be - Product ID (Group by), Quantity and Sum of all amounts from OrdersLines table.
I got this so far:
SELECT P.ProductID, P.Quantity, SUM(OL.Amount)
FROM atbl_Sales_Products AS P
LEFT JOIN atbl_Sales_OrdersLines AS OL ON OL.ProductID = P.ProductID
GROUP BY P.ProductID
This produces error:
Column 'atbl_Sales_Products.Quantity' is invalid in the select list
because it is not contained in either an aggregate function or the
GROUP BY clause.
Thanks for help!
Well, there are two ways to write this depending on what you want
Adding the Quantity to the aggregate...
SELECT
P.ProductID,
SUM(P.Quantity),
SUM(OL.Amount)
FROM atbl_Sales_Products AS P
LEFT JOIN atbl_Sales_OrdersLines AS OL ON OL.ProductID = P.ProductID
GROUP BY P.ProductID
Adding the quantity to the grouping
SELECT
P.ProductID,
P.Quantity,
SUM(OL.Amount)
FROM atbl_Sales_Products AS P
LEFT JOIN atbl_Sales_OrdersLines AS OL ON OL.ProductID = P.ProductID
GROUP BY P.ProductID, P.Quantity
--based on comment
HAVING SUM(OL.Amount) > P.Quantity
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 have the following query. Given productid as input (2,4,5) i want to get keyid associate with that list. Then i want to get all products associated with that keyids.
For example:
suppose i am passing productid as 2,4,5 as input to my sproc, i will get keyids as 22,34,35,38 (CTE result). This keys are mapped to the input productlist. Based on this keys (CTE result) i want all the products associated to this keys. Say the keyid = 22 will now have product names with product id as 2,4,5,89 & keyid = 34 will now have products associated to 2,4,5,23,45 etc.
I came up with the following solution for the above problem. I am just hoping whether we could somehow improve this solution or do this job in single query as two tables are getting repeated.
WITH GetKey_CTE
AS
(
SELECT k.id, some other select statements
FROM KeyDim k
INNER JOIN KeyData kd on kd.id = k.id
INNER JOIN KeyProductMapping kpm on kpm.id = k.id and kpm.mkey = k.mkey
INNER JOIN Products p on p.productid = kpm.productid
and p.productid IN (2,4,5)
LEFT JOIN some more joins
WHERE clause conditions
)
SELECT cte.id as keyid, pn.productname, some other columns
FROM GetKey_CTE cte
INNER JOIN KeyProductMapping kpm on cte.id = kpm.id
INNER JOIN Products pn on pn.productid = kpm.productid
ORDER BY cte.id
Dataset Example for products and productkeymapping tables :
For Products table:
productid name
1 car
2 bike
3 plane
4 bus
5 train
45 cycle
ProductKeyMapping table
productid keyid
1 23
2 987
45 23
1 56
say input productid is 1, then final result should be:
keyid productid name
23 1 car
23 45 cycle
56 1 car
just looking at the data and that simple example
select pm2.*, product.name
from productmapping pm1
join productmapping pm2
on pm2.keyid = pm1.keyid
and pm1.productid in (1)
join product
on product.id = pm2.productid
declare #product table(id int, name varchar(20));
declare #map table(productid int, keyid int);
insert into #product values
(1, 'car'),
(2, 'bike'),
(3, 'plane'),
(4, 'bus'),
(5, 'train'),
(45, 'cycle');
insert into #map values
(1, '23'),
(2, '987'),
(45, '23'),
(1, '56');
select pm2.*, p.name
from #map pm1
join #map pm2
on pm2.keyid = pm1.keyid
and pm1.productid in (1)
join #product p
on p.id = pm2.productid
order by pm2.keyid;
you can also done by using sub query
SELECT * FROM KeyProductMapping km
INNER JOIN
(
SELECT k.id, some other select statements
FROM KeyDim k
INNER JOIN KeyData kd ON kd.id = k.id
INNER JOIN KeyProductMapping kpm ON kpm.id = k.id AND kpm.mkey = k.mkey
INNER JOIN Products p ON p.productid = kpm.productid
AND p.productid IN (2,4,5)
LEFT JOIN some more joins
WHERE clause conditions) AS p ON p.id = km.id
INNER JOIN Products pn ON p.productid = km.productid
ORDER BY cte.id
SELECT cte.id as keyid, pn.productname, some other columns
FROM ( SELECT k.id, some other select statements
FROM KeyDim k
JOIN KeyData kd
on kd.id = k.id
JOIN KeyProductMapping kpm
on kpm.id = k.id
and kpm.mkey = k.mkey
JOIN Products p
on p.productid = kpm.productid
and p.productid IN (2,4,5)
LEFT JOIN some more joins
WHERE clause conditions
) CTE
JOIN KeyProductMapping kpm
on cte.id = kpm.id
JOIN Products pn
on pn.productid = kpm.productid
ORDER BY cte.id
Above you you query with the CTE in line (a subquery)
A lot of stuff does not make sense to me
JOIN KeyProductMapping kpm
on kpm.id = k.id
and kpm.mkey = k.mkey
JOIN Products p
on p.productid = kpm.productid
and p.productid IN (2,4,5)
is the same as
JOIN KeyProductMapping kpm
on kpm.id = k.id
and kpm.mkey = k.mkey
and p.productid IN (2,4,5)
unless product does not have those values
why
SELECT k.id, some other select statements
FROM KeyDim k
JOIN KeyData kd
on kd.id = k.id
same as
SELECT kd.id -- move this to main select, some other select statements
FROM KeyData kd
why?
LEFT JOIN some more joins
move that to main statement
You can replace the CTE with an inline view like below but note sure why you are duplicating the JOINS again. Can you post some sample data along with your desired result to look further.
SELECT cte.id AS keyid, pn.productname, some other columns
FROM (
SELECT k.id, some other select statements
FROM KeyDim k
INNER JOIN KeyData kd ON kd.id = k.id
INNER JOIN KeyProductMapping kpm ON kpm.id = k.id AND kpm.mkey = k.mkey
INNER JOIN Products p ON p.productid = kpm.productid
AND p.productid IN (2,4,5)
LEFT JOIN some more joins
WHERE clause conditions ) cte
INNER JOIN KeyProductMapping kpm ON cte.id = kpm.id
INNER JOIN Products pn ON cte.productid = kpm.productid
ORDER BY cte.id
My first version of the question was confusing, I need to make smaller chunks.
If a user can filter products from a website, one product should occur only once in the list.
Because of joins this code gives me two same products, how do I solve that?
I think I need a solution without using distinct because it will give me headache later on.
code from AW2012:
declare #safetystocklevel int
set #safetystocklevel = 1000
declare #status int
set #status = 2
select * from Production.Product p
inner join Purchasing.ProductVendor pv on p.ProductID = pv.ProductID
inner join Purchasing.Vendor v on v.BusinessEntityID = pv.BusinessEntityID
inner join Production.ProductDocument pd on p.ProductID = pd.ProductID
inner join Production.Document d on d.DocumentNode = pd.DocumentNode
WHERE
(#safetystocklevel = '' or p.SafetyStockLevel = #safetystocklevel)
and (#status = '' or d.Status = #status)
output:
ProductId Name
506 Reflector
506 Reflector
Edit:
Thanks, I now use Group by to get distinct rows.
Yeah, maybe using group by works for me, Im gonna do some testing now.....
Hi again
I want all products to be searchable, so I guess I need left outer joins to achieve that.
When I add dynamic order by I get into trouble, more rows are added.
Probably because I must add poh.Status to the group by.
There are 504 rows in the product table, this query returns 776 rows.
(I have removed the filtering in WHERE since it is not interesting now, and Im joining to other tables now just to get more rows to play with)
Code:
declare #sortType nvarchar(50)
set #sortType = 'Status'
select p.ProductID,
CASE WHEN #sortType = 'Status' THEN poh.Status END as Status,
CASE WHEN #sortType = 'ProductId' THEN p.ProductID END as ProductId
from Production.Product p
left outer join Purchasing.PurchaseOrderDetail pod on p.ProductID = pod.ProductID
left outer join Purchasing.PurchaseOrderHeader poh on poh.PurchaseOrderID = pod.PurchaseOrderID
left outer join Production.ProductDocument ppd on ppd.ProductID = p.ProductID
left outer join Production.Document pd on pd.DocumentNode = ppd.DocumentNode
group by p.ProductID, poh.Status
ORDER BY
CASE WHEN #sortType = 'Status' THEN poh.Status END ASC,
CASE WHEN #sortType = 'ProductId' THEN p.ProductID END ASC
You can use Group By ProductId, Name, to select the single row, if you are not planning to include distinct. But I'll prefer "distinct" if you are not using any aggregate value in select clause.
select p.ProductId, p.Name from Production.Product p
inner join Purchasing.ProductVendor pv on p.ProductID = pv.ProductID
inner join Purchasing.Vendor v on v.BusinessEntityID = pv.BusinessEntityID
inner join Production.ProductDocument pd on p.ProductID = pd.ProductID
inner join Production.Document d on d.DocumentNode = pd.DocumentNode
WHERE
(#safetystocklevel = '' or p.SafetyStockLevel = #safetystocklevel)
and (#status = '' or d.Status = #status)
GROUP BY p.ProductId, p.Name
I have four tables, but for this I only need three tables, and I want to display the customerid, orderid, productid, quantity and the total cost which isn't in any table but need to calculate? so far I have managed to get it to display total cost for one order with the order id, but I want it to display all the columns mentioned above
Order:
order_id(primary key)
customer_id( foreign key)
orderline:
orderid(fk) + productid(fk) (pk)
quantity
product:
productid(pk)
price
What I have done is
select orderid, sum(rowcost) as totalcost
from (select o.quantity, o.productid, o.orderid, os.customerid, p.price,
(o.quantity * p.price) as rowcost
from orderline o
inner join order os
on o.orderid = os.orderid
inner join product p
on p.productid = o.productid
where productid = 123)
group by orderid;
Now I want it to display all the orderids along with the productid, customerid, totalcost, orderid and quantity. The list should follow customerid order.
How would I do this?
when I add more variables in the select, it gives me errors. I have tried many ways, none of them worked.
do you mean you want something like this:
select o.quantity, o.productid, o.orderid, os.customerid, p.price,
(o.quantity * p.price) as rowcost,
sum(o.quantity * p.price) over (partition by os.customerid) as totalcost
from orderline o
inner join order os
on o.orderid = os.orderid
inner join product p
on p.productid = o.productid
where p.productid = 123
or this, to keep the sum correct if you wanted to filter afterwards on a product
select *
from (select o.quantity, o.productid, o.orderid, os.customerid, p.price,
(o.quantity * p.price) as rowcost,
sum(o.quantity * p.price) over (partition by os.customerid) as totalcost
from orderline o
inner join order os
on o.orderid = os.orderid
inner join product p
on p.productid = o.productid)
where productid = 123--or just remove this where clause
fiddle: http://sqlfiddle.com/#!4/3103d/1