Selecting only rows that have matching column values - sql

so I have the task of returning the a companies information if and only if ALL of their products have been discontinued.
I have a Suppliers and a Products table. The Suppliers table has a ProductID column and the Products table has a ProductID and a Discontinued column that stores a bit (1 being true or 0 being false).
If anyone has a solution to this, that would be a life saver.
EDIT: the query I'm working with would be something like this
select
s.CompanyName, p.ProductName, p.Discontinued
from
Suppliers s
join
Products p on s.SupplierID = p.SupplierID
and the output would be something like this
CompanyName ProductName Discontinued
-------------------------------------------------------------
Exotic Liquids Chai 0
Exotic Liquids Chang 0
Exotic Liquids Aniseed Syrup 0
New Orleans Cajun Delights Chef Anton's Cajun Seasoning 0
New Orleans Cajun Delights Chef Anton's Gumbo Mix 1
Grandma Kelly's Homestead Grandma's Boysenberry Spread 0
Grandma Kelly's Homestead Uncle Bob's Organic Dried Pears 0
Grandma Kelly's Homestead Northwoods Cranberry Sauce 0
Tokyo Traders Mishi Kobe Niku 1
Tokyo Traders Ikura 0
but I only want it to return the suppliers with all discontinued products

I'm going out on a limb here, but I think you're trying to JOIN two tables on the ProductID.
If that's the case, then you SQL query would look something like this:
SELECT a.ProductID, b.Discontinued FROM Suppliers a
LEFT JOIN Products b ON (a.productID = b.productID)
WHERE b.Discontinued = true

select Suppliers.SupplierID, CompanyName from Suppliers
inner join Products on
Products.SupplierID = Suppliers.SupplierID
where Discontinued = 1
and Products.SupplierID not in
(select SupplierID from Products where Discontinued = 0)
group by Suppliers.SupplierID, CompanyName

Become a better developer in every language you use. Stop trying to do everthing at once. Break the problem into digestible pieces. And learn how to post good questions. You have a query question - how do you expect others to understand your issue and supply useful suggestions without knowing your schema and understanding how you use it. Sure - it's fairly simple in this case but a script that generates your tables, populates them with sample data, and includes whatever you have tried encourages others to respond.
So the first step is to answer the question "which supplies provide only discontinued products". Apparently you have all the information you need in Product. Something like:
select P.SupplierID from dbo.Products as P
group by P.SupplierID
having min(cast(P.Discontinued as tinyint)) = 1
order by P.SupplierID;
I think that is correct but it is still a guess. Sometimes a bit column gets "used" in opposition to the name. What does that do? For each group generated by the group by clause (supplier ID), it will determine the min value of Discontinued (casting is necessary since you cannot use min/max with bit). For a supplier with all discontinued products, that column must be 1 for all associated rows. Since 1 > 0, that means that all products of a supplier are discontinued. Grouping also gives us a distinct set of rows.
Now that you know how to generate a distinct set of SupplierID values, you should be able to apply that to any other query to get information about those suppliers. You can join, use IN, use exists - try all three if you want to really make this a learning experience.

Related

AdventureWorks - Selling Price Problem - Queries Microsoft SQL Server

Just looking for someone who has downloaded AdventureWorks data and done queries with them.
I was looking for someone to explain the difference between list price and unit price.
I filtered to productid 749 and 83% of the time it is being sold to the customer with listprice = unitprice.
I did some digging to see if there were any discounts etc. with the below query which did not come up with an answer. Is there something I am missing?
select *
from sales.specialoffer
where SpecialOfferID = 1;
select SOH.customerID,
SOH.orderdate,
pp.listprice,
sod.unitprice,
sod.ProductID,
sod.SpecialOfferID,
SOD.UnitPriceDiscount,
sr.SalesReasonID,sr.name,
sr.ReasonType
from sales.SalesOrderHeader SOH
inner join sales.SalesOrderDetail SOD
on soh.SalesOrderID = sod.SalesOrderID
inner join production.Product PP
on SOD.ProductID= PP.ProductID
left join sales.SalesOrderHeaderSalesReason SOHSR
on soh.SalesOrderID = sohsr.SalesOrderID
left join sales.SalesReason SR
on SOHSR.SalesReasonID = SR.SalesReasonID
where standardcost >0
and PP.listprice != sod.unitprice
and pp.productid = 749
;
This is really an accounting question. List price, without any further attributes, is generally the "current" price as of now. This value will typically change over time. When you sell (or buy) something, you capture the price of each item sold (as well as other information) with the details of each sale - which is the price you find in the SOD table. Why? For very important accounting reasons.
So no - you aren't missing anything. BTW - did you notice a table called ProductListPriceHistory? So again - the difference you see is a current (or "now") fact versus an historical fact.
Lastly, don't expect a sample database to be completely consistent with respect to all the information it contains. The sample database was built to demonstrate various features of sql server and to serve as a learning platform. FWIW this database is quite dated. MS has developed WorldWideImporters as a replacement.

Oracle, SQL Conditional exclusion based on the items in the joined table

The following query excludes all the products however, I am trying to exclude the products "only if" the R.OPERATING_UNITS = 'WP' and PRODUCT_CAT = 'FUEL' in the joined table. I don't know how to condition that. I wanted to know what is the best efficient way to do that. Below is the query, the RESOURCE, PRODUCT table and also the desired result set. I simplified both the tables and query for the sake of explanation.
SELECT R.DEPTID,
R.FISCAL_YEAR,
sum(R.AMOUNT) total
FROM RESOURCE R
WHERE
R.PRODUCT_ID NOT IN (
SELECT PRODUCT_ID FROM PRODUCT WHERE PRODUCT_CAT='FUEL' )
group by R.FISCAL_YEAR,R.DEPTID
the RESOURCE table
DPTID FISCAL_YEAR OPERATING_UNIT AMOUNT PRODUCT
PTT 2017 WP 1200 31000
PTT 2017 SP 3000 32000
PTT 2017 GP 1000 31000
PTT 2017 WP 1000 32000
FPP 2017 WP 1000 32000
FPP 2018 GP 2000 33000
FPP 2017 SP 1000 32000
FPP 2018 WP 2200 31000
PRODUCT Table:
PRODUCT PRODUCT_CAT
31000 FUEL
32000 NON-FUEL
33000 MATERIAL
Result set. Note that it is ignoring WP when calculating the sum.
2017 PTT 5000 (igonred 1200 since operating unit=wp and product is 31000->FUEL but included wp and 32000)
2017 FPP 2000
2018 FPP 2000 (it did not consider the 2200 since operating unit=wp and product is 31000->FUEL)
WP filter should work after you change below statement
NOT IN (
SELECT PRODUCT_ID FROM PRODUCT WHERE PRODUCT_CAT='FUEL' )
and then you can filter operating unit.
SELECT R.DEPTID,
R.FISCAL_YEAR,
sum(R.AMOUNT) total
FROM RESOURCE R
WHERE
r.OPERATING_UNIT = 'WG' and
R.PRODUCT_ID IN
(
SELECT PRODUCT_ID FROM PRODUCT WHERE PRODUCT_CAT='FUEL' )
group by R.FISCAL_YEAR,R.DEPTID
Sticking in a not-equal to clause with an OR in-between should filter out the cases where both OperatingUnit = 'WP' & ProductCat = 'Fuel'
SELECT r.DEPTID
,r.FISCAL_YEAR
,SUM(r.AMOUNT) AS TOTAL
FROM [Resource] r
INNER JOIN [Product] p ON r.PRODUCT = p.PRODUCT
WHERE r.OPERATING_UNIT != 'WP'
OR p.PRODUCT_CAT != 'FUEL'
GROUP BY r.DEPTID
,r.FISCAL_YEAR
I used the following query below to view the data and verify that it's returning the 6/8 rows I wanted.
SELECT *
FROM [Resource] r
INNER JOIN [Product] p ON r.PRODUCT = p.PRODUCT
For ease of writing - and reading - the exclusion condition, it would be nice if we could work with tuples. And we can. One benefit is that it will be easy, in the future, to add other pairs of operating unit and product category to the exclusion list, without having to write lengthy conditions with lots of OR and AND.
If you run a query like this, and then you take a look at the EXPLAIN PLAN for the query, you will see that the parser expanded the tuple condition to a long logical expression with OR (and AND, if more than one tuple is excluded) - so the end result is the same, but the code looks more natural.
select r.deptid, r.fiscal_year, sum(r.amount) as total
from resource r inner join product p on r.product = p.product
where (r.operating_unit, p.product_cat) not in ( ('WP', 'FUEL') )
group by r.deptid, r.fiscal_year
;
Regarding NULL: if either r.operating_unit or p.product_cat can be NULL, you need to state how they should be handled. If, for example, the operating unit is WP but the product category is NULL, the corresponding row will be excluded in the query above. That may be the proper handling: the unit is definitely 'WP', and since we don't know the product category, we must make a decision. Since it may be 'FUEL', we just don't know for sure, we may choose to exclude it. Obviously, if both columns are NOT NULL then this is not an issue.
Note - I hope you don't really have a table PRODUCT with a column PRODUCT; that will lead to confusion which almost always then leads to bugs.

Query AND and not OR using WHERE IN (list)

Considering I have three tables:
product
shop
supplier
and their junction tables:
product_shop
product_supplier
I am trying to get all products that are supplied by a specific supplier and available in multiple shops, i.e. all products from supplier A that are in shop 1 and 2.
If we assume, supplier has 10 products, of which 5 are available in shop 1 and 7 are available in shop 2, but only 3 are available in shop 1 and 2, then those are the 3 I am seeking.
Here is what I have so far (in postgresql 9.6), which simulates an inclusive IN. Which works, but it doesn't seem right (proper) to me:
edit:
I use this query dynamically and have therefor to pass the list of shops (which varies), the supplier of course, and the count (which is equal to the length of the list of shops). It's not that this is a bother, but I was nonetheless wondering if there is a way to create a query using only joins.
SELECT
product_shop.product
FROM
(SELECT product FROM product_shop where shop in (1, 2)) product_shop JOIN
product_supplier ON product_shop.product = product_supplier.product
WHERE
product_supplier.supplier = A
GROUP BY
product_shop.product
HAVING
count(product_shop.product) = 2
Is there a better query to achieve the result I am seeking?
*Not a native speaker
I believe this will give you the desired result
SELECT product.* FROM product
JOIN product_supplier ON product.id = product_supplier.productId
JOIN product_shop ON product.id = product_shop.productId
WHERE product_shop.shopId IN (1 , 2)
AND product_supplier.supplierId = 'A'
GROUP BY product.id
HAVING COUNT(product.id) > 1
Its basically equivalent, but you do not need the subquery. There is, however, an error.
With your query you will only get the products that are available in exactly 2 shops.
since you wrote:
I am trying to get all products that are supplied by a specific supplier and available in multiple shops
Using HAVING COUNT(product.id) > 1 would be more generic and correct.
However, it would not make any difference in this case, since you restricted the search to 2 shops.
I Also changed references like product_supplier.supplier to product_supplier.supplierId. You shroud use Id fields for table relationships.
Here is the query with out Having the count
with cte_product -- fetching all the products which are in shop 1&2
(
SELECT product,count(product) as cnt FROM product_shop
where shop in (1, 2)
group by product
)
SELECT *
FROM
cte_product psh
JOIN
product_supplier psu
ON psh.product = psu.product
WHERE
psu.supplier = A
and psh.cnt > 1;

SQL displaying the right number, names

I'm working on a Coffee Shop database and trying to find number of sales per item. The number comes back correctly, but instead of displaying the name of the coffee and the sales it displays all the coffee names with the correct data of the first drink, then all of the coffee names with the correct data of the second drink.
select p.ProductName, TotalSold = SUM(o.Quantity)
From MSProducts p, MSOrderline o
Group By p.ProductName, o.ProductID
Output should be...
1 FlavoredSyrup-Shot 11
2 ExtraExpresso 7
3. Americano-Small 5
Though it didn't fit on the page it continues with the quantity 5 below.
I think the lack of join condition was the cause of data duplication. This may work, provided you have ProductID in both tables.
select p.ProductName, SUM(o.Quantity) as TotalSold
From MSProducts p
inner join MSOrderline o
on p.ProductID = o.ProductID
Group By p.ProductName
You need to map both the tables correctly.
MSProducts p, MSOrderline o
If you dont map then all the rows of first table will be mapped with all the rows of the second table. Please map it using the common column

SQL Query Daily Sales Per Month of a specific product

I've got a query which gets the lists of customers who've made a purchase of the product for the current day
select Customer.customerName, sum(InvoiceDetail.itemPrice * InvoiceDetail.itemQuantity) as dailyPurchase from Invoice
inner join InvoiceDetail on Invoice.invoiceID = InvoiceDetail.invoiceID
inner join Customer on Invoice.customerID = Customer.customerID
inner join Item on InvoiceDetail.itemID = Item.itemID
inner join Branch on Branch.branchID = Invoice.branchID
inner join Region on Region.regionID = Branch.regionID
where
Invoice.inactive = 0 and InvoiceDetail.inactive = 0
and Item.itemTypeID = 3
and Region.regionCode = 'CR'
and cast(Invoice.invoiceDate as date) = convert(date, '01/08/2016', 103)
group by Customer.customerName
What I need is a table with a list of all the dates in the current month, listing ALL customers who have at least purchased the product ONCE. It should resemble something similar to this image here.
Any help on how to get started or a general idea of how to get the desired result, is much appreciated. Thanks!
Sample Data from Results:
customerName dailyPurchase
AGH COMMUNICATIONS 450.00
ARIEL AMARCORD SHOP 285.00
AKN COMMUNICATION 300.00
AWSDAC TELECOMMUNICATION 2850.00
BARLEY MOBILE & SERVICES 285.00
Table Structure - I'm sorry, I don't know an easier way to copy this.
First get the customers who have purchased the product atleast once this month alongwith date.
Then use pivot to get the result in the form that you want (as seen in image). Search stackoverflow for pivot in sql server, you will get good info.
Give some information about the table structure and sample data and I might be able to help you with the query to get the results.