Sum only the selected rows - sql

The below query gets customer statement:
SELECT t.S_Type,t.Number, t.Debit, t.Credit,t.CustID,b.Balance
FROM Statement as t
CROSS apply
(
SELECT Balance = SUM(Debit) - SUM(Credit)
FROM Statement as x
WHERE x.Number<= t.Number
) b
ORDER BY t.Number
Query result:
type # Debit credit cid balance
Sales Invoice 1 200.00 0.00 3 200.00
Sales Invoice 10 850.00 0.00 3 1050.00
Service Invoice 11 90.00 0.00 21 1140.00
Sales Invoice 12 20.00 0.00 3 1160.00
Sales Invoice 13 200.00 0.00 2 1360.00
Sales Invoice 14 20.00 0.00 9 1380.00
Sales Invoice 15 120.00 0.00 17 1500.00
Sales Invoice 16 100.00 0.00 19 1600.00
Sales Invoice 17 140.00 0.00 20 1740.00
Sales Invoice 18 4250.00 0.00 16 5990.00
Sales Invoice 19 2500.00 0.00 22 8490.00
Sales Invoice 2 100.00 0.00 7 8590.00
Sales Invoice 20 1225.00 0.00 2 9815.00
Sales Invoice 3 200.00 0.00 1 10015.00
Sales Invoice 4 520.00 0.00 2 10535.00
Sales Invoice 5 25.00 0.00 1 10560.00
Sales Invoice 6 160.00 0.00 2 10720.00
Sales Invoice 7 20.00 0.00 7 10740.00
Sales Invoice 9 850.00 0.00 2 11590.00
But I'd like to get the statement for customer with id 7. The query I use for this is:
SELECT t.S_Type,t.Number, t.Debit, t.Credit,t.CustID,b.Balance
FROM Statement as t
CROSS apply
(
SELECT Balance = SUM(Debit) - SUM(Credit)
FROM Statement as x
WHERE x.Number<= t.Number
) b
where t.CustID='7'
ORDER BY t.Number
This query's result is:
type # Debit credit cid balance
Sales Invoice 2 100.00 0.00 7 8590.00
Sales Invoice 7 20.00 0.00 7 10740.00
However, that result is wrong. I expect it to be:
balance
100.00
120.00
What's wrong with the query?

To fix your query you need to add the CustID to the correlated cross apply so that the sum only is calculated for the customer in the outer scope and not for all:
SELECT t.S_Type, t.Number, t.Debit, t.Credit, t.CustID, b.Balance
FROM Statement AS t
CROSS APPLY
(
SELECT Balance = SUM(Debit) - SUM(Credit)
FROM Statement AS x
WHERE x.Number <= t.Number AND t.CustID = x.CustID
) b
WHERE t.CustID='7'
ORDER BY t.Number
If you're using SQL Server 2012+ a better alternative that also should perform better would be to use sum() as a window function:
SELECT
S_Type, Number, Debit, Credit, CustId,
Balance = SUM(Debit - Credit) OVER (PARTITION BY CustId ORDER BY Number)
FROM Statement
WHERE CustID='7'
ORDER BY Number
Your original query (without the where clause) would be:
SELECT
S_Type, Number, Debit, Credit, CustID,
Balance = SUM(Debit - Credit) OVER (ORDER BY number)
FROM Statement
ORDER BY Number

In CROSS APPLY add following in the WHERE:
AND x.CustID = t.CustID
Finally I got what you need. To make this you need to filter by customer also. Otherwise the cumulative balance is made on whole set and after that you show only customer 7.
After the change you will have cumulative balance only for customer 7.
Proper SQL:
SELECT t.S_Type,t.Number, t.Debit, t.Credit,t.CustID,b.Balance
FROM Statement as t
CROSS apply
(
SELECT Balance = SUM(Debit) - SUM(Credit)
FROM Statement as x
WHERE x.Number<= t.Number and x.CustID = t.CustID
) b
where t.CustID='7'
ORDER BY t.Number

Related

Monthly Actual Cost vs Monthly Contract Amount

I have a project that pulls cost per category (labor, equipment, indirect), per month for a job and then shows a running, cumulative total of the contract amount per month. (This calculation is based on the total contract amount divided by the number of months in a project. Then the second month is the first month + second month, third month is first month + second month + third month, etc.)
I have the query shown below. When I run this, it returns the data shown under current results. What I need is the running total column populated even when there are no costs as shown under expected results. How do I accomplish this?
Current results:
fiscalMonth
Labor
Equipment
Indirect
RunningTotal
ContractPerMonth
2021-12-01
0.00
0.00
0.00
0.00
0.00
2022-01-01
6518.78
0.00
0.00
2141444.44
2141444.44
2022-02-01
8563.68
0.00
58.81
4282888.88
2141444.44
2022-03-01
7271.28
429.14
139167.21
6424333.32
2141444.44
2022-04-01
44538.32
2117.64
59379.53
8565777.76
2141444.44
2022-05-01
-14932.44
2476.85
1279972.38
10707222.20
2141444.44
2022-06-01
3701.65
250.00
992.45
12848666.64
2141444.44
2022-07-01
0.00
0.00
0.00
0.00
0.00
2022-08-01
0.00
0.00
0.00
0.00
0.00
Expected results:
fiscalMonth
Labor
Equipment
Indirect
RunningTotal
ContractPerMonth
2021-12-01
0.00
0.00
0.00
2141444.44
0.00
2022-01-01
6518.78
0.00
0.00
4282888.88
2141444.44
2022-02-01
8563.68
0.00
58.81
6424333.32
2141444.44
2022-03-01
7271.28
429.14
139167.21
8565777.76
2141444.44
2022-04-01
44538.32
2117.64
59379.53
10707222.20
2141444.44
2022-05-01
-14932.44
2476.85
1279972.38
12848666.64
2141444.44
2022-06-01
3701.65
250.00
992.45
14990111.08
2141444.44
2022-07-01
0.00
0.00
0.00
17131555.52
2141444.44
2022-08-01
0.00
0.00
0.00
19272999.96
0.00
Create Table #costs(fiscalMonth date, Labor numeric(12,2), Equipment numeric(12,2), Indirect numeric(12,2), RunningTotal numeric(12,2), ContractPerMonth numeric(12,2))
Declare #startDate as date, #endDate as date, #lastMonth as date,#count int
select #startDate = StartMonth, #endDate = ProjCloseDate from JCCM
WHERE
ltrim(rtrim(Contract)) = (#Job)
insert into #costs
SELECT
Mth
,isnull(Sum(LaborCost),0) as LaborCost
,Isnull(Sum(EquipmentCost),0) as EquipmentCost
,isnull(Sum(IndirectCost),0) as IndirectCost
,SUM (ContractAmtPerMonth) OVER (ORDER BY Mth) AS RunningTotal
,ContractAmtPerMonth
FROM(
SELECT
cp.Mth
,CASE WHEN ct.JBCostTypeCategory = 'L' THEN SUM(cp.ActualCost) END as LaborCost
,CASE WHEN ct.JBCostTypeCategory = 'E' THEN SUM(cp.ActualCost) END as EquipmentCost
,CASE WHEN ct.JBCostTypeCategory = 'O' THEN SUM(cp.ActualCost) END as IndirectCost
,(Select CASE WHEN Sum(cm.ContractAmt) <> 0 THEN CAST(SUM(cm.ContractAmt / ((DATEDIFF(Month,cm.StartMonth,cm.ProjCloseDate)+1))) as numeric(12,2)) END from JCCM cm WHERE cm.JCCo = jm.JCCo and cm.Contract = jm.Contract) as ContractAmtPerMonth
FROM
JCCP cp
LEFT JOIN JCCT ct ON cp.PhaseGroup = ct.PhaseGroup AND cp.CostType = ct.CostType
LEFT JOIN JCJM jm ON cp.JCCo = jm.JCCo and cp.Job = jm.Job
WHERE
cp.JCCo IN (#Company)
AND ltrim(rtrim(cp.Job)) = (#Job)
AND ct.JBCostTypeCategory IN ('L','E','O')
GROUP BY
jm.JCCo
,jm.Job
,jm.Contract
,cp.Mth
,ct.JBCostTypeCategory
)cost
GROUP BY
Mth
,ContractAmtPerMonth
WHILE (#startDate <= #endDate)
BEGIN
print CAST(#startDate AS VARCHAR(10)) + ' ' + CAST(#endDate AS VARCHAR(10))
select #count = Count (*) from #costs
print #count
if Not exists(select 1 from #costs where fiscalMonth = #startDate)
begin
insert into #costs (fiscalMonth, Labor, Equipment, Indirect,RunningTotal, ContractPerMonth ) values (#startDate,0,0,0,0,0)
end
set #startDate = DATEADD(month, 1, #startDate);
END
select * from #costs order by fiscalMonth
Drop table #costs
I was actually able to figure out how to accomplish what I needed. I modified how I created my table (changed table name from #costs to #months). This pulled the months, row number, and contract amount. I then joined it to my existing cost query. In SSRS, I'm able to calculate the running total by multiplying the monthly contract amount by the row number. Seems to be working.
create table #months (JCCo numeric, Job varchar(10), StartDate date, EndDate date)
insert into #months
SELECT JCCo,Contract,StartMonth, ProjCloseDate FROM JCCM WHERE JCCo = (#Company) AND Contract = (#Job);
with Q1 as
(
select JCCo,Job,StartDate as Mth
from #months
union all
select q.JCCo,q.Job,DATEADD(m, 1, q.Mth)
from Q1 q
inner join #months t on t.Job = q.Job
where Mth <= (DATEADD(MONTH,-1,t.EndDate))
)
select ROW_NUMBER() OVER ( order by Q1.Job, Q1.Mth) as Row#, Q1.JCCo, Q1.Job, Q1.Mth as fiscalMonth, ISNULL(cd.LaborCost,0) as Labor, ISNULL(cd.EquipmentCost,0) as Equipment, ISNULL(cd.IndirectCost,0) as Indirect, CAST(cm.ContractAmt / ((DATEDIFF(Month,cm.StartMonth,cm.ProjCloseDate)+1)) as DECIMAL(12,2)) as MonthlyContract, cm.ContractAmt as TotalContract
from Q1

How to add Totals to rows groups generated by a CTE

I have this CTE. My goal is to display all the rows in the result set and the total of a column which has a column with the same value.
WITH CTEVal AS
(SELECT RNum, Amount, Tag, TAmount,
ROW_NUMBER() OVER(ORDER BY CONVERT(DATETIME, TDate, 101) DESC) AS RowNum
from [dbo].[viewJD])
SELECT TDate, RNum,
CASE WHEN Tag = 'DR' THEN CONVERT(VARCHAR(50),Amount) ELSE '0.00' END AS Debit,
CASE WHEN Tag = 'CR' THEN CONVERT(VARCHAR(50),Amount) ELSE '0.00' END AS Credit FROM CTEVal
which returns the result set
TDate Rnum Debit Credit
12-22-2015 8 100 0.00
12-22-2015 8 300 0.00
12-22-2015 8 0.00 400
12-22-2015 9 250 0.00
12-22-2015 9 250 0.00
12-22-2015 9 0.00 500
12-23-2015 10 600 0.00
12-23-2015 10 0.00 100
12-23-2015 10 0.00 500
Expected result set
TDate Rnum Debit Credit
12-22-2015 8 100 0.00
12-22-2015 8 300 0.00
12-22-2015 8 0.00 400
TOTAL 400 400
12-22-2015 9 250 0.00
12-22-2015 9 250 0.00
12-22-2015 9 0.00 500
TOTAL 500 500
12-23-2015 10 600 0.00
12-23-2015 10 0.00 100
12-23-2015 10 0.00 500
TOTAL 600 600
Selecting * from viewJD would return
TDate Rnum Amount Tag TAmount
12-22-2015 8 100 DR 400
12-22-2015 8 300 DR 400
12-22-2015 8 400 CR 400
12-22-2015 9 250 DR 500
12-22-2015 9 250 DR 500
12-22-2015 9 500 CR 500
12-23-2015 10 600 DR 600
12-23-2015 10 100 CR 600
12-23-2015 10 500 CR 600
Sum of DR = sum of CR is = TAmount
I have a solution. I know it is not a fine solution. I have tried using Rollup but could not get the exact result format. So I came up with this.
This is indeed a long solution.
But I hope you would get some idea to solve the problem from this.
Here I have first taken the Debit and Credit separated values into a table variable.
Then I applied SUM to the values and took the complete result to a cte.
I applied DENSE_RANK to get the correct ordering such that the 'total' row comes after the detail rows.
Finally selected records from cte with value 'Total' for column RNum for the row showing 'total'.
CREATE TABLE viewJD
(
TDate Date,
Rnum INT,
Amount INT,
Tag VARCHAR(2),
TAmount INT
)
INSERT INTO viewJD
VALUES
('12-22-2015', 8 , 100 , 'DR', 400),
('12-22-2015' , 8 , 300 , 'DR' , 400),
('12-22-2015', 8 , 400 , 'CR' , 400),
('12-22-2015' , 9 , 250 , 'DR' , 500 ),
('12-22-2015' , 9 , 250 , 'DR' , 500 ),
('12-22-2015' , 9 , 500 , 'CR' , 500 ),
('12-23-2015' , 10 , 600 , 'DR' , 600 ),
('12-23-2015' , 10 , 100 , 'CR' , 600 ),
('12-23-2015' , 10 , 500 , 'CR' , 600 )
DECLARE #ValTable TABLE
(
TDate Date,
Rnum INT,
Debit INT,
Credit INT
)
INSERT INTO #ValTable
SELECT TDate, RNum,
CASE WHEN Tag = 'DR' THEN Amount ELSE 0.00 END AS Debit,
CASE WHEN Tag = 'CR' THEN Amount ELSE 0.00 END AS Credit
FROM viewJD
;WITH cte
AS (
SELECT *,RowOrder=DENSE_RANK() OVER (PARTITION BY NULL ORDER By RNUM) from #ValTable
UNION ALL
SELECT NULL,
RNum ,
SUM([Debit]) AS Debit,
SUM([Credit]) AS Credit ,
RowOrder =DENSE_RANK() OVER (PARTITION BY NULL ORDER By RNUM)
FROM #ValTable
GROUP BY TDate,RNum
)
SELECT TDate,
Rnum = CASE WHEN TDate IS NULL THEN 'Total' ELSE CONVERT(VARCHAR(20),Rnum ) END,
Debit,
Credit
FROM cte
ORDER BY roworder,Rnum,TDate DESC
DROP TABLE viewJD
The format you provide in your expected output is not feasible or advisable. The following code stores them as separate columns.
SELECT TDate,
RNum,
CASE WHEN Tag = 'DR' THEN Amount ELSE 0.00 END AS Debit,
CASE WHEN Tag = 'CR' THEN Amount ELSE 0.00 END AS Credit,
(select sum(Amount)
from [dbo].[viewJD] td2
where td2.RNum = td1.RNum
and Tag = 'DR') as Debit_Total,
(select sum(Amount)
from [dbo].[viewJD] td2
where td2.RNum = td1.RNum
and Tag = 'CR') as Credit_Total
FROM [dbo].[viewJD] td1
This will give you output like
TDate Rnum Debit Credit Debit_Total Credit_Total
12-22-2015 8 100 0.00 400.00 400.00
12-22-2015 8 300 0.00 400.00 400.00
12-22-2015 8 0.00 400 400.00 400.00
12-22-2015 9 250 0.00 500.00 500.00
12-22-2015 9 250 0.00 500.00 500.00
12-22-2015 9 0.00 500 500.00 500.00
12-23-2015 10 600 0.00 600.00 600.00
12-23-2015 10 0.00 100 600.00 600.00
12-23-2015 10 0.00 500 600.00 600.00
Not the nicest to look at, but the easiest to use in other queries.

SQL Running Total Reset on Condition

I have the following table:
Transaction History Table
TransactionHistoryId ProductCode Type Quantity PurchasePrice CurrentPrice
1 Product1 B 10 3.00 2.00
2 Product1 B 5 7.00 2.00
3 Product1 S -7 7.00 2.00
4 Product1 S -8 3.00 3.00
5 Product1 B 4 10.00 10.00
6 Product1 B 5 12.00 12.00
8 Product2 B 8 20.00 20.00
I would like to acheive the following table:
TransactionHistoryId ProductCode Type Quantity PurchasePrice QtyRunning PriceRunning
1 Product1 B 10 3.00 10 30.00
2 Product1 B 5 7.00 15 65.00
3 Product1 S -7 7.00 8 65.00
4 Product1 S -8 3.00 0 0.00
5 Product1 B 4 10.00 4 40.00
6 Product1 B 5 12.00 9 100.00
8 Product2 B 8 20.00 8 160.00
Create Table SQL
IF OBJECT_ID('TEMPDB..#TransactionHistory') IS NOT NULL
DROP TABLE #TransactionHistory
create table #TransactionHistory
(TransactionHistoryId int,
ProductCode varchar(10),
Type char(1),
Quantity smallint,
PurchasePrice decimal(18,2),
CurrentPrice decimal(18,2)
)
insert into #TransactionHistory
values
(1,'Product1','B',10,3.00,2.00),
(2,'Product1','B',5,7.00,2.00),
(3,'Product1','S',-7,7.00,2.00),
(4,'Product1','S',-8,3.00,3.00),
(5,'Product1','B',4,10.00,10.00),
(6,'Product1','B',5,12.00,12.00),
(8,'Product2','B',8,20.00,20.00)
Rules
PriceRunningTotal resets when the quantity running total is 0
PriceRunningTotal sums up only Type = 'B' (buys), when Type = 'S' (sold) keep the previous purchase price running total
Notice there is a Product 2 so it should have it's own running count independent of Product 1
Purpose
A query to ultimately find out the following:
Product Quantity AdjustedPurchasePrice
Product1 9 $11.11
Product2 8 $20
I used the following SQL Server 2012 query to get the result, but I feel it could be done much better:
Query
SELECT *,
PriceRunningTotalFinal =
SUM(CASE
WHEN QuantityRunningTotal = 0 THEN -1 * PriceRunningTotal
WHEN Quantity < 0 THEN 0 ELSE PurchasePrice * Quantity END) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
)
FROM (
SELECT TransactionHistoryId, ProductCode, Type, Quantity, PurchasePrice,
QuantityRunningTotal = SUM(Quantity) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
),
PriceRunningTotal = SUM(CASE WHEN Quantity < 0 THEN 0 ELSE PurchasePrice * Quantity END) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
)
FROM TransactionHistory
) AS Results1
ORDER BY ProductCode;
Problem
Ideally I would of liked to use the QuantityRunningTotal within another query but I can't nest windowed functions.
Anyone know of a more efficient way to achieve this result?
Hmmm. I think something like this:
select th.*,
sum(case when type = 'B' then Quantity * PurchasePrice
else 0
end) over (partition by grp, ProductCode order by TransactionHistoryId
) as PriceRunningTotal
from (select th.*,
sum(case when running_quantity = 0 then 1 else 0 end) over (partition by ProductCode order by TransactionHistoryId) as grp
from (select th.*,
sum(quantity) over (partition by ProductCode order by TransactionHistoryId
) as running_quantity
from TransactionHistory th
) th;
I'm not sure if this is the same logic as your query. For this query:
The innermost subquery calculates the running quantity.
The middle subquery calculates a group based on the number of times the running quantity has been 0.
The outermost query then calculates the running price.

SQL See Whether Two or More Columns In a Table is Greater Than 0

I have ran in to a little problem and would appreciate any help.
My Table is such:
CASH | CREDIT CARD | DEBIT CARD | ACCOUNT | OTHER
-------------------------------------------------
0.00 0.00 0.00 0.00 0.00
1.00 0.00 0.00 0.00 0.00
2.00 1.00 0.00 0.00 0.00
My aim is to SELECT * FROM any of the above rows that have more than one column > 0.
So the third row would be selected in this scenario with the above table.
SELECT
[CASH], [CREDIT CARD], [DEBIT CARD], [ACCOUNT], [OTHER]
FROM table
WHERE
CASE WHEN [CASH] > 0 THEN 1 ELSE 0 END+
CASE WHEN [CREDIT CARD] > 0 THEN 1 ELSE 0 END+
CASE WHEN [DEBIT CARD] > 0 THEN 1 ELSE 0 END+
CASE WHEN [ACCOUNT] > 0 THEN 1 ELSE 0 END+
CASE WHEN [OTHER] > 0 THEN 1 ELSE 0 END >= 2
I prefer t-clausen's answer but just as an exercise, I decide to try it as an UNPIVOT followed by a PIVOT, so that we could write it using more of the general SQL tools:
declare #t table (SomeID int,Cash money,Credit money,Debit money,Account money,Other money)
insert into #t(SomeID,Cash,Credit,Debit,Account,Other) values
(1,0.00,0.00,0.00,0.00,0.00),
(2,1.00,0.00,0.00,0.00,0.00),
(3,2.00,1.00,0.00,0.00,0.00)
;With Unpiv as (
select *,SUM(CASE WHEN MoneyValue > 0.00 THEN 1 ELSE 0 END) OVER (PARTITION BY SomeID) as cnt
from #t t
unpivot (MoneyValue for MoneyType in (Cash,Credit,Debit,Account,Other)) x
), Repiv as (
select *
from Unpiv u
pivot (SUM(MoneyValue) for MoneyType in (Cash,Credit,Debit,Account,Other)) x
where
cnt >= 2
)
select * from Repiv
This does assume that you've got another column (here, called SomeID) by which each row can be uniquely identified.
Result:
SomeID cnt Cash Credit Debit Account Other
----------- ----------- --------------------- --------------------- --------------------- --------------------- ---------------------
3 2 2.00 1.00 0.00 0.00 0.00
Just hoping the above might be more adaptable for some variants of the requirements.
SELECT
[CASH], [CREDIT CARD], [DEBIT CARD], [ACCOUNT], [OTHER]
FROM table
WHERE (
SELECT COUNT(*)
FROM (VALUES ([CASH]),([CREDIT CARD]),([DEBIT CARD]),([ACCOUNT]),([OTHER])) t(value)
WHERE value > 0
) >= 2

Sql Select from two tables in single query

I have Two Tables as under
SalesDetails PurchaseDetails
Date SaleOrderId ProductId Qty Date PurcsOrderId ProductId Qty
2/2/12 S_1 P_1 4 1/2/12 PO_1 P_1 50
3/2/12 S_2 P_1 5 4/2/12 PO_2 P_1 50
3/2/12 S_2 P_2 7
6/2/12 S_3 P_1 3
9/2/12 S_4 1 5
from these two tables i want to show a report like this
Product Inquiry Report
ProductId: P_1
Date TrId Debit Credit
1/2/12 PO_1 50
2/2/12 S_1 4
3/2/12 S_2 5
4/2/12 PO_2 50
6/2/12 S_3 3
Here Qty column of purchasedetails becomes 'Debit' in the report and Qty in SalesDetails becomes 'Credit' column in Report.In report, the transaction should be shown in order to Date as shown Is there a way to perform this in a single query, if yes, then how? if no, then is there any other solution? Thanks in advance
Use UNION:
SELECT Date, SaleOrderID as TrId, NULL as Debit, Qty as Credit
FROM SalesDetails
UNION
SELECT Date, PurcsOrderId AS TrId, Qty AS Debit, NULL as Credit
FROM PurchaseDetails
ORDER BY Date
SQL Fiddle:
SELECT *
FROM
(
SELECT sa.Date, sa.SaleOrderId AS TRId, Null AS Debit, sa.Qty AS Credit
FROM SalesDetails sa
UNION
SELECT pa.Date, pa.PurcsOrderId AS TRId, pa.Qty AS Debit, Null AS Credit
FROM PurchaseDetails pa
) m
ORDER BY 1
If you would rather the values be 0 then use 0 AS Debit and 0 AS Credit respectively.