Related
I´m having issues with the following query. I have two tables; Table Orderheader and table Bought. The first query I execute gives me, for example, two dates. Based on these two dates, I need to find Production data AND, based on the production data, I need to find the Bought data, and combine those data together. Lets say I do the following:
Select Lotdate From Orderheader where orhsysid = 1
This results in two rows: '2019-02-05' and '2019-02-04'. Now I need to do two things: I need two run two queries using this result set. The first one is easy; use the dates returned and get a sum of column A like this:
Select date, SUM(Amount) from Orderheader where date = Sales.date() [use the two dates here]
The second one is slighty more complicated, I need to find the last day where something has been bought based on the two dates. Production is everyday so Productiondate=Sales.date()-1. But Bought is derived from Productionday and is not everyday so for every Productionday it needs to find the last Boughtday. So I can't say where date = Orderheader.date. I need to do something like:
Select date, SUM(Amount)
FROM Bought
WHERE date = (
SELECT top 1 date
FROM Bought
WHERE date < Orderheader.date)
But twice, for both the dates I got.
This needs to result in 1 table giving me:
Bought.date, Bought.SUM(AMOUNT), Orderheader.date, Orderheader.SUM(AMOUNT)
All based on the, possible multiple, Lotdate(s) I got from the first query from Sales table.
I've been struggling with this for a moment now, using joins and nested queries but I can't seem to figure it out!
Example sample:
SELECT CONVERT(date,ORF.orfDate) as Productiedatum, SUM(orlQuantityRegistered) as 'Aantal'
FROM OrderHeader ORH
LEFT JOIN OrderFrame ORF ON ORH.orhFrameSysID = ORF.orfSysID
LEFT JOIN OrderLine ORL ON ORL.orhSysID = ORH.orhSysID
LEFT JOIN Item ON Item.itmSysID = ORL.orlitmSysID
where CONVERT(date,ORF.orfDate) IN
(
SELECT DISTINCT(CONVERT(date, Lot.lotproductiondate)) as Productiedatum
FROM OrderHeader ORH
LEFT JOIN Registration reg ON reg.regorhSysID = ORH.orhSysID
LEFT JOIN StockRegistration stcreg ON stcreg.stcregRegistrationSysID = reg.regSysID
LEFT JOIN Lot ON Lot.lotSysID = stcregSrclotSysID
WHERE ORH.orhSysID = 514955
AND regRevokeRegSysID IS NULL
AND stcregSrcitmSysID = 5103
)
AND ORL.orlitmSysID = 5103
AND orldirSysID = 2
AND NOT orlQuantityRegistered IS NULL
GROUP BY Orf.orfDate
Sample output:
Productiedatum Aantal
2019-02-05 20
2019-02-06 20
Here I used a nested subquery to get the results from 'Production' (orderheader) because I just can use date = date. I'm struggling with the Sales part where I need to find the last date(s) and use those dates in the Sales table to get the sum of that date.
Expected output:
Productiedatum Aantal Boughtdate Aantal
2019-02-04 20 2019-02-01 55
2019-02-05 20 2019-02-04 60
Try this.
IF OBJECT_ID('tempdb..#Production') IS NOT NULL DROP TABLE #Production
IF OBJECT_ID('tempdb..#Bought') IS NOT NULL DROP TABLE #Bought
CREATE table #Production(R_NO int,ProductionDate datetime,ProductionAmount float)
CREATE table #Bought(R_NO int,Boughtdate datetime,Boughtamount float)
insert into #Production(ProductionDate,ProductionAmount,R_NO)
select p.date ProductionDate,sum(Amount) ProductionAmount,row_number()over (order by p.date) R_NO
from Production P
join Sales s on p.date=S.date-1
where orhsysid=1
group by p.date
declare #loop int,#ProdDate datetime
select #loop =max(R_NO) from #Production
while (1<=#loop)
begin
select #ProdDate=ProductionDate from #Production where r_no=#loop
insert into #Bought(Boughtdate,Boughtamount,R_NO)
select Date,Sum(Amount),#loop R_NO from Bought where date=(
select max(date) from bought B
where B.Date<#ProdDate)
group by Date
set #loop=#loop-1
end
select ProductionDate,ProductionAmount,Boughtdate,Boughtamount from #Bought B
join #Production p on B.R_NO=P.R_NO
I've been trying for days to solve this problem to no solution.
I want to get the lowest running balance in a group.
Here is a sample data
The running balance is imaginary and is not part of the table.
the running balance is also computed dynamically.
the problem is I want to get the lowest running balance in a Specific month (January)
so the output should be 150 for memberid 10001 and 175 for memberid 10002 as highlighted in the image.
my desired out put should be
memberid | balance
10001 | 150
10002 | 175
Is that possible using sql query only?
PS. Using c# to compute lowest running balance is very slow since I have more than 600,000 records in my table.
I've updated the question.
The answer provided by Mihir Shah gave me the idea how solve my problem.
His answer takes to much time to process making it as slow as my computation on my c# program because his code loops on every record.
Here is my answer to get the minimum lowest value in a specific group (specific month) with a running value or running total without sacrificing a lot of performance.
with IniMonth1 as
(
select a.memberid, a.iniDeposit, a.iniWithdrawal,
(cast(a.iniDeposit as decimal(10,2)) - cast(a.iniWithdrawal as decimal(10,2))) as RunningTotal
from
(
select b.memberid, sum(b.depositamt) as iniDeposit, sum(b.withdrawalamt) as iniWithdrawal
from savings b
where trdate < '01/01/2016'
group by b.memberid
) a /*gets all the memberid, sum of deposit amount and withdrawal amt from the beginning of the savings before the specific month */
where cast(a.iniDeposit as decimal(10,2)) - cast(a.iniWithdrawal as decimal(10,2)) > 0 /*filters zero savings */
)
,DetailMonth1 as
(
select a.memberid, a.depositamt,a.withdrawalamt,
(cast(a.depositamt as decimal(10,2)) - cast(a.withdrawalamt as decimal(10,2))) as totalBal,
Row_Number() Over(Partition By a.memberid Order By a.trdate Asc) RowID
from savings a
where
a.trdate >= '01/01/2016'
and
a.trdate <= '01/31/2016'
and (a.depositamt<>0 or a.withdrawalamt<>0)
) /* gets all record within the specific month and gives a no of row as an id for the running value in the next procedure*/
,ComputedDetailMonth1 as
(
select a.memberid, min(a.runningbalance) as MinRunningBal
from
(
select a.rowid, a.memberid, a.totalbal,
(
sum(b.totalbal) +
(case
when c.runningtotal is null then 0
else c.runningtotal
end)
)as runningbalance , c.runningtotal as oldbalance
from DetailMonth1 a
inner join DetailMonth1 b
on b.rowid<=a.rowid
and a.memberid=b.memberid
left join IniMonth1 c
on a.memberid=c.memberid
group by a.rowid,a.memberid,a.totalbal,c.runningtotal
) a
group by a.memberid
) /* the loop is only for the records of the specific month only making it much faster */
/* this gets the running balance of specific month ONLY and ADD the sum total of IniMonth1 using join to get the running balance from the beginning of savings to the specific month */
/* I then get the minimum of the output using the min function*/
, OldBalanceWithNoNewSavingsMonth1 as
(
select a.memberid,a.RunningTotal
from
IniMonth1 a
left join
DetailMonth1 b
on a.memberid = b.memberid
where b.totalbal is null
)/*this gets all the savings that is not zero and has no transaction in the specific month making and this will become the default value as the lowest value if the member has no transaction in the specific month. */
,finalComputedMonth1 as
(
select a.memberid,a.runningTotal as MinRunTotal from OldBalanceWithNoNewSavingsMonth1 a
union
select b.memberid,b.MinRunningBal from ComputedDetailMonth1 b
)/*the record with minimum running total with clients that has a transaction in the specific month Unions with the members with no current transaction in the specific month*/
select * from finalComputedMonth1 order by memberid /* display the final output */
I have more than 600k savings record on my savings table
Surprisingly the performance of this code is very efficient.
It takes almost 2hr using my c# program to manually compute every record of all the members.
This code makes only 2 secs and at most 9 secs just to compute everything.
i Just display to c# for another 2secs.
The output of this code was tested and compared with my computation using my c# program.
May be below one is help you
Set Nocount On;
Declare #CashFlow Table
(
savingsid Varchar(50)
,memberid Int
,trdate Date
,deposit Decimal(18,2)
,withdrawal Decimal(18,2)
)
Insert Into #CashFlow(savingsid,memberid,trdate,deposit,withdrawal) Values
('10001-0002',10001,'01/01/2015',1000,0)
,('10001-0003',10001,'01/07/2015',25,0)
,('10001-0004',10001,'01/13/2015',25,0)
,('10001-0005',10001,'01/19/2015',0,900)
,('10001-0006',10001,'01/25/2015',25,0)
,('10001-0007',10001,'01/31/2015',25,0)
,('10001-0008',10001,'02/06/2015',25,0)
,('10001-0009',10001,'02/12/2015',25,0)
,('10001-0010',10001,'02/18/2015',0,200)
,('10002-0001',10002,'01/01/2015',500,0)
,('10002-0002',10002,'01/07/2015',25,0)
,('10002-0003',10002,'01/13/2015',0,200)
,('10002-0004',10002,'01/19/2015',25,0)
,('10002-0005',10002,'01/25/2015',25,0)
,('10002-0006',10002,'01/31/2015',0,200)
,('10002-0007',10002,'02/06/2015',25,0)
,('10002-0008',10002,'02/12/2015',25,0)
,('10002-0009',10002,'02/12/2015',0,200)
;With TrialBalance As
(
Select Row_Number() Over(Partition By cf.memberid Order By cf.trdate Asc) RowNum
,cf.memberid
,cf.deposit
,cf.withdrawal
,cf.trdate
From #CashFlow As cf
)
,RunningBalance As
(
Select tb.RowNum
,tb.memberid
,tb.deposit
,tb.withdrawal
,tb.trdate
From TrialBalance As tb
Where tb.RowNum = 1
Union All
Select tb.RowNum
,rb.memberid
,Cast((rb.deposit + tb.deposit - tb.withdrawal) As Decimal(18,2))
,rb.withdrawal
,tb.trdate
From TrialBalance As tb
Join RunningBalance As rb On tb.RowNum = (rb.Rownum + 1) And tb.memberid = rb.memberid
)
Select rb.memberid
,Min(rb.deposit) As runningBalance
From RunningBalance As rb
Where Year(rb.trdate) = 2015
And Month(rb.trdate) = 1
Group By rb.memberid
I am trying to create a query that will allow me to split a single figure over multiple rows.
For example, purchase order x may have 15 items allocated to it. Shipping cost is, say, 9.95. How can I calculate 1/15th of 9.95 and then update the cost price of the stock with 1/15th of the shipping cost?
Therefore the cost price of the item would increase from 4.50 to 5.16 (4.50 + 0.66).
Here's a solution for SQL Server:
update ol
set price = price + 5.0 / ol.LineCount
from [Order] o
join (
select *
, count(*) over () as LineCount
from OrderLine
) ol
on o.ID = ol.OrderID
where o.OrderNr = 'Ord1';
Live example at SQL Fiddle.
If you're using another DBMS, please update your post!
I have created a product table and updated the shipping price of items.
Hope that's what you are looking for.
INSERT INTO [TravelAgentDB].[dbo].[Product]
([ID]
,[Name]
,[Price]
,[shippingPrice])
VALUES
(1,'Nexus 7' , 250 , 20),
(2,'Nexus 7 case' , 50 , 20),
(3,'Nexus 7 headphone' , 20 , 20)
GO
select * from product
Declare #itemsCount int
Select #itemsCount = count(*) From product where id in (1,2,3)
Declare #totalShippingPrice Decimal(18,2) = 9.95
Declare #shippingPriceperItem Decimal(18,2) = #totalShippingPrice / #itemsCount
Update Product
set [shippingPrice] = #shippingPriceperItem
Where id in (1,2,3)
select * from product
I am struggling to find a good way to run running totals with a group by in it, or the equivalent. The below cursor based running total works on a complete table, but I would like to expand this to add a "Client" dimension. So I would get running totals as the below creates but for each company (ie Company A, Company B, Company C, etc.) in one table
CREATE TABLE test (tag int, Checks float, AVG_COST float, Check_total float, Check_amount float, Amount_total float, RunningTotal_Check float,
RunningTotal_Amount float)
DECLARE #tag int,
#Checks float,
#AVG_COST float,
#check_total float,
#Check_amount float,
#amount_total float,
#RunningTotal_Check float ,
#RunningTotal_Check_PCT float,
#RunningTotal_Amount float
SET #RunningTotal_Check = 0
SET #RunningTotal_Check_PCT = 0
SET #RunningTotal_Amount = 0
DECLARE aa_cursor CURSOR fast_forward
FOR
SELECT tag, Checks, AVG_COST, check_total, check_amount, amount_total
FROM test_3
OPEN aa_cursor
FETCH NEXT FROM aa_cursor INTO #tag, #Checks, #AVG_COST, #check_total, #Check_amount, #amount_total
WHILE ##FETCH_STATUS = 0
BEGIN
SET #RunningTotal_CHeck = #RunningTotal_CHeck + #checks
set #RunningTotal_Amount = #RunningTotal_Amount + #Check_amount
INSERT test VALUES (#tag, #Checks, #AVG_COST, #check_total, #Check_amount, #amount_total, #RunningTotal_check, #RunningTotal_Amount )
FETCH NEXT FROM aa_cursor INTO #tag, #Checks, #AVG_COST, #check_total, #Check_amount, #amount_total
END
CLOSE aa_cursor
DEALLOCATE aa_cursor
SELECT *, RunningTotal_Check/Check_total as CHECK_RUN_PCT, round((RunningTotal_Check/Check_total *100),0) as CHECK_PCT_BIN, RunningTotal_Amount/Amount_total as Amount_RUN_PCT, round((RunningTotal_Amount/Amount_total * 100),0) as Amount_PCT_BIN
into test_4
FROM test ORDER BY tag
create clustered index IX_TESTsdsdds3 on test_4(tag)
DROP TABLE test
----------------------------------
I can the the running total for any 1 company but I would like to do it for multiple to produce something like the results below.
CLIENT COUNT Running Total
Company A 1 6.7%
Company A 2 20.0%
Company A 3 40.0%
Company A 4 66.7%
Company A 5 100.0%
Company B 1 3.6%
Company B 2 10.7%
Company B 3 21.4%
Company B 4 35.7%
Company B 5 53.6%
Company B 6 75.0%
Company B 7 100.0%
Company C 1 3.6%
Company C 2 10.7%
Company C 3 21.4%
Company C 4 35.7%
Company C 5 53.6%
Company C 6 75.0%
Company C 7 100.0%
This is finally simple to do in SQL Server 2012, where SUM and COUNT support OVER clauses that contain ORDER BY. Using Cris's #Checks table definition:
SELECT
CompanyID,
count(*) over (
partition by CompanyID
order by Cleared, ID
) as cnt,
str(100.0*sum(Amount) over (
partition by CompanyID
order by Cleared, ID
)/
sum(Amount) over (
partition by CompanyID
),5,1)+'%' as RunningTotalForThisCompany
FROM #Checks;
SQL Fiddle here.
I originally started posting the SQL Server 2012 equivalent (since you didn't mention what version you were using). Steve has done a great job of showing the simplicity of this calculation in the newest version of SQL Server, so I'll focus on a few methods that work on earlier versions of SQL Server (back to 2005).
I'm going to take some liberties with your schema, since I can't figure out what all these #test and #test_3 and #test_4 temporary tables are supposed to represent. How about:
USE tempdb;
GO
CREATE TABLE dbo.Checks
(
Client VARCHAR(32),
CheckDate DATETIME,
Amount DECIMAL(12,2)
);
INSERT dbo.Checks(Client, CheckDate, Amount)
SELECT 'Company A', '20120101', 50
UNION ALL SELECT 'Company A', '20120102', 75
UNION ALL SELECT 'Company A', '20120103', 120
UNION ALL SELECT 'Company A', '20120104', 40
UNION ALL SELECT 'Company B', '20120101', 75
UNION ALL SELECT 'Company B', '20120105', 200
UNION ALL SELECT 'Company B', '20120107', 90;
Expected output in this case:
Client Count Running Total
--------- ----- -------------
Company A 1 17.54
Company A 2 43.86
Company A 3 85.96
Company A 4 100.00
Company B 1 20.55
Company B 2 75.34
Company B 3 100.00
One way:
;WITH gt(Client, Totals) AS
(
SELECT Client, SUM(Amount)
FROM dbo.Checks AS c
GROUP BY Client
), n (Client, Amount, rn) AS
(
SELECT c.Client, c.Amount,
ROW_NUMBER() OVER (PARTITION BY c.Client ORDER BY c.CheckDate)
FROM dbo.Checks AS c
)
SELECT n.Client, [Count] = n.rn,
[Running Total] = CONVERT(DECIMAL(5,2), 100.0*(
SELECT SUM(Amount) FROM n AS n2
WHERE Client = n.Client AND rn <= n.rn)/gt.Totals
)
FROM n INNER JOIN gt ON n.Client = gt.Client
ORDER BY n.Client, n.rn;
A slightly faster alternative - more reads but shorter duration and simpler plan:
;WITH x(Client, CheckDate, rn, rt, gt) AS
(
SELECT Client, CheckDate, rn = ROW_NUMBER() OVER
(PARTITION BY Client ORDER BY CheckDate),
(SELECT SUM(Amount) FROM dbo.Checks WHERE Client = c.Client
AND CheckDate <= c.CheckDate),
(SELECT SUM(Amount) FROM dbo.Checks WHERE Client = c.Client)
FROM dbo.Checks AS c
)
SELECT Client, [Count] = rn,
[Running Total] = CONVERT(DECIMAL(5,2), rt * 100.0/gt)
FROM x
ORDER BY Client, [Count];
While I've offered set-based alternatives here, in my experience I have observed that a cursor is often the fastest supported way to perform running totals. There are other methods such as the quirky update which perform about marginally faster but the result is not guaranteed. The set-based approach where you perform a self-join becomes more and more expensive as the source row counts go up - so what seems to perform okay in testing with a small table, as the table gets larger, the performance goes down.
I have a blog post almost fully prepared that goes through a slightly simpler performance comparison of various running totals approaches. It is simpler because it is not grouped and it only shows the totals, not the running total percentage. I hope to publish this post soon and will try to remember to update this space.
There is also another alternative to consider that doesn't require reading previous rows multiple times. It's a concept Hugo Kornelis describes as "set-based iteration." I don't recall where I first learned this technique, but it makes a lot of sense in some scenarios.
DECLARE #c TABLE
(
Client VARCHAR(32),
CheckDate DATETIME,
Amount DECIMAL(12,2),
rn INT,
rt DECIMAL(15,2)
);
INSERT #c SELECT Client, CheckDate, Amount,
ROW_NUMBER() OVER (PARTITION BY Client
ORDER BY CheckDate), 0
FROM dbo.Checks;
DECLARE #i INT, #m INT;
SELECT #i = 2, #m = MAX(rn) FROM #c;
UPDATE #c SET rt = Amount WHERE rn = 1;
WHILE #i <= #m
BEGIN
UPDATE c SET c.rt = c2.rt + c.Amount
FROM #c AS c
INNER JOIN #c AS c2
ON c.rn = c2.rn + 1
AND c.Client = c2.Client
WHERE c.rn = #i;
SET #i = #i + 1;
END
SELECT Client, [Count] = rn, [Running Total] = CONVERT(
DECIMAL(5,2), rt*100.0 / (SELECT TOP 1 rt FROM #c
WHERE Client = c.Client ORDER BY rn DESC)) FROM #c AS c;
While this does perform a loop, and everyone tells you that loops and cursors are bad, one gain with this method is that once the previous row's running total has been calculated, we only have to look at the previous row instead of summing all prior rows. The other gain is that in most cursor-based solutions you have to go through each client and then each check. In this case, you go through all clients' 1st checks once, then all clients' 2nd checks once. So instead of (client count * avg check count) iterations, we only do (max check count) iterations. This solution doesn't make much sense for the simple running totals example, but for the grouped running totals example it should be tested against the set-based solutions above. Not a chance it will beat Steve's approach, though, if you are on SQL Server 2012.
UPDATE
I've blogged about various running totals approaches here:
http://www.sqlperformance.com/2012/07/t-sql-queries/running-totals
I didn't exactly understand the schema you were pulling from, but here is a quick query using a temp table that shows how to do a running total in a set based operation.
CREATE TABLE #Checks
(
ID int IDENTITY(1,1) PRIMARY KEY
,CompanyID int NOT NULL
,Amount float NOT NULL
,Cleared datetime NOT NULL
)
INSERT INTO #Checks
VALUES
(1,5,'4/1/12')
,(1,5,'4/2/12')
,(1,7,'4/5/12')
,(2,10,'4/3/12')
SELECT Info.ID, Info.CompanyID, Info.Amount, RunningTotal.Total, Info.Cleared
FROM
(
SELECT main.ID, SUM(other.Amount) as Total
FROM
#Checks main
JOIN
#Checks other
ON
main.CompanyID = other.CompanyID
AND
main.Cleared >= other.Cleared
GROUP BY
main.ID) RunningTotal
JOIN
#Checks Info
ON
RunningTotal.ID = Info.ID
DROP TABLE #Checks
I have a small issue with a stored procedure in SQL Server 2005.
RepInfoID(primary), Date, RepID are the columns in RepInfo
1. 25660, 02/03/2012, 100
2. 25661, 02/03/2012, 100
3. 25662, 02/03/2012, 101
and
RepCID(primary), RepInfoID, Amount are the columns in RepCollection
1. 4343, 25660, 200(money)
2. 4344, 25661, 600
Desired results is:
100(RepID) , 02/03/2012, 800(money)
There can be more than row with the same date.
When a date is selected, foreach RepID on the selected date, I want the sum of Amount on that particular day. I hope I am clear.
Thank you for your help..
CREATE PROCEDURE [dbo].[PracticewithAmount]
(#Date datetime)
AS
BEGIN
Select *
from RepInfo
left outer join RepCollection on RepInfo.RepInfoID = RepCollection.RepInfoID
where RepInfo.Date = #Date
and distinct(RepID)
END
Your question isn't entirely clear, but here's what I think you're saying:
SELECT
--i.RepInfoID(primary),
i.Date,
i.RepID,
--c.RepCID(primary),
--c.RepInfoID,
sum(c.Amount) as Amount
FROM
RepInfo i
LEFT JOIN RepCollection c
ON i.RepInfoID = c.RepInfoID
WHERE
i.Date = #Date
GROUP BY
i.Date,
i.RepID