Trying to strip out values from a column based on another columns and store that value to be used in a calculation - sql

I am somewhat a novice at t-sql. I'm having trouble getting incorporating row values into a variables based on another columns value.
The database table has a single quantity field where all the quantity amounts are stored as positive amounts. There is a separate column that represents the direction of the transaction. A direction of 1 means it's inbound. A direction of -1 means it's inbound. Netting the inbound qty against the outbound provides the net remaining quantity.
I've been able to segregate inbound and outbound into separate columns. I've aliased each column (InQty and OutQty) to give the reader the proper context. The last thing I need is to net the two amounts (i.e. InQty minus OutQty) into another column labelled NetQty. I've tried to use variables, with and without a case statement but nothing I've tried is working. I fell like I'm real close but just can't seem to get the proper structure to the query.
The output I'm trying to get is:
ItemID ItemPartNumber InQty OutQty NetQty
1140797106360931 ACRFCO 5.000000 2.000000
Below is the query I've developed so far.
The comment lines are there to me assist in developing the query.
Select convert(varchar(max), i.itemid) as ItemID
,ItemPartNumber As ItemPartNumber
,Sum(case when Direction = 1 Then TransactionQty end) As InQty
,Sum(case when Direction = -1 Then TransactionQty end) as OutQty
FROM InventoryTransaction IT
join item i on i.itemid = it.ItemID
group by i.itemid, it.ItemPartNumber
Order by it.ItemPartNumber

I think you want:
select i.itemid, i.ItemPartNumber,
Sum(case when Direction = 1 Then TransactionQty end) As InQty,
Sum(case when Direction = -1 Then TransactionQty end) as OutQty,
sum(case when Direction = 1 then TransactionQty
when Direction = -1 then TransactionQty
end) as netqty
from InventoryTransaction IT i
item i
on i.itemid = it.ItemID
group by i.itemid, it.ItemPartNumber
order by it.ItemPartNumber;
I see no reason to convert the first value to a string, so I removed the conversion.
Assuming that direction only takes on the values -1 and 1 (and perhaps 0 and NULL), you can simplify this to:
select i.itemid, i.ItemPartNumber,
sum(case when Direction = 1 Then TransactionQty end) As InQty,
sum(case when Direction = -1 Then TransactionQty end) as OutQty,
sum(Direction * TransactionQty) as netqty
from InventoryTransaction IT i
item i
on i.itemid = it.ItemID
group by i.itemid, it.ItemPartNumber
order by it.ItemPartNumber

I think a good way to avoid further comparisons, sums or other operations and get better performance could be to use your query as a subquery:
SELECT ItemID, ItemPartNumber, InQty, OutQty, InQty-OutQty AS NetQty FROM(
/* Start of your query */
Select convert(varchar(max), i.itemid) as ItemID
,ItemPartNumber As ItemPartNumber
,Sum(case when Direction = 1 Then TransactionQty end) As InQty
,Sum(case when Direction = -1 Then TransactionQty end) as OutQty
FROM InventoryTransaction IT
join item i on i.itemid = it.ItemID
group by i.itemid, it.ItemPartNumber
Order by it.ItemPartNumber
/* End of your query */
) AS T;

Related

how to sum all columns with same id

Hi i have the following results:
i need to sum up all the items that have cashamount and same Payment code = 9
i have tried this query:
SELECT
CASE
WHEN StoreID = 1 THEN 'CWM'
WHEN StoreID = 2 THEN 'CWD' END as accountcode,
DocEntry,
PaymentCode,
case when PaymentCode <> 1 then paymentamount end as OtherPaymentAmount,
sum(case when PaymentCode = 1 then paymentamount end) as CashAmount,
tenders.sapcreditcard AS sapcreditcard,
--paymentamount,
-- sum (case when PaymentCode >= 1 then paymentamount else NULL end) as Total,
FileName, BPA_ProcessStatus, ERP_PaymentProcessed
FROM [Plu].[dbo].[payments_header] LEFT JOIN tenders ON payments_header.PaymentCode = tenders.postenderid
WHERE BPA_ProcessStatus='N' and ERP_PaymentProcessed='N'
group by PaymentCode, paymentamount, docentry, storeid,sapcreditcard, FileName, BPA_ProcessStatus,ERP_PaymentProcessed, cashamount
what im missing?
The GROUP BY clause lists all the columns you want to use to create separate groups. Yours is as follows...
GROUP BY
PaymentCode,
paymentamount,
docentry,
storeid,
sapcreditcard,
FileName,
BPA_ProcessStatus,
ERP_PaymentProcessed,
cashamount
Any time any of these are different, you'll get a separate row.
This means that your sum(case when PaymentCode = 1 then paymentamount end) ends up making very little sense.
Your GROUP BY says you want each different payment amount on a different row
Your SELECT says you want to aggregate multiple paymounts amounts
My best guess is that you want this...
SELECT
CASE
WHEN StoreID = 1 THEN 'CWM'
WHEN StoreID = 2 THEN 'CWD'
END
AS accountcode,
DocEntry,
PaymentCode,
SUM(CASE WHEN PaymentCode <> 1 THEN paymentamount END) AS OtherPaymentAmount,
SUM(CASE WHEN PaymentCode = 1 THEN paymentamount END) AS CashAmount,
tenders.sapcreditcard,
FileName,
BPA_ProcessStatus,
ERP_PaymentProcessed
FROM
[Plu].[dbo].[payments_header]
LEFT JOIN
tenders
ON payments_header.PaymentCode = tenders.postenderid
WHERE
BPA_ProcessStatus='N'
AND ERP_PaymentProcessed='N'
GROUP BY
CASE
WHEN StoreID = 1 THEN 'CWM'
WHEN StoreID = 2 THEN 'CWD'
END,
DocEntry,
PaymentCode,
tenders.sapcreditcard,
FileName,
BPA_ProcessStatus,
ERP_PaymentProcessed
Added SUM() around the OtherPaymentAmount calculations, to match CashAmount
Changed the GROUP BY to match the non-aggregated columns in the SELECT
NOTE: In all the places where you specify a column name, your should always qualify it with the source table's name.

How to use count for a case statement

I'm using the below code to get the count of every case statements which has sum inside the case statements but I'm getting the error message.
SELECT
count(case when SUM(Orders.Sales)>10000 then 1 end) as High,
count(case when SUM(Orders.Sales)>5000 and SUM(Orders.Sales)<9999 then
SUM(Orders.Sales) end) as Medium,
count(case when SUM(Orders.Sales)<5000 then SUM(Orders.Sales) end) as Low
FROM Orders
INNER JOIN Returns ON Orders.[Order ID] = Returns.[Order ID]
OUTPUT
This is the required output which I'm supposed to be expected.
I feel that you should be doing the outer count rollups over a subquery which aggregates by order:
SELECT
COUNT(CASE WHEN sales < 5000 THEN 1 END) AS Low,
COUNT(CASE WHEN sales < 9999 THEN 1 END) AS Medium,
COUNT(CASE WHEN sales >= 9999 THEN 1 END) AS High
FROM
(
SELECT o.[Order ID], SUM(o.Sales) AS sales
FROM Orders o
INNER JOIN Returns r ON o.[Order ID] = r.[Order ID]
GROUP BY o.[Order ID]
) t;
That being said, I don't actually know what the purpose of joining Orders to the Returns table is. If you intend to only find sales amounts from orders which have been returned, then maybe this makes sense. Otherwise, maybe it doesn't make sense.

SQL - Dividing aggregated fields, very new to SQL

I have list of line items from invoices with a field that indicates if a line was delivered or picked up. I need to find a percentage of delivered items from the total number of lines.
SALES_NBR | Total | Deliveryrate
1 = Delivered 0 = picked up from FULFILLMENT.
SELECT SALES_NBR,
COUNT (ITEMS) as Total,
SUM (case when FULFILLMENT = '1' then 1 else 0 end) as delivered,
(SELECT delivered/total) as Deliveryrate
FROM Invoice_table
WHERE STORE IN '0123'
And SALE_DATE >='2020-02-01'
And SALE_DATE <='2020-02-07'
Group By SALES_NBR, Deliveryrate;
My query executes but never finishes for some reason. Is there any easier way to do this? Fulfillment field does not contain any NULL values.
Any help would be appreciated.
I need to find a percentage of delivered items from the total number of lines.
The simplest method is to use avg():
select SALES_NBR,
avg(fulfillment) as delivered_ratio
from Invoice_table
where STORE = '0123' and
SALE_DATE >='2020-02-01' and
SALE_DATE <='2020-02-07'
group by SALES_NBR;
I'm not sure if the group by sales_nbr is needed.
If you want to get a "nice" query, you can use subqueries like this:
select
qry.*,
qry.delivered/qry.total as Deliveryrate
from (
select
SALES_NBR,
count(ITEMS) as Total,
sum(case when FULFILLMENT = '1' then 1 else 0 end) as delivered
from Invoice_table
where STORE IN '0123'
and SALE_DATE >='2020-02-01'
and SALE_DATE <='2020-02-07'
group by SALES_NBR
) qry;
But I think this one, even being ugglier, could perform faster:
select
SALES_NBR,
count(ITEMS) as Total,
sum(case when FULFILLMENT = '1' then 1 else 0 end) as delivered,
sum(case when FULFILLMENT = '1' then 1 else 0 end)/count(ITEMS) as Deliveryrate
from Invoice_table
where STORE IN '0123'
and SALE_DATE >='2020-02-01'
and SALE_DATE <='2020-02-07'
group by SALES_NBR

Combine Three Grouped Select Queries Into One

--ON TIME PMWO's
SELECT LOCATION, COUNT(WONUM) AS OnTimePMWOs
FROM
WORKORDER
WHERE worktype = 'pm' and actfinish<=targcompdate
GROUP BY LOCATION
--PAST DUE PMWO'S
SELECT LOCATION, COUNT(WONUM) AS PastDuePMWOs
FROM
WORKORDER
WHERE worktype = 'pm' and actfinish>=targcompdate
GROUP BY LOCATION
--30 DayForecast-
SELECT W.location, COUNT(W.wonum) AS Forecast30days
from
workorder AS W
INNER JOIN PMFORECAST AS P
ON W.CHANGEDATE=P.CHANGEDATE
WHERE WORKTYPE='PM' AND P.forecastdate>= GETDATE()+30
GROUP BY LOCATION
This is an answer (all be it just a guess based on the limited question). I'm not a fan of placing CASE statements inside aggregates, but depending on environment, indexing, and data in the included tables, it might perform okay. Please be more involved when posting things. Show us what you've tried, explain the problem, give samples of data, include the desired output, all that fun stuff. The better the question, the better chance of a good answer. Okay, done on the high horse...
DECLARE #Forecast30days DATE
SET #Forecast30days = CURRENT_TIMESTAMP + 30
SELECT
wo.LOCATION
,COUNT(CASE WHEN wo.actfinish <= wo.targcompdate THEN wo.WONUM ELSE NULL END) AS OnTimePMWOs
,COUNT(CASE WHEN wo.actfinish >= wo.targcompdate THEN wo.WONUM ELSE NULL END) AS PastDuePMWOs
,COUNT(CASE WHEN pm.changedate IS NOT NULL THEN wo.WONUM ELSE NULL END) AS Forecast30days
FROM WORKORDER AS wo
LEFT JOIN PMFORECAST AS pm
ON wo.changedate = pm.changedate
AND pm.forecastdate >= #Forecast30days
WHERE wo.worktype = 'pm'
AND ((wo.actfinish IS NOT NULL AND wo.targcompdate IS NOT NULL)
OR (pm.changedate IS NOT NULL))
GROUP BY
wo.LOCATION

SQL Select Query - problem pivoting rows into columns

I have three tables in an SQL 2005 database, that I need to query and display on one row. The tables are:
MasterStock
StockID, Description
1, Plate
2, Bowl
ShopStock
ShopID, StockID, StockLevel
1,1,6
2,1,0
3,1,0
4,1,10
Sales
StockId, ShopId, SoldQuantity, transDate
1, 1, 1, 5/1/2011
1,2,1, 5/1/2011
I need to get them to show one row:
StockID, Description, 1 Sales, 1 Stock, 2 Sales, 2 Stock, 3 Sales,…
I have managed to get what somewhere with the query below:
SELECT MasterStock.StockID, MasterStock.Description,
SUM(CASE WHEN sales.shopid = 1 THEN sales.Soldquantity ELSE 0 END) AS [1 Sold],
MAX(CASE WHEN shopstock.shopid = 1 THEN shopstock.stockLevel ELSE 0 END) AS [1 Stock],
SUM(CASE WHEN sales.shopid = 2 THEN sales.Soldquantity ELSE 0 END) AS [2 Sold],
MAX(CASE WHEN shopstock.shopid = 2 THEN shopstock.stockLevel ELSE 0 END) AS [2 Stock],
SUM(CASE WHEN sales.shopid = 3 THEN sales.Soldquantity ELSE 0 END) AS [3 Sold],
MAX(CASE WHEN shopstock.shopid = 3 THEN shopstock.stockLevel ELSE 0 END) AS [3 Stock],
SUM(CASE WHEN sales.shopid = 4 THEN sales.Soldquantity ELSE 0 END) AS [4 Sold],
MAX(CASE WHEN shopstock.shopid = 4 THEN shopstock.stockLevel ELSE 0 END) AS [4 Stock]
FROM ShopStock INNER JOIN
Sales ON ShopStock.StockID = Sales.StockID AND ShopStock.shopID = Sales.ShopID
INNER JOIN MasterStock ON ShopStock.StockID = MasterStock.StockID
WHERE (sales.transdate > 1/1/2010)
GROUP BY MasterStock.StockID, MasterStock.Description
However, if there are no sales for the product it doesn’t show any stock levels. If I remove the shopID join on shopstock and sales it shows the stock levels, but reports inaccurate sales - multiplies by four (one for each shopstock record?).
I know I’m missing something here, but I’m not getting anywhere! Any help would be greatly received.
Two problems:
1) You need a LEFT OUTER JOIN between ShopStock and Sales, which will ensure that the query returns records from ShopStock even if there are no related entries in Sales. By definition, an INNER JOIN will not return records from either side of the join, if one of the sides is missing records.
2) You need to move your sales.transdate > 1/1/2010 condition to the inner join, rather than the WHERE clause. Conditions in the WHERE clause will be logically applied after any logic in the table joins. So even if you get your joins right, the where clause will filter out stock without sales because sales.transdate will appear null.
Something like this:
FROM ShopStock LEFT OUTER JOIN Sales
ON ShopStock.StockID = Sales.StockID
AND Sales.transdate > 1/1/2010
INNER JOIN // the rest of your joins here
I'm guessing you also want >= on your transdate filter as well, but that's just a hunch.
Good luck!