Select average column value - sql

I have a table containing order details. I would like to be able to select the average attribute for a certain item.
For example, select "item a", now find the average color of "item a". If there were 10 orders of "item a" and the colors ordered broke down as follows:
4 - black
2 - blue
2 - red
1 - orange
1 - white
I would like for it to return "black". Is there any such statement that could do that?
Also, would it then be possible to weigh the average, for example giving the last 3 orders of "item a" a weight of 2, instead of 1. So if the last 3 orders were all yellow, it would essentially count as 6?

You can group by on color, and then select the first row:
select color
from OrderLines
where ItemId = 'item a'
group by
color
order by
count(*) desc
limit 1
You could give some rows a higher weight with a subquery. This one gives the last 3 orders a higher weight:
select color
from (
select o1.color
, case when
(
select count(*)
from OrderLines o2
where o1.item = o2.item
and o1.OrderDt < o2.OrderDt
) < 3 then 2 else 1 end as weight
from OrderLines o1
)
where Item = 'item a'
group by
color
order by
sum(weight) desc
limit 1

Related

Limit result depending on sum of value

I´ve been trying to automize a list of addresses in SQL. I have multiple addresses and quantities and i need only the addresses that will fulfill the quantity i need
For example:
I have a table with
Item A qty 20
Item B qty 5
Item C qty 23
And a table with addresses and units
Address 1 item A 15units
Address 2 item A 10units
Address 3 item A 10units
Address 4 item A 13units
The result should show only
Address 2 item A 10units
Address 3 item A 10units
Assuming that your first table sales stores the sales made :
Item
qty
A
20
B
5
C
23
and your second table stocks indicates the addresses of the stored items :
Address
item
units
1
A
15
2
A
10
3
A
10
4
A
13
then you can select a subset of addresses so that the sum of the stored units is equal or greater than the quantity sold :
WITH list AS (
SELECT st.item
, sa.qty AS sales_qty
, array_agg(st.address) OVER w AS addresses
, array_agg(st.units) OVER w AS units
, sum(units) OVER w AS total_stored_units
FROM sales AS sa
INNER JOIN stocks AS st
ON st.item = sa.item
WINDOW w AS (PARTITION BY st.item ORDER BY st.address)
)
SELECT DISTINCT ON (item)
item, unnest(addresses) AS address, unnest(units) AS units
FROM list
WHERE total_stored_units >= sales_qty
ORDER BY item, total_stored_units ASC
Result
item
address
units
A
1
15
A
2
10
see dbfiddle
Thanks a lot #Edouard!
I ended up using something like
WITH r AS
(
SELECT
item,
address,
quantity,
sum(quantity)
OVER (
PARTITION BY item
ORDER BY quantity desc
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as total_units
FROM st
GROUP BY 1,2,3
)
SELECT *
FROM r
GROUP BY 1,2,3,4
HAVING total_units <= qty_needed + avg(units)
ORDER BY units desc
I couldnt run your query in the bigquery console, but from it a re read some documentation of windows functions find this solution.
Again, thanks a lot!

SQL - create flag in query to highlight order which contain quantity = 1

I have tried creating a case statement but doesn't seem to give me what i want. Id like to get a split of the table (which is at a product level) and aggregate at an order level of items which contain quantity of 1.
Any ideas on how I would do this?
order id | Product | Quantity
---------+---------+--------------
11111 | sdsd4 | 1 (single item )
22222 | sasas | 1 (multiple items)
22222 | wertt | 1 (multiple items)
I'd like to get a case statement to add another column to split out orders with quantity = 1 and orders greater 1
Any idea on how I would do this?
The desired outcome would be the column in (brackets)
I could then count the orders and bring in the newly created column as the dimension
More detail here:
enter image description here
Attached is an image of table structure.
Logic, if quantity = 1 and 1 order then single item order
if order has one item but multiples of same item non single item order
if order has more than one product then non single item order
If your database supports analytic functions, then you can use a query like this one:
SELECT *,
CASE WHEN count("Product") OVER (partition by "order id") > 1
THEN 'multiple items' ELSE 'single item'
END As "How many items"
FROM Table1
Demo: https://dbfiddle.uk/?rdbms=postgres_11&fiddle=b659279fc16d2084cb1cf4a3bea361a1
Below is for BigQuery Standard SQL
#standardSQL
SELECT *,
CASE COUNT(DISTINCT Product) OVER(PARTITION BY order_id)
WHEN 1 THEN 'Single Item Order'
ELSE 'Multiple Items Order'
END Single_or_Multiple
FROM `project.dataset.table`
You can test, play with above using dummy data as below
#standardSQL
WITH `project.dataset.table` AS (
SELECT 11111 order_id, 'sdsd4' Product, 1 Quantity UNION ALL
SELECT 22222, 'sasas', 2 UNION ALL
SELECT 22222, 'wertt', 1
)
SELECT *,
CASE COUNT(DISTINCT Product) OVER(PARTITION BY order_id)
WHEN 1 THEN 'Single Item Order'
ELSE 'Multiple Items Order'
END Single_or_Multiple
FROM `project.dataset.table`
with result
Row order_id Product Quantity Single_or_Multiple
1 11111 sdsd4 1 Single Item Order
2 22222 sasas 2 Multiple Items Order
3 22222 wertt 1 Multiple Items Order
If I understand this right, you could use a subquery to get the count of records for an order and flag a record, if this count is larger then 1 and the quantity is equal to 1.
SELECT t1.order_id,
t1.product,
t1.quantity,
CASE
WHEN t1.quantity = 1
AND (SELECT count(*)
FROM elbat t2
WHERE t2.order_id = t1.order_id) > 1 THEN
'flag'
ELSE
'no flag'
END flag
FROM elbat t1;

Select if then case with first record

Can you do something like this in SQL Server?
I want to select from a table which has some records with the same product_id in one column and a Y or N in another (in stock), and take the first one which has a Y where the product_id is the same, while matching the product_id_set from another table.
... ,
SELECT
(SELECT TOP 1
(product_name),
CASE
WHEN in_stock = 'Y' THEN product_name
ELSE product_name
END
FROM
Products
WHERE
Products.product_set = Parent_Table.product_set) AS 'Product Name',
...
Sample data would be
product_set in_stock product_id product_name
---------------------------------------------------
1 N 12 Orange
1 Y 12 Pear
2 N 12 Apple
2 N 12 Lemon
Output from product_set = 1 would be 'Pear' for example.
So there's kind of two solutions depending on the answer to the following question. If there are no records for a product id with an in_stock value of 'Y', should anything return? Secondly, if there are multiple rows with in_stock 'Y', do you care which one it picks?
The first solution assumes you want the first row, whether or not there is ANY "Y" value.
select *
from (select RID = row_number() over (partition by product_set order by in_stock desc) -- i.e. sort Y before N
from Products) a
where a.RID = 1
The second will only return a value if there is at least one row with a 'Y' for in_stock. Note that the order by (select null) is essentially saying you don't care which one it picks if there are multiple in_stock items. If you DO care the order, replace it with the appropriate sort condition.
select *
from (select RID = row_number() over (partition by product_set order by (select null)) -- i.e. sort Y before N
from Products
where in_stock = 'Y') a
where a.RID = 1
I don't know what the structure of the "parent table" in your query is, so I've simplified it to assume you have what you need in Products alone.
SELECT ISNULL(
(
SELECT TOP 1 product_name
FROM Products
WHERE Products.product_set = Parent_Table.product_set
AND Products.in_stock = 'Y'
), 'Not in the stock') AS 'Product Name'

SQl - Select best price based on quantity

I have a table that contains prices for a particular item based upon the quantity being ordered and the type of client placing the order ...
ID Name Quantity ClientType Price/Unit ($)
========================================================
1 Cheese 10 Consumer 20
2 Cheese 20 Consumer 15
3 Cheese 30 Consumer 12
4 Cheese 10 Restaurant 18
5 Cheese 20 Restaurant 13
6 Cheese 30 Restaurant 10
I have having trouble with WHERE clause in the SQL to select the row where the customer gets the best price based upon the quantity that is ordered. The rule is they must at least meet the quantity in order to get the price for that pricing tier. If their order is below the minimum quantity then they get the Price for the first quantity (10 in this case) and if they order more than the largest quantity (30 in this example) they get that price.
For example ... If a Restaurant orders 26 units of cheese the row with ID = 5 should be chosen. If a Consumer ordered 9 units of cheese then the row returned should be ID = 1. If the Consumer orders 50 units of cheese then they should get ID = 3.
declare #SelectedQuantity INT;
SELECT *
FROM PriceGuide
WHERE Name = 'Cheese'
AND ClientType = 'Consumer'
AND Quantity <= #SelectedQuantity
What am I missing in the WHERE clause?
Edit
The first solution didn't handle the special case correctly, as mentioned in the comments.
Next try:
SELECT TOP 1 ID, Name, Quantity, ClientType, [Price/Unit]
FROM PriceGuide
WHERE Name = 'Cheese'
AND ClientType = 'Consumer'
ORDER BY CASE WHEN Quantity <= #SelectedQuantity THEN Quantity ELSE -Quantity END DESC
Assuming that Quantity is positive, the ORDER BY will return rows that meet Quantity <= #SelectedQuantity condition first, in a descending order.
For rows that do not match this condition, it uses -Quantity for ordering. So if no rows match the condition, the one with smallest quantity will be returned.
This is a little tricky because you need to deal with the quantities less than 10.
I think the best approach is:
SELECT TOP 1 *
FROM PriceGuide
WHERE Name = 'Cheese' AND ClientType = 'Consumer'
ORDER BY (CASE WHEN #SelectedQuantity >= Quantity THEN 1 ELSE 0 END) DESC,
(CASE WHEN #SelectedQuantity >= Quantity THEN PriceUnit END) ASC,
Quantity ASC;
This version handles the minimum quantity by keeping all the rows for a given Name/ClientType, using the ORDER BY for prioritization.
Your query will return all matching rows less than the #SelectedQuantity. You only want to return the row with highest quantity less than the selected quantity, so a subquery is needed to get this result:
SELECT *
FROM PriceGuide a
WHERE a.Name = 'Cheese'
AND a.ClientType = 'Consumer'
AND a.Quantity = (SELECT MAX(Quantity) FROM PriceGuide
WHERE ClientType = a.ClientType
AND Name = a.Name
AND Quantity <= #SelectedQuantity)
In your example of 26 unit, your query will return row 4 and 5 whereas you need it to return only row 5.
declare #SelectedQuantity INT;
SELECT *
FROM PriceGuide
WHERE Name = 'Cheese'
AND ClientType = 'Consumer'
AND Quantity <= #SelectedQuantity
ORDER BY Quantity DESC
LIMIT 1

Individual percentage for each item, throughput

With the following code I get how many items that is not "Out", but it returns percentage for all the items and not for each individual. I know it has to do with the count(date) that counts all the date of the all the unitids. Is there any way to count each item individual so it doesn't show the total percentage?
SELECT unitid, (COUNT(date)* 100 / (SELECT COUNT(*) FROM items)) AS Percentage
FROM items
WHERE date !='Out'
GROUP BY unitid
EDIT1, clarification: Lets say I have 2 of each product, product a, b, c, d and e, one of each item is 'Out'. The result I get is:
unitid Percentage
1. a 10
2. b 10
3. c 10
4. d 10
5. e 10
I'd like it to show this instead:
unitid Percentage
1. a 50
2. b 50
3. c 50
4. d 50
5. e 50
Thanks :)
You need a link between the count items and the item selected.
SELECT
unitid,
COUNT(date) * 100
/ (SELECT COUNT(*) FROM items B WHERE B.unidid = A.unitid) AS Percentage
FROM items A
WHERE date !='Out'
GROUP BY unitid
You query does not require a subquery, just a conditional aggregation:
SELECT i.unitid, 100*sum(case when date <> 'Out' then 1 else 0 end)/count(date) as Percentage
FROM items i
GROUP BY unitid
Assuming that [date] is never NULL, you express this more simply as:
select i.unitid, 100*avg(case when date<>'out' then 1.0 else 0 end) as Percentage
from items i
group by unitid
Let's see if I understand this correctly. If you have one a, two b, three c, and four d's, one of each is "Out", whatever that is, your result set should be:
unitid Percentage
1. a 100.00
2. b 50.00
3. c 33.33
4. d 25.00
To do that, you can try this:
Select counts.unitId, 100.0 *outcounts.count/ counts.count as Percentage
from (select unitid, count(*) as count
from items
where items.date ='Out'
group by unitid) as outcounts
inner join (select unitid, count(*) as count
from items
group by unitid) as counts
on outcounts.unitId = counts.unitId
here's an SQL Fiddle with the setup