SQL: Cumulative of A.field since last value in B.field? - sql-server

Context - I'm very new to SQL. I get the basics, and have my head around basic joins, but I'm not quite at the point of building my own querys beyond basic selects.
I have the following table layout:
Site Date Cash_1 Sales_1
4 10/04/2017 £1,500 £1,500
4 09/04/2017 NULL £1,000
4 08/04/2017 NULL £2,000
4 07/04/2017 NULL £1,000
4 06/04/2017 £5,000 £1,000
As you can see, the (example) cumulative sales between 06/04 and 09/04 for this site total the cash amount for 06/04.
This happens when site 4 fails it's overnight upload of it's cash statement - the subsequent days get rolled together in a single statement, which is dated on the first date it failed to upload.
What I'm hoping to try and get is something like the below. The idea is that the cumulative column will show the total sales since the last successful cash statement import, which will help with balance checks.
Site Date Cash_1 Sales_1 Cumulative
4 10/04/2017 £1,500 £1,500 £1,500
4 09/04/2017 NULL £1,000 £5,000
4 08/04/2017 NULL £2,000 £4,000
4 07/04/2017 NULL £1,000 £2,000
4 06/04/2017 £5,000 £1,000 £1,000
I can get my head around using "SUM(Y) as X " to create a cumulative column in the standard sense, but I can't work out how to reset the accumulation based on another columns values.

Try with ROW_NUMBER() AND Update Statement As below:
DECLARE #tblTest as Table(
SiteNo INT,
[Date] Date,
Cash INT,
Sales INT
)
INSERT INTO #tblTest VALUES(4,'10-Apr-2017',1500,1500)
INSERT INTO #tblTest VALUES(4,'09-Apr-2017',NULL,1000)
INSERT INTO #tblTest VALUES(4,'08-Apr-2017',NULL,2000)
INSERT INTO #tblTest VALUES(4,'07-Apr-2017',NULL,1000)
INSERT INTO #tblTest VALUES(4,'06-Apr-2017',5000,1000)
;With T AS
(
SELECT
*,
NULL AS Cumulative,
ROW_NUMBER() OVER(ORDER BY Date) AS RowNo
FROM #tblTest
)
SELECT
*
INTO #tblTest
FROM T
ORDER BY RowNo
DECLARE #sum INT=0
Update #tblTest
SET #sum=Cumulative=Sales+ CASE WHEN Cash IS NULL THEN #sum ELSE 0 END
SELECT * FROM #tblTest ORDER BY RowNo DESC
DROP TABLE #tblTest

Related

SQL query with function

I have a table of data which i am using a count statement to get the amount of records for the submission date
example
AuditId Date Crew Shift Cast ObservedBy 2ndObserver AuditType Product
16 2017-06-27 3 Day B1974, B1975 Glen Mason NULL Identification Billet
20 2017-06-29 1 Day 9879 Corey Lundy NULL Identification Billet
21 2017-06-29 4 Day T9627, T9625 Joshua Dwyer NULL ShippingPad Tee
22 2017-06-29 4 Day NULL Joshua Dwyer NULL Identification Billet
23 2017-06-29 4 Day S9874 Joshua Dwyer NULL ShippingPad Slab
24 2017-06-29 4 Day Bay 40 Joshua Dwyer NULL Identification Billet
Basically I am using the following code to get my results
SELECT YEAR([Date]) as YEAR, CAST([Date] as nvarchar(25)) AS [Date], COUNT(*) as "Audit Count"
FROM AuditResults
where AuditType = 'Identification' AND Product = 'Billet'
group by Date
this returns example
YEAR Date Audit Count
2017 2017-06-27 1
2017 2017-06-29 3
Now I want to be able to retrieve all dates even if blank
so I would like the return to be
YEAR Date Audit Count
2017 2017-06-27 1
2017 2017-06-28 0
2017 2017-06-29 3
I have the following function I am trying to use:
ALTER FUNCTION [dbo].[fnGetDatesInRange]
(
#FromDate datetime,
#ToDate datetime
)
RETURNS #DateList TABLE (Dt date)
AS
BEGIN
DECLARE #TotalDays int, #DaysCount int
SET #TotalDays = DATEDIFF(dd,#FromDate,#ToDate)
SET #DaysCount = 0
WHILE #TotalDays >= #DaysCount
BEGIN
INSERT INTO #DateList
SELECT (#ToDate - #DaysCount) AS DAT
SET #DaysCount = #DaysCount + 1
END
RETURN
END
How do I use my select statement with this function? or is there a better way?
cheers
Try this;
ALTER FUNCTION [dbo].[fnGetDatesInRange]
(
#FromDate datetime,
#ToDate datetime
)
RETURNS #YourData TABLE ([Year] int, DateText nvarchar(25),[Audit Count] int)
AS
begin
insert into #YourData
SELECT
YEAR(allDates.[Date]) as YEAR,
CAST(allDates.[Date] as nvarchar(25)) AS [Date],
COUNT(r.Product) as "Audit Count"
from
(
SELECT
[date]=convert(datetime, CONVERT(float,d.Seq))
FROM
(
select top 100000 row_number() over(partition by 1 order by A.name) as Seq
from syscolumns A, syscolumns B
)d
)allDates
left join
AuditResults r on r.[Date]=allDates.[date] and r.AuditType = 'Identification' AND r.Product = 'Billet'
where
allDates.[Date]>=#FromDate and allDates.[Date]<=#ToDate
group by
allDates.[Date]
return
end
The key is the 'allDates' section ;
SELECT
[date]=convert(datetime, CONVERT(float,d.Seq))
FROM
(
select top 100000 row_number() over(partition by 1 order by A.name) as Seq
from syscolumns A, syscolumns B
)d
This will return all dates between 1900 and 2173 (in this example). Limit that as you need but a nice option. A ton of different ways to approach this clearly
you have to create another table calendar as (Mysql)- idea is the same on all RDBMS-
CREATE TABLE `calendar` (
`dt` DATE NOT NULL,
UNIQUE INDEX `calendar_dt_unique` (`dt`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
and fill with date data.
more details

Insert row by using loop with condition

I have tbl_emp like that:
empid
1
2
3
4
And tbl_att like that:
empid workingdate
1 2017-05-11
2 2017-05-13
3 2017-05-14
...........
...........
I have a job in SQL Server agent to execute step every Sunday and I want that job to insert a row for each empid with that day into tbl_att. Let's say Sunday is 2017-05-22 so I want it like that:
empid workingdate
1 2017-05-22
2 2017-05-22
3 2017-05-22
It means that I want it to insert into tbl_att for all empid with the same day (task execution day), so can anyone guide me a query that I need to put into my step command?
Try this,
INSERT INTO tbl_att SELECT empid,CAST(GETDATE() AS DATE) FROM tbl_emp;
insert into tbl_att (empid, workingdate)
Select empid,cast(getdate() as date) from tbl_emp
As you run the job on every Sunday, above query will insert the data as per your expectation I believe. FYI, But it depends on the system date
Hope it helps you
CREATE TABLE #tbl_att (empid INT,workingdate DATE)
CREATE TABLE #EmpidTab (empid INT)
INSERT INTO #EmpidTab
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4
SELECT * FROM #EmpidTab
INSERT INTO #EmpidTab
SELECT 5
INSERT INTO #tbl_att
SELECT Empid
,Getdate() AS workingdate
FROM #EmpidTab i
WHERE NOT EXISTS (
SELECT 1
FROM #tbl_att t
WHERE t.empid = i.Empid
) --Eliminate duplicte insertion of empid's
SELECT ##ROWCOUNT AS NoRowsInserted
SELECT *
FROM #tbl_att

Modifying the current row based on the previous row in sql server

I have a result set like this:
YearMonth Sales
201411 100
201412 100
201501 100
201502 100
201503 100
201504 100
201505 100
201506 100
201507 100
201508 100
Need to add another row with 4% more sales than the previous month. For example my Result should be
YearMonth Sales New Sales
201411 100 100.00
201412 100 104.00
201501 100 108.16
201502 100 112.49
201503 100 116.99
201504 100 121.67
201505 100 126.53
201506 100 131.59
201507 100 136.86
201508 100 142.33
Please help me to get the best way for it.
Got perfect answer for your requirement. It took long time to figure out. Just change the #Temp table name with your table name and verify the column names also.
DECLARE #nCurrentSale FLOAT
DECLARE #nYeatDate INT
DECLARE #nSale FLOAT
CREATE TABLE #TempNEW(YearMonth VARCHAR(10), Sales FLOAT, NewSale FLOAT)
SELECT TOP 1 #nCurrentSale = Sales FROM #Temp
ORDER BY (CAST('01/' + SUBSTRING (CAST(YearMonth AS VARCHAR), 5 , 2) + '/' + SUBSTRING (CAST(YearMonth AS
VARCHAR), 0 , 5) AS DATETIME)) ASC
DECLARE Cursor1 CURSOR FOR
SELECT YearMonth, Sales FROM #Temp
ORDER BY (CAST('01/' + SUBSTRING (CAST(YearMonth AS VARCHAR), 5 , 2) + '/' + SUBSTRING (CAST(YearMonth AS
VARCHAR), 0 , 5) AS DATETIME)) ASC
OPEN Cursor1
FETCH NEXT FROM Cursor1 INTO #nYeatDate, #nSale
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #TempNEW(YearMonth, Sales, NewSale) VALUES(#nYeatDate, #nSale, CAST(#nCurrentSale AS DECIMAL(12,2)))
SET #nCurrentSale = #nCurrentSale + ((#nCurrentSale/100) * 4)
FETCH NEXT FROM Cursor1 INTO #nYeatDate, #nSale
END
CLOSE Cursor1
DEALLOCATE Cursor1
SELECT * FROM #TempNEW
Notify me with your status.
Yes it possible. But first you have to alter the table and add the extra column NewSales then try with this link
https://dba.stackexchange.com/questions/34243/update-row-based-on-match-to-previous-row
i think you can done it through this link
Also sql server support some "Computed Columns in SQL Server with Persisted Values"
using that you can specify the formula what you want, then the new column value will automatically created according to your formula
Here's two thoughts... Not super clear if I understood the use case... Also this solution will only work for SQL 2012 and up
So given the table
CREATE TABLE [dbo].[LagExample](
[YearMonth] [nvarchar](100) NOT NULL,
[Sales] [money] NOT NULL
)
First one is fairly simple and just assumes you are wanting to base the magnitude of your percentage increase on how many days came before it...
;WITH cte
as
(
SELECT YearMonth,
ROW_NUMBER() OVER (ORDER BY YearMonth) - 1 AS SalesEntry,
cast(LAG(Sales, 1,Sales) OVER (ORDER BY YearMonth) as float) as Sales
FROM LagExample
)
SELECT YearMonth,
Sales,
cast(Sales * POWER(cast(1.04 as float), SalesEntry) AS decimal(10,2)) as NewSales
FROM cte
The Second one uses a recursive CTE to calculate the value as you move along the months..
Here's a good link about recursive CTEs
http://www.codeproject.com/Articles/683011/How-to-use-recursive-CTE-calls-in-T-SQL
;with data
as
(
SELECT Lead(le.YearMonth, 1, null) OVER (ORDER BY le.YearMonth) as NextYearMonth,
cast(le.Sales as Decimal(10,4)) as Sales,
le.YearMonth
FROM LagExample le
)
,cte
as
(
SELECT *
FROM data
Where YearMonth = '201411'
UNION ALL
SELECT
data.NextYearMonth,
cast(cte.Sales * 1.04 as Decimal(10,4)) as Sales,
data.YearMonth
From cte join
data on data.YearMonth = cte.NextYearMonth
)
SELECT YearMonth, cast(Sales as Decimal(10,2))
FROM cte
order by YearMonth

Checking next row in table is incremented by 1 minute in datetime column

I need to check alot of data in a Table to make sure my feed has not skipped anything.
Basically the table has the following columns
ID Datetime Price
The data in DateTime column is incremented by 1 minute in each successive row. I need to check the next row of the current one to see if is 1 minute above the one being queries in that specific context.
The query will probably need some sort of loop, then grab a copy of the next row and compare it to the datetime row of the current to make sure it is incremented by 1 minute.
I created a test-table to match your description, and inserted 100 rows with 1 minute between each row like this:
CREATE TABLE [Test] ([Id] int IDENTITY(1,1), [Date] datetime, [Price] int);
WITH [Tally] AS (
SELECT GETDATE() AS [Date]
UNION ALL
SELECT DATEADD(minute, -1, [Date]) FROM [Tally] WHERE [Date] > DATEADD(minute, -99, GETDATE())
)
INSERT INTO [Test] ([Date], [Price])
SELECT [Date], 123 AS [Price]
FROM [Tally]
Then i deleted a record in the middle to simulate a missing minute:
DELETE FROM [Test]
WHERE Id = 50
Now we can use this query to find missing records:
SELECT
a.*
,CASE WHEN b.[Id] IS NULL THEN 'Next record is Missing!' ELSE CAST(b.[Id] as varchar) END AS NextId
FROM
[Test] AS a
LEFT JOIN [Test] AS b ON a.[Date] = DATEADD(minute,1,b.[Date])
WHERE
b.[Id] IS NULL
The resullt will look like this:
Id Date Price NextId
----------- ----------------------- ----------- ------------------------------
49 2013-05-11 22:42:56.440 123 Next record is Missing!
100 2013-05-11 21:51:56.440 123 Next record is Missing!
(2 row(s) affected)
The key solution to the problem is to join the table with itself, but use datediff to find the record that is supposed to be found on the next minute. The last record of the table will of course report that the next row is missing, since it hasn't been inserted yet.
Borrowing TheQ's sample data you can use
WITH T
AS (SELECT *,
DATEDIFF(MINUTE, '20000101', [Date]) -
DENSE_RANK() OVER (ORDER BY [Date]) AS G
FROM Test)
SELECT MIN([Date]) AS StartIsland,
MAX([Date]) AS EndIsland
FROM T
GROUP BY G

Auto running number ID with format xxxx/year number (9999/12) in SQL Server stored procedure

I have one table (Stock_ID, Stock_Name). I want to write a stored procedure in SQL Server with Stock_ID running number with a format like xxxx/12 (xxxx = number start from 0001 to 9999; 12 is the last 2 digits of current year).
My scenario is that if the year change, the running number will be reset to 0001/13.
what do you intend to do when you hit more than 9999 in a single year??? it may sound impossible, but I've had to deal with so many "it will never happen" data related design mess-ups over the years from code first design later developers. These are major pains depending on how may places you need to fix these items which are usually primary key and foreign keys used all over.
This looks like a system requirement to SHOW the data this way, but it is the developers responsibility to design the internals of the application. The way you store it and display it don't need to be identical. I'd split that into two columns, using an int for the number portion and a tiny int for the 2 digit year portion. You can use a computed column for quick and easy display (persist it and index if necessary), where you pad with leading zeros and add the slash. Throw in a check constraint on the year portion to make sure it stays within a reasonable range. You can make the number portion an identity and just have a job reseed it back to 1 every new years eve.
try it out:
--drop table YourTable
--create the basic table
CREATE TABLE YourTable
(YourNumber int identity(1,1) not null
,YourYear tinyint not null
,YourData varchar(10)
,CHECK (YourYear>=12 and YourYear<=25) --optional check constraint
)
--add the persisted computed column
ALTER TABLE YourTable ADD YourFormattedNumber AS ISNULL(RIGHT('0000'+CONVERT(varchar(10),YourNumber),4)+'/'+RIGHT(CONVERT(varchar(10),YourYear),2),'/') PERSISTED
--make the persisted computed column the primary key
ALTER TABLE YourTable ADD CONSTRAINT PK_YourTable PRIMARY KEY CLUSTERED (YourFormattedNumber)
sample data:
--insert rows in 2012
insert into YourTable values (12,'aaaa')
insert into YourTable values (12,'bbbb')
insert into YourTable values (12,'cccc')
--new years eve job run this
DBCC CHECKIDENT (YourTable, RESEED, 0)
--insert rows in 2013
insert into YourTable values (13,'aaaa')
insert into YourTable values (13,'bbbb')
select * from YourTable order by YourYear,YourNumber
OUTPUT:
YourNumber YourYear YourData YourFormattedNumber
----------- -------- ---------- -------------------
1 12 aaaa 0001/12
2 12 bbbb 0002/12
3 12 cccc 0003/12
1 13 aaaa 0001/13
2 13 bbbb 0002/13
(5 row(s) affected)
to handle the possibility of more than 9999 rows per year try a different computed column calculation:
CREATE TABLE YourTable
(YourNumber int identity(9998,1) not null --<<<notice the identity starting point, so it hits 9999 quicker for this simple test
,YourYear tinyint not null
,YourData varchar(10)
)
--handles more than 9999 values per year
ALTER TABLE YourTable ADD YourFormattedNumber AS ISNULL(RIGHT(REPLICATE('0',CASE WHEN LEN(CONVERT(varchar(10),YourNumber))<4 THEN 4 ELSE 1 END)+CONVERT(varchar(10),YourNumber),CASE WHEN LEN(CONVERT(varchar(10),YourNumber))<4 THEN 4 ELSE LEN(CONVERT(varchar(10),YourNumber)) END)+'/'+RIGHT(CONVERT(varchar(10),YourYear),2),'/') PERSISTED
ALTER TABLE YourTable ADD CONSTRAINT PK_YourTable PRIMARY KEY CLUSTERED (YourFormattedNumber)
sample data:
insert into YourTable values (12,'aaaa')
insert into YourTable values (12,'bbbb')
insert into YourTable values (12,'cccc')
DBCC CHECKIDENT (YourTable, RESEED, 0) --new years eve job run this
insert into YourTable values (13,'aaaa')
insert into YourTable values (13,'bbbb')
select * from YourTable order by YourYear,YourNumber
OUTPUT:
YourNumber YourYear YourData YourFormattedNumber
----------- -------- ---------- --------------------
9998 12 aaaa 9998/12
9999 12 bbbb 9999/12
10000 12 cccc 10000/12
1 13 aaaa 0001/13
2 13 bbbb 0002/13
(5 row(s) affected)
This might help:
DECLARE #tbl TABLE(Stock_ID INT,Stock_Name VARCHAR(100))
INSERT INTO #tbl
SELECT 1,'Test'
UNION ALL
SELECT 2,'Test2'
DECLARE #ShortDate VARCHAR(2)=RIGHT(CAST(YEAR(GETDATE()) AS VARCHAR(4)),2)
;WITH CTE AS
(
SELECT
CAST(ROW_NUMBER() OVER(ORDER BY tbl.Stock_ID) AS VARCHAR(4)) AS RowNbr,
tbl.Stock_ID,
tbl.Stock_Name
FROM
#tbl AS tbl
)
SELECT
REPLICATE('0', 4-LEN(RowNbr))+CTE.RowNbr+'/'+#ShortDate AS YourColumn,
CTE.Stock_ID,
CTE.Stock_Name
FROM
CTE
From memory, this is a way to get the next id:
declare #maxid int
select #maxid = 0
-- if it does not have #maxid will be 0, if it was it will give the next id
select #maxid = max(convert(int, substring(Stock_Id, 1, 4))) + 1
from table
where substring(Stock_Id, 6, 2) = substring(YEAR(getdate()), 3, 2)
declare #nextid varchar(7)
select #nextid = right('0000'+ convert(varchar,#maxid),4)) + '/' + substring(YEAR(getdate()), 3, 2)

Resources