Products with only one available colour in stock - SQL query - sql

I have a table that lists thousands of products. A product can be either Standard (123450.000.000), one colour (123456.BLA.000), one size (123456.000.LAR) or both colour and size (123456.BLA.LAR).
A product can have multiple colours (123456.BLA.000, 123456.YEL.OOO etc etc). I am trying to do a query that brings back a product that has multiple colours but only one colour in stock i.e. ProductQTY = 1 and the remaining colours are out of stock.
All I have been able to come up with is the query below but this just brings back all 'variant' products that have 1 in stock. What do I have to add or change to make it bring back results whereby a product has different colours but only ONE of those colours are in stock and the remaining are out of stock? Do I need to do a UNION?
select *
from Product
where productcode NOT LIKE ('%000.000')
AND ProductQTY = '1'

First off, that's a terrible structure. Store color and size information in separate tables, don't make them part of one massive variable.
Second, you want to do a query that selects Product IDs (1-6, looks like) that have a count > 1 with color present and the sum of their quantities is 1 exactly (according to your question - if that one record could have a qty>1 and still be okay, this is a little more complicated).
select * from product where substr(product,1,6) in (
select substr(product,1,6) from product
where not (product like '%.000.%')
group by 1
having count(1) > 1
and sum(ProductQTY)=1
)

The question would be much easier if you would split up the properties in different columns. But you could do that in a query aswell:
select code from
(select substring(productcode,-3) as size,
substring(productcode,-7,3) as colour,
substring(productcode,0,len(prodcutcode)-7) as code
from product) t
group by code
having sum(productQTY) = 1
//or count(*) = 1 to get all unique ones

Related

Calculate theoretically available quantity of a BOM item

For a project I'm working on, I need to be able to communicate the quantity available to 'make' for a BOM item based on its contents. I won't go in-depth as to how this will be communicated, the base is in the SQL query.
The idea is fairly simple;
a BOM (Bill Of Material, indicating that it requires to be created) item (let's call this BOM1) has 1 or more stockitems linked to it. Having a quantity of 1 or higher. In this example let's take ITEM1 (3 units) ITEM2 (2 units) and ITEM3(5 units). Naturally, all of this is stored in a single table for referencing;
Secondary to that, there's the quantity in stock for each part within that BOM. this is stored in a 'bin content' table, example as below;
The sum of the stock, per item, is then summarised as below;
Now, based on the 'contribution' of each item to the BOM item, you can calculate the maximum available number to create;
in this scenario, based on ITEM3, I can create a maximum of 8 of item BOM1 as this has the lowest contribution (for lack of a better word I guess). Let's assume that parts can only be used once for a BOM item.
I can create a query for a single BOM item using a TOP 1 and ORDER BY ASC statement, the problem is that I have multiple BOM items, each having its own parts with multiple bin locations;
select TOP 1
BOM_ITEM.ITEM_NO
, (SUM(BIN_CONTENT.QTY) / BOM_ITEM.PART_ITEM_QTY) as AVAIL_TO_MAKE
from BOM_ITEM
JOIN BIN_CONTENT
ON BOM_ITEM.PART_ITEM_NO = BIN_CONTENT.ITEM_NO
WHERE BOM_ITEM.ITEM_NO = 'BOM1'
GROUP BY
BOM_ITEM.ITEM_NO
ORDER BY AVAIL_TO_MAKE ASC
So, based on the BOM item, I want to retrieve only the part item qty with the lowest 'contribution' so to communicate the theoretically available quantity to make, I just can't figure it out though.
any suggestions would be greatly appreciated!
The below query computes for each BOM item, how many items can be created given the available stock.
The query uses subquery to compute the stock across all bins. The main query groups by BOM item. For each BOM item, the minimum available to make is used. Additionally, the floor function is used to round down to an integer value.
WITH STOCK
USING (
SELECT ITEM_NO, SUM(QTY) AS QTY
FROM BIN_CONTENT
GROUP BY ITEM_NO
)
SELECT BOM_ITEM.ITEM_NO AS BOM_ITEM_NO,
FLOOR(MIN(STOCK.QTY / BOM_ITEM.PART_ITEM_QTY)) AS AVAIL_TO_MAKE
FROM BOM_ITEM
JOIN STOCK ON STOCK.ITEM_NO = BOM_ITEM.ITEM_NO
GROUP BY BOM_ITEM.ITEM_NO

How to select duplicate categories except one in a group from database

I have an application where I need to select all duplicate categories inside EAN group. See picture below to understand what is going on.
I've tried many approaches as of today I wasn't able to solve the problem. What I got so fat is a list of products grouped by EAN and ordered by ean also each new group row is visualized in different color to better distinguish in between.
I have to admit I am missing the logic here, what I need is a list of product id-s to be able to mark them for deletion but they need to be selected based on this: EAN group > category needs to be appear at least 2 times in the EAN group. The result would a list of product id-s.
I have attached a picture to this question where I marked with green color those I need except the one marked with red.
I was using this code to select those rows, but it selects only one row out or 2 as seen on the picture below
SELECT product_id
FROM products
GROUP BY ean, categories
HAVING COUNT(*) > 1 AND (MAX(CAST(price as decimal)) OR LENGTH(description) < 100)
It should be
Select * from products group by EAN having count(EAN)>1

Get the product of two values from two different tables

If anyone can help me figure out where I am going wrong with this SQL that would be great. Please see my attempt to answer it below. I have answer how I think it should be answered but I am very confused by the exam advice below, which says I should use a SUM function? I have googled this and I do not see how a SUM function can help here when I need get the product of two values in this case. Or am I missing something major?
Question: TotalValue is a column in Order relation that contains derived data representing total value (amount) of each order. Write a SQL SELECT statement that computes a value for this column.
My answer:
SELECT Product.ProductPrice * OrderLine.QuantityOrdered AS Total_Value
FROM Product,
OrderLine
GROUP BY Product;
Advice from exam paper:
This is a straightforward question. Tip: you need to use the SUM function. Also, note that you can take the sum of various records set using the GROUP BY clause.
Ok your question became a lot clearer once I clicked on the the hyperlink (blue text).
Each order is going to be made up of a quantity of 1 or more products.
So there could be 3 Product A and 5 Product B etc.
So you have to get the total for each product which is your Price * Quantity, but then you need to add them all together which is where the SUM comes in.
Example:
3 * ProductA Price (e.g. €5) = 15
5 * ProductB Price (e.g. €4) = 20
Total Value = 35
So you need to use the Product, Order and OrderLine tables.
Something like (I haven't tested it):
SELECT SUM(Product.ProductPrice * OrderLine.QuantityOrdered) FROM Product, Order, OrderLine
WHERE Order.OrderID = OrderLine.OrderID
AND Product.ProductID = OrerLine.ProductID
GROUP BY Order.OrderID
This should return rows containing the totalValue for each order - the GROUP BY clause causes the SUM to SUM over each group - not the entire rows.
For a single order you would need add (before the GROUP BY) "AND Order.OrderID = XXXXX" where XXXXX is the actual orders OrderId.

SQL Query For Product Sales Report

I have a query that gives a product sales report by whatever date range I specify.
Something like select whatever from wherever where date ordered between start date and end date order by product id.
My page then loop through the recordset and displays the results on the page in a list.
What I would like to do is provide a list showing PRODUCT A total sales = whatever, PRODUCT B total sales = whatever so on and so forth. So as the loop runs product a = product a + 1
I do this already with staff sales, but there are only 5 staff so I have managed to do this, but there are over 300 product codes.
What is the best way to proceed.
Possible solutions:
Do this in your application code by keeping track of the product code and running totals. When the product code changes, emit an extra row with the totals into the output.
Do something similar to #1, but use a separate GROUP BY query to get the totals.
Create a SELECT statement that UNIONs together two queries, one for the product detail lines and one with the summary information.
Use some product specific command (you don't say what database you're using) to accomplish #3 without having to do the UNION yourself. Both MySQL and SQL Server offer (different) ROLL UP clauses that can do what you want.

looping through a numeric range for secondary record ID

So, I figure I could probably come up with some wacky solution, but i figure i might as well ask up front.
each user can have many orders.
each desk can have many orders.
each order has maximum 3 items in it.
trying to set things up so a user can create an order and the order auto generates a reference number and each item has a reference letter. reference number is 0-99 and loops back around to 0 once it hits 99, so orders throughout the day are easy to reference for the desks.
So, user places an order for desk #2 of 3 items:
78A: red stapler
78B: pencils
78C: a kangaroo foot
not sure if this would be done in the program logic or done at the SQL level somehow.
was thinking something like neworder = order.last + 1 and somehow tying that into a range on order create. pretty fuzzy on specifics.
Without knowing the answer to my comment above, I will assume you want to have the full audit stored, rather than wiping historic records; as such the 78A 78B 78C type orders are just a display format.
If you have a single Order table (containing your OrderId, UserId, DeskId, times and any other top-level stuff) and an OrderItem table (containing your OrderItemId, OrderId, LineItemId -- showing 1,2 or 3 for your first and optional second and third line items in the order, and ProductId) and a Product table (ProductId, Name, Description)
then this is quite simple (thankfully) using the modulo operator, which gives the remainder of a division, allowing you in this case to count in groups of 3 and 100 (or any other number you wish).
Just do something like the following:
(you will want to join the items into a single column, I have just kept them distinct so that you can see how they work)
Obviously join/query/filter on user, desk and product tables as appropriate
select
o.OrderId,
o.UserId,
o.DeskId
o.OrderId%100 + 1 as OrderNumber,
case when LineItem%3 = 1 then 'A'
when LineItem%3 = 2 then 'B'
when LineItem%3 = 0 then 'C'
end as ItemLetter,
oi.ProductId
from tb_Order o inner join tb_OrderItem oi on o.OrderId=oi.OrderId
Alternatively, you can add the itemLetter (A,B,C) and/or the OrderNumber (1-100) as computed (and persisted) columns on the tables themselves, so that they are calculated once when inserted, rather than recalculating/formatting when they are selected.
This sort-of breaks some best practice that you store the raw data in the DB and you format on retrieval; but if you are not going to update the data and you are going to select the data for more than you are going to write the data; then I would break this rule and calculate your formatting at insert time