I have a dataset where I have an ItemID and then quantity sold at each price like this:
ItemID | Quantity | Price
ABC 10 14.50
ABC 4 14.25
DEF 32 22.41
ABC 24 14.10
GHI 8 8.50
GHI 12 8.60
DEF 2 22.30
Every entry has a unique combination of ItemID and Price. I would like to add a fourth column that has the total quantity sold for that ItemID. So it would look like this for the above table:
ItemID | Quantity | Price | TotalQ
ABC 10 14.50 38
ABC 4 14.25 38
DEF 32 22.41 34
ABC 24 14.10 38
GHI 8 8.50 20
GHI 12 8.60 20
DEF 2 22.30 34
I can't seem to do this without performing an aggregate function on an aggregate function, which obviously gives an error. How would I go about accomplishing this?
I'm using SQL Server 2008.
Please try:
SELECT
*,
SUM(Quantity) OVER(PARTITION BY ItemID) TotalQ
FROM
YourTable
Try this code
select a.ItemID,a.Quantity,a.Price,x.Total form table_name a
left outer join
(select sum(Quantity) Total, ItemID from table_name group by ItemID)x
on x.ItemID = a.ItemID
If you need this just in query you can write something like this:
;WITH CTE_Total AS
(
SELECT ItemID, SUM(Quantity) as TotalQ
FROM YourTable
GROUP BY ItemID
)
SELECT t.*, c.TotalQ
FROM YourTable t
LEFT JOIN CTE_Total c on t.ItemID = c.ItemID
SQLFiddle DEMO
But, if you want to have automated computed column in your table, first you need to create function that does the calculation:
CREATE FUNCTION dbo.fn_YourTableTotalQ (#ItemID VARCHAR(3))
RETURNS Int
AS
BEGIN
DECLARE #toRet INT
SELECT #toRet = COALESCE(SUM(Quantity),0)
FROM YourTable WHERE ItemID = #ItemID
RETURN #toRet
END
... and then add your new column as computed using this function:
ALTER TABLE YourTable
ADD TotalQ AS dbo.fn_YourTableTotalQ(ItemID)
SQLFiddle DEMO
Related
I have a table like this:
id | invoice_id | product_id | quantity | total
1 5 10 2 100
2 5 10 1 50
3 5 11 1 200
4 5 11 1 200
I want to combine the rows having same product_id in an invoice by adding their quantities and total values to one of the rows and then delete the other rows in the table. So the output should be like this
id | invoice_id | product_id | quantity | total
1 5 10 3 150
3 5 11 2 400
How can I do this? I was thinking of using an sql function that returns a list of id's having the same invoice and product and then using aggregate functions on quantity and price. Are there any simpler ways to do this?
First, you need an UPDATE statement that updates for each invoice_id, product_id combination the row with the min id with the totals of quantity and total:
UPDATE tablename t
SET quantity = s.quantity,
total = s.total
FROM (
SELECT MIN(id) id, SUM(quantity) quantity, SUM(total) total
FROM tablename
GROUP BY invoice_id, product_id
) s
WHERE s.id = t.id;
Then a DELETE statement to delete all the other ids:
DELETE FROM tablename t1
WHERE t1.id > (
SELECT MIN(t2.id)
FROM tablename t2
WHERE t2.invoice_id = t1.invoice_id AND t2.product_id = t1.product_id
);
See the demo.
This looks like an aggregation query:
select min(id) as id, invoice_id, product_id,
sum(quantity) as quantity, sum(total) as total
from t
group by invoice_id, product_id;
At the company where I work we buy the same products from different sellers according to who offers us the better price, and to make the purchase decisions easier I'd like a query that would tell me for each productId, what's the latest price recorded for each seller iD.
I have a table that looks something like this.
purchaseId
productId
price
sellerId
date
1
1
23
1
2021-01-13
2
2
20
1
2021-01-13
3
1
33
2
2021-01-19
4
3
55
2
2021-01-19
5
2
25
1
2021-01-23
6
1
19
3
2021-01-23
7
1
22
1
2021-01-26
8
2
29
2
2021-01-29
I'd like the query to return a table like this, with a column for each sellerID:
productId
priceSellerID1
priceSellerId2
priceSellerId3
1
22
33
19
2
25
19
NULL
3
NULL
55
NULL
There are and could be more and more sellers, this is just a small example.
I want to pull this information to fill a DataGridView in a winforms app, what I thought of first was to populate the DGV with the productIds, and then, for each row pull the information with a query like this for each sellerId:
SELECT price FROM purchase_details
WHERE productId = #productID AND date = (SELECT MAX(DATE) FROM purchase_details WHERE productId = #productID and sellerId = #sellerId)
This takes lot of time to fill in the dgv for just a few ids, so I know it's just not the right way, but unfortunately I can't wrap my head around a query that would gather this information efficiently.
Any information would be greatly appreciated, and if there's way I could improve the question I'd like to know too. Thanks.
Seems like you're looking for Dynamic SQL with a little twist. The subquery src will select the last record per productID and SellerID ordered by date
Example
Declare #SQL varchar(max) ='
Select *
From (
Select top 1 with ties
productID
,Item = concat(''priceSellerID'',sellerId)
,Value = price
From #YourTable
Order by row_number() over (partition by productID,SellerID order by date desc)
) src
Pivot (min([Value]) For [Item] in (' + stuff((Select Distinct ','+QuoteName(concat('priceSellerID',sellerId))
From #YourTable
For XML Path('')),1,1,'') + ') ) p
'
Exec(#SQL);
Results
I have a set of records where we identify several items connected to a customer.
My dilemma is that if a customer has both items then I would like to exclude that customer.
If they only have one specific item then I want to include it.
I will be using this code to create a view so i'm trying to find the best way. I could try Row_number() to identify different records, but I'm not sure that would be ideal in this situation.
Example data table:
Customer | ItemID | value1 | Value2
A 12 35 0
B 12 35 0
C 13 0 25
C 12 0 25
D 18 225 12
Desired Output:
Customer | ItemID | value1 | Value2
A 12 35 0
B 12 35 0
This is what I have so far:
select Customer, ItemID, Value1, Value2
from Table1
where itemID = 12
This would give me customer 'C', which I don't want.
If you want customers who have itemid = 12 but not itemid = 13 you can use NOT EXISTS:
select * from tablename t
where itemid = 12
and not exists (
select 1 from tablename
where customer = t.customer
and itemid = 13
)
If you want customers who have itemid = 12 and not any other itemid:
select * from tablename t
where itemid = 12
and not exists (
select 1 from tablename
where customer = t.customer
and itemid <> 12
)
or:
select * from tablename
where customer in (
select customer from tablename
group by customer
having min(itemid) = 12 and max(itemid) = 12
)
I think you need to clarify your question but, as I understand it, you're looking to return the all rows where:
1) A customer has a particular item (i.e. Item ID 12, which excludes customer D)
and
(2) They only have one item in total, which excludes customer C since they have two items.
If that is the case, then here's what I've got:
SELECT *
FROM Table1
WHERE ItemID == '12' AND
Customer in (
SELECT Customer
FROM Table1
GROUP BY Customer
HAVING Count(Customer) = 1
)
Edit: I clarified my interpretation of OP's question. I also tested my solution on SQL Fiddle (http://sqlfiddle.com/#!5/b5f1f/2/0) and updated the WHERE clause accordingly.
I have two tables, Stock and Warehouse.
I need to get the Items which are available in all Warehouses.
Here an example:
Stock Table:
ItemID WarehouseID ItemAmount
-------------------------------------------
1043 1 20
1043 2 2
1043 3 16
1043 4 17
1044 1 32
1044 2 12
1044 4 7
1055 2 6
Warehouse Table:
WarehouseID WarehouseName
-------------------------------
1 Name1
2 Name2
3 Name3
4 Name4
For the Example the result should be Item 1043 because its available in all Warehouses, unlike the other ones.
I didn't get to a solution, can anyone help me?
You could also use this "double negative" query using NOT EXISTS:
SELECT DISTINCT s.ItemID
FROM StockTable s
WHERE NOT EXISTS
(
SELECT 1 FROM Warehouse w
WHERE NOT EXISTS(SELECT 1 FROM StockTable s2
WHERE w.WarehouseID = s2.WarehouseID
AND s.ItemID = s2.ItemID)
)
Demo fiddle
This approach looks more verbose but it has some benefits:
you can change it easily if the rules are getting more complex
you can remove the DISTINCT to see all rows
you can add all columns since GROUP BY was not used
it has no issues with null values
select itemid
from stock
group by itemid
having count(distinct warehouseid) = (select count(*) from warehouse);
SQLFiddle: http://sqlfiddle.com/#!15/e4273/1
If the stock table may also contain items with an amount = 0 you need to add a where clause:
select itemid
from stock
where itemamount > 0
group by itemid
having count(distinct warehouseid) = (select count(*) from warehouse);
NOT EXISTS combined with EXCEPT:
select distinct ItemID
from stock s1
where not exists (select warehouseid from warehouse
except
select warehouseid from stock s2 where s2.ItemID = s1.ItemID);
You can even replace select distinct ItemID with select * to get all those items.
I use this query:
SELECT
ItemID
FROM
stock
GROUP BY
ItemID
HAVING
SUM(DISTINCT warehouseid) = (SELECT SUM(WarehouseID) from warehouse)
That is more reliable than using COUNT, because in a rare situation of don't making a foreign key it should returns some invalid results.
I have a database table named as tbl_stock.
eg:
Order_ID Item_Name Item_Stock Item_Dispatched Item_Avaliable
........ .......... .......... ............... ..............
1 abc 10 2 8
2 abc 13 7 6
3 abc 23 10 13
4 xyz 43 12 31
5 xyz 4 1 3
I want to get an out put in gridview as shown below:
Item_Name Item_Stock Item_Dispatched Item_Avaliable
......... .......... ............... ..............
abc 46 19 27
xyz 47 13 33
I want to calculate specific column values. While using the given below SQL query showing one error. The error is "Column 'tbl_Stock.Item_Name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
SQL :
SELECT DISTINCT Item_Name,
COUNT(Item_Stock) AS Item_Stock,
COUNT(Item_Dispatched) AS Item_Dispatched,
COUNT(Item_Avaliable) AS Item_Avaliable
FROM tbl_Stock
Add group by clause rather than distinct like:
SELECT Item_Name,
SUM(Item_Stock) AS Item_Stock,
SUM(Item_Dispatched) AS Item_Dispatched,
SUM(Item_Avaliable) AS Item_Avaliable
FROM tbl_Stock
GROUP BY Item_Name
It will group all your records by Item_Name and will do SUM on each and every column.
as the error says, add GROUP BY to calculate totals for each item
SELECT /*DISTINCT*/ Item_Name,
SUM(Item_Stock) AS Item_Stock,
SUM(Item_Dispatched) AS Item_Dispatched,
SUM(Item_Avaliable) AS Item_Avaliable
FROM tbl_Stock
GROUP BY Item_Name
also there will be no need in DISTINCT.
COUNT() function will return number or rows (3 for abc item in Item_Stock, Item_Dispatched and Item_Avaliable columns and 2 for xyz), so to get desired result change it and use SUM()