SQL Query - Sum of Order tha Contain an Item - sql

I am surprised I have not been able to find a solution to this. We have a table
ORDER # | PRODUCT ID | PRICE
1 | 1 | 1.00
1 | 2 | 2.00
2 | 3 | 3.00
2 | 4 | 4.00
3 | 1 | 5.00
3 | 4 | 6.00
We want to capture the sum of the revenues of all orders which included productID=1. The result in this example should be 1+2+5+6 = 14
What is the best way to achieve this?
Currently, best solution I have is to run two queries.
1 - SELECT orderID FROM table WHERE prodID=$prodID
2 - SELECT price FROM table WHERE orderID=[result of the above]
This has worked, but would strongly prefer to have a single query.

Here is a query that gives the results you are looking for:
SELECT OrderNum, SUM(PRICE) as TotalPrice
FROM MyTable AS M
WHERE EXISTS (SELECT 1 -- Include only orders that contain product 1
FROM MyTable AS M2
WHERE M2.OrderNum=M.OrderNum AND M2.ProductId=1)
GROUP BY OrderNum

Try:
select sum(price) as total_price
from orders
where prod_order in
(select prod_order
from orders
where product_id = 1)
Check this SQLFiddle to confirm the result.

select sum(price) as total_price where product_id=[enter here id];

SELECT SUM(t1.price) FROM tableName t1 WHERE
t1.orderId IN (SELECT t2.orderId FROM tableName t2 WHERE
t2.productId=productIdYouWant)
If you need more informations on how this work, please feel free to ask.

You need a nested select. The inner select should give you the total order value;
select order, sum(price) as totalvalue from table group by order
Now you need to select the orders which have a product id of 1, and sum the order price;
select sum(totalvalue) from (
select order, sum(price) as totalvalue from table group by order
) where order in (
select order from table where productid = 1
)

Related

SELECT only rows when count=1 - without additional SELECT or/ and having

I wonder if there is a way to build a query without joins or/and having clause that would return the same result as the query below? I already found similar question (select and count rows) but didn't find the answer.
SELECT ID, CATEGORY, PRODUCT, DESC
FROM SALES s
JOIN (SELECT ID, COUNT(CATEGORY)
FROM SALES
GROUP by ID
HAVING count(CATEGORY)=1) S2 ON S.ID=S2.ID;
So the table looks like
ID | Country | Product | DESC
1 | USA | Cream | Super cream
1 | Canada | Toothpaste| Great Toothpaste
2 | Germany | Beer | Tasty Beer
and the result I would like to get is
ID | Country | Product | DESC
2 | Germany | Beer | Tasty Beer
because id=1 has 2 different countries assigned
I'm using SQL Server
In general I'm interested in the 'fastest' solution. The table is huge and I just wonder if there is a way to do it smarter.
you may want to consider this query.
select t2.id, t2.category, t2.product, t2.desc from (
select id, category, product,
case when (select count(1) from sales where id=t1.id group by id) as ct
,desc
from sales t1) t2 where t2.ct = 1
You can try this Query:
SELECT ID, CATEGORY, PRODUCT, DESC
FROM SALES s
WHERE 1 = (
SELECT COUNT(*)
FROM SALES x
WHERE x.ID = s.ID
);
One method uses window functions:
SELECT ID, CATEGORY, PRODUCT, DESC
FROM (SELECT s.*, COUNT(*) OVER (PARTITION BY ID) as cnt
FROM SALES s
) s
WHERE cnt = 1;
However, the fastest solution would require a unique id and an index. That would be:
select s.*
from sales s
where not exists (select 1
from sales s2
where s2.id = s.id and
s2.<unique key> <> s.<unique key>
);
This can take advantage of an index on (id, <unique key>).
Note: This particular formulation assumes that category is never null.

Within GROUP BY grouping, select value based on highest value of another column

I am attempting to build a query that reduces a GROUP BY group to a single row, including a value for a column based on the max value of another column. In this case, I want an item id, total qty ordered and most-used supplier.
I've successfully built a query that sums the qty ordered and groups by item and supplier, yielding:
| id | qty | supplier |
| 1 | 20 | S&S Activewear |
| 1 | 10 | J&J Textiles |
| 2 | 5 | AB Footwear |
| 2 | 10 | CD Shoes |
and the intended result would be total qty ordered (for all suppliers) and most used supplier, so:
| id | total_qty | most_used_supplier |
| 1 | 30 | S&S Activewear |
| 2 | 15 | CD Shoes |
Conceptually, I imagine doing a subquery, grouping the above results by id alone, then sum(qty) and somehow choose the supplier value by ranking the GROUP BY by qty.
I have read many related posts but I am failing to apply any of those methods successfully to this end, including use of ROW_NUMBER and PARTITION_BY.
I am doing this in Elixir with Ecto on a Postgres DB, but to keep it generalized so anyone can respond, I am just looking to understand how this would be done in SQL. Please let me know if I can provide more detail, thank you.
There are several approaches and it sounds like you've played with this one a bit even:
with data as (
select *,
row_number() over (partition by id order by qty desc) as rn
from T
)
select id, sum(qty) as total_qty,
(select d2.supplier from data d2
where d2.id = d.id and rn = 1) as most_used_supplier
from data d
group by id;
I'm going to suggest multiple subqueries:
select id, sum(qty),
(select t2.supplier
from t t2
where t2.id = t.id
order by t2.qty desc
fetch first 1 row only
) as supplier
from t
group by id;
This uses standard syntax for returning one row. Your database may have another syntax for the equivalent of fetch first 1 row only.
First find biggest quantities for each id.
Then find appropriate suppliers which provide those biggest quantities. Here issue may appear if there are more then one "biggest", and you have to see how to deal with it.
Finally, just join it once more to same table, adding appropriate quantity sums.
SELECT item.id, sum(item.qty) total_qty, biggestSupplier.supplier most_used_supplier
from item join
(
SELECT item.id, supplier
from item
JOIN
(
SELECT id, max(qty) maxqty
FROM item
GROUP BY id
) maxQtyForId ON item.id = maxQtyForId.id AND item.qty = maxQtyForId.maxqty
) biggestSupplier ON item.id = biggestSupplier.id
group by item.id, biggestSupplier.supplier
I Divide the problem in 2. First, finding the max qty and then adding up the qty. Finally, Join the table to get the answers.
SELECT T4.ID, T5.sumQty AS total_qty,T4.supplier AS most_used_supplier
FROM [Test].[dbo].[Test] AS T4 LEFT JOIN
(
SELECT ID,SUM(QTY) as sumQty
FROM [Test].[dbo].[Test]
GROUP BY ID
)AS T5
ON T4.ID = T5.ID
WHERE supplier IN
(
SELECT supplier
FROM [Test].[dbo].[Test] AS T1 LEFT JOIN
(
SELECT MAX(qty) AS maxQty, ID
FROM [Test].[dbo].[Test] AS T
GROUP BY id
) AS T2
ON T1.ID = T2.ID
AND T1.qty = T2.maxQty
WHERE T2.ID IS NOT NULL
)

SQL - returning values when a condition is met for that value over two or more rows

I’ve got two data tables -
Order level:
Order_number | order total | etc
Order_num1 | $10
Order_num2 | $20
And line-item level:
Order_number | sku | quantity | etc
Order_num1 | sku1 | 3
Order_num1 | sku2 | 2
Order_num2 | sku1 | 4
And so on.
I am trying to write a query that returns any Order_numX that contains both skuY and skuZ.
Im thinking about somehow grouping the line item table but I’m not sure.
Any help is appreciated! Thank you.
You can use aggregation and having:
select order_number
from line_item
where sku in (?, ?)
group by order_number
having count(distinct sku) = 2; -- number of items in list
So I suggest you to using EXISTS function:
SELECT *
FROM Order
WHERE EXISTS(SELECT 1
FROM line-item
WHERE line-item.Order_number=Order.Order_number
AND line-item.sku='skuY'
)
AND EXISTS(SELECT 1
FROM line-item
WHERE line-item.Order_number=Order.Order_number
AND line-item.sku='skuZ'
)
This query gives you the orders which have both skuY and skuZ values of sku column of child table, which here is line-item
SELECT Order_number
FROM line_item
GROUP BY Order_number
HAVING COUNT( CASE WHEN sku = skuY THEN 1 END) > 0
AND COUNT( CASE WHEN sku = skuZ THEN 1 END) > 0

Postgres: select all row with count of a field greater than 1

i have table storing product price information, the table looks similar to, (no is the primary key)
no name price date
1 paper 1.99 3-23
2 paper 2.99 5-25
3 paper 1.99 5-29
4 orange 4.56 4-23
5 apple 3.43 3-11
right now I want to select all the rows where the "name" field appeared more than once in the table. Basically, i want my query to return the first three rows.
I tried:
SELECT * FROM product_price_info GROUP BY name HAVING COUNT(*) > 1
but i get an error saying:
column "product_price_info.no" must appear in the GROUP BY clause or be used in an aggregate function
SELECT *
FROM product_price_info
WHERE name IN (SELECT name
FROM product_price_info
GROUP BY name HAVING COUNT(*) > 1)
Try this:
SELECT no, name, price, "date"
FROM (
SELECT no, name, price, "date",
COUNT(*) OVER (PARTITION BY name) AS cnt
FROM product_price_info ) AS t
WHERE t.cnt > 1
You can use the window version of COUNT to get the population of each name partition. Then, in an outer query, filter out name partitions having a population that is less than 2.
Window Functions are really nice for this.
SELECT p.*, count(*) OVER (PARTITION BY name) FROM product p;
For a full example:
CREATE TABLE product (no SERIAL, name text, price NUMERIC(8,2), date DATE);
INSERT INTO product(name, price, date) values
('paper', 1.99, '2017-03-23'),
('paper', 2.99, '2017-05-25'),
('paper', 1.99, '2017-05-29'),
('orange', 4.56, '2017-04-23'),
('apple', 3.43, '2017-03-11')
;
WITH report AS (
SELECT p.*, count(*) OVER (PARTITION BY name) as count FROM product p
)
SELECT * FROM report WHERE count > 1;
Gives:
no | name | price | date | count
----+--------+-------+------------+-------
1 | paper | 1.99 | 2017-03-23 | 3
2 | paper | 2.99 | 2017-05-25 | 3
3 | paper | 1.99 | 2017-05-29 | 3
(3 rows)
Self join version, use a sub-query that returns the name's that appears more than once.
select t1.*
from tablename t1
join (select name from tablename group by name having count(*) > 1) t2
on t1.name = t2.name
Basically the same as IN/EXISTS versions, but probably a bit faster.
SELECT name, count(name)
FROM product_price_info
GROUP BY name
HAVING COUNT(name) > 1
LIMIT 3

In SQL, how can I divide a value by the number of rows it is in?

I am selecting from two tables, a product table and a shipping table, and building the table below, but I'd like to divide the product $ value by the number of rows per ID so that the product $ is split between all the rows it appears on. Is there a way to do this in the select statement as I'm building the table?
What I have:
ID | Product $ | Shipping $
---------------------------------
123456 | 200.00 | 5.00
123456 | 200.00 | 10.00
123567 | 186.00 | 7.99
What I'd like:
ID | Product $ | Shipping $
---------------------------------
123456 | 100.00 | 5.00
123456 | 100.00 | 10.00
123567 | 186.00 | 7.99
It is simpler to use windowing functions instead of subqueries:
SELECT
[ID]
,[Product $] / COUNT(*) OVER(PARTITION BY [ID])
,[Shipping $]
FROM MyTable
SELECT ID,
([Product $]/(SELECT COUNT(id) FROM t t1 WHERE t1.id=t.id)) [Product $] ,
[Shipping $]
FROM t
Before joining product & shipping table, you can calculate product price based on the count (as in the inner query) and then you can inner join it with shipping table. From the post-view, you can use the following query as a reference:
SELECT ps.id
,t1.new_c
,ps.shipping
FROM product_shipping ps
INNER JOIN (
SELECT id
,product / count(1) new_c
FROM product_shipping
GROUP BY id
,product
) t1 ON ps.id = t1.id;
Since sqlfiddle is down, Here you go with ideone
SELECT ID, ([Product $] / (SELECT COUNT(ID) FROM TABLE t2 WHERE t2.ID = TABLE.ID)) as [Product $], [Shipping $]
FROM TABLE
How about this
SELECT p.ID, (p.[Product $] / c.CNT ) as [Result]
(
SELECT ID,sum([Product $]) as [Product $]
FROM [table]
group by ID
) p inner join
(
SELECT ID,count(1) as CNT
FROM [table]
group by ID
) c on (p.ID = c.ID)