Calculating Subtotals using rollup in SQL Server - sql-server

I have a table(#mytable) that contains basic financial info about companies.
CREATE TABLE #mytable
(
Companyid varchar2(50),
DataDescription varchar2(100),
Value DECIMAL(23,6),
Department varchar2(100),
CurrencyIS03 varchar2(5),
DateofData datetime
)
INSERT INTO #mytable (Companyid, DataDescription, Value, Department, CurrencyIS03, DateofData)
SELECT
'A100', 'Revenue', '1000.00', 'Corporate', 'USD', '2014-12-31 00:00:00'
UNION ALL
SELECT 'A100','Revenue','2000.00','Banking','USD','2014-12-31 00:00:00'
UNION ALL
SELECT 'A100','Revenue','2500.00','Corporate','USD','2013-12-31 00:00:00'
UNION ALL
SELECT 'A100','Revenue','3000.00','Banking','USD','2013-12-31 00:00:00'
UNION ALL
SELECT 'A100','Operating Income','10000.00','Corporate','USD','2014-12-31 00:00:00'
UNION ALL
SELECT 'A100','Operating Income','1000.00','Banking','USD','2014-12-31 00:00:00'
UNION ALL
SELECT 'A200','Revenue','1100.00','Corporate','USD','2013-12-31 00:00:00'
UNION ALL
SELECT 'A200','Revenue','3000.00','Banking','USD','2013-12-31 00:00:00'
UNION ALL
SELECT 'A200','Operating Income','5500.00','Corporate','USD','2014-12-31 00:00:00'
UNION ALL
SELECT 'A200','Operating Income','10000.00','Banking','USD','2014-12-31 00:00:00'
I have to find sub totals based on Companyid, DataDescription, Department, CurrencyIS03, DateofData. I am not sure how to do that. I tried doing the following
select
Companyid, DataDescription,
sum(Value) as total,
Department, CurrencyIS03, DateofData
from
#mytable
group by
rollup(CompanyID, Datadescription, Department, CurrencyIS03, DateofData)
This is not returning the correct answer.
Below is what I expect.
CREATE TABLE #outputtable
(
Companyid varchar2(50),
DataDescription varchar2(100),
TotalValue DECIMAL(23,6),
Department varchar2(100),
CurrencyIS03 varchar2(5),
DateofData datetime
)
INSERT INTO #outputtable (Companyid, DataDescription, TotalValue, Department, CurrencyIS03, DateofData)
SELECT 'A100','Revenue','1000.00','Corporate','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','2000.00','Banking','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','3000.00','Total','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','2500.00','Corporate','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','3000.00','Banking','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A100','Revenue','5500.00','Total','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A100','Operating Income','10000.00','Corporate','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Operating Income','1000.00','Banking','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A100','Operating Income','11000.00','Total','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A200','Revenue','1100.00','Corporate','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A200','Revenue','3000.00','Banking','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A200','Revenue','4100.00','Total','USD','2013-12-31 00:00:00' UNION ALL
SELECT 'A200','Operating Income','5500.00','Corporate','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A200','Operating Income','10000.00','Banking','USD','2014-12-31 00:00:00' UNION ALL
SELECT 'A200','Operating Income','15500.00','Total','USD','2014-12-31 00:00:00'
Any help is appreciated.
Thanks

It looks like you're only rolling up Department within the grouping so you just have use ROLLUP(Department) and group by the rest normally.
SELECT Companyid,
DataDescription,
SUM(Value) AS total,
COALESCE(Department,'Total') Department,
CurrencyIS03,
DateofData
FROM #mytable
GROUP BY CompanyID,
Datadescription,
ROLLUP(Department),
CurrencyIS03,
DateofData
ORDER BY CompanyID ASC,
Datadescription DESC,
DateofData DESC,
Department ASC

I'm not sure why you would want to use ROLLUP for this.
I would just do:
select * from
(select Companyid,DataDescription,sum(Value) as Value ,'Total' as Department,CurrencyIS03,DateofData
from mytable
group by CompanyID, Datadescription, CurrencyIS03,DateofData
union all
select * from mytable) a
order by CompanyID asc, Datadescription desc, DateofData desc, Department asc
There is a fiddle

Related

Use min() when row_number() is descending

I have #Tickets table with 2 open tickets for JFK.
declare #Tickets table
(
Airport varchar(10),
TicketNum varchar(10),
Created date,
Modified date,
LastModified date,
Modified_By varchar(10),
TicketStatus varchar(10),
AssignedTo varchar(10)
)
insert into #Tickets
select 'JFK', '001', '9/25/2021', '9/26/2021', '9/29/2021', 'Jimmy', 'Open', 'Ralph' union
select 'JFK', '002', '9/28/2021', '9/28/2021', '9/30/2021', 'Mary', 'Open', 'Andrew'
select Airport, lastmodified, assignedto, Modified_By
from
(
select airport, lastmodified, assignedto, Modified_By,
row_number() over(partition by airport order by lastmodified desc) rn
from #Tickets
) src
where rn = 1
The following returns the last modified date of JFK tickets (9/30/2021), the last person that modified any of the JFK tickets (Mary) and the owner of the ticket that Mary last modified (Andrew).
Airport lastmodified assignedto Modified_By
JFK 2021-09-30 Andrew Mary
What I can't figure out is how to show min(created) to show when the first ticket was created.
The complete result should be
Airport First_Created lastmodified assignedto Modified_By
JFK 2021-09-25 2021-09-30 Andrew Mary
How can I plug in a min(create_date) as 'First_Created' in the query above?
I'm sure I can have two cte like below and then a join, but I prefer not using joins unless there are no other options:
declare #Tickets table
(
Airport varchar(10),
TicketNum varchar(10),
Created date,
Modified date,
LastModified date,
Modified_By varchar(10),
TicketStatus varchar(10),
AssignedTo varchar(10)
)
insert into #Tickets
select 'JFK', '001', '9/25/2021', '9/26/2021', '9/29/2021', 'Jimmy', 'Open', 'Ralph' union
select 'EWR', '001', '9/25/2021', '9/26/2021', '9/29/2021', 'Jimmy', 'Open', 'Ralph' union
select 'STI', '001', '9/25/2021', '9/26/2021', '9/29/2021', 'Jimmy', 'Open', 'Ralph' union
select 'JFK', '002', '9/28/2021', '9/28/2021', '9/30/2021', 'Mary', 'Open', 'Andrew'
;with cte as
(
select Airport, lastmodified, assignedto, Modified_By
from
(
select airport, lastmodified, assignedto, Modified_By,
row_number() over(partition by airport order by lastmodified desc) rn
from #Tickets
) src
where rn = 1
), cte2 as
(
select airport, min(created) as 'created' from #Tickets group by airport
)
select cte.Airport, lastmodified, assignedto, Modified_By, created
from cte inner join cte2 on
cte.Airport = cte2.Airport
You could try something like this. The query selects the latest modified row (partitioned by Airport) and CROSS APPLY's the earliest Created date.
with last_mod_cte as (
select top 1 with ties *
from #tickets
order by row_number() over(partition by airport order by lastmodified desc))
select lm.Airport, min_created.dt First_Created, lm.LastModified, lm.AssignedTo, lm.Modified_By
from last_mod_cte lm
cross apply (select min(Created)
from #Tickets t
where lm.Airport=t.Airport) min_created(dt);

Sum monthly amount and output for last day of month

I have table like example 1
here is summed amount by day. i need sum amount by month and join that value for last day of month, pls see image example 2
Thanks
Try this:
SELECT t.amount, t.dt, CASE WHEN month_cnt = rn THEN s ELSE NULL END AS month_s FROM (
select your_table.*,
sum(amount) over(partition by year(dt), month(dt) ) s,
count(*) over(partition by year(dt), month(dt)) month_cnt,
ROW_NUMBER() over(partition by year(dt), month(dt) order by dt) rn
from your_table
)t
order by dt
How about something like this?
create table #test
(
amount int,
trans date
)
insert into #test
SELECT
30, '2017-02-15'
UNION
SELECT
20, '2017-02-18'
UNION
SELECT
25, '2017-02-25'
UNION
SELECT
10, '2017-03-22'
UNION
SELECT
80, '2017-03-23'
UNION
SELECT
54, '2017-04-11'
SELECT
DATEPART(month, trans) month, SUM(amount) Sum
FROM
#test
GROUP BY
DATEPART(month, trans)

Repeat the first date withing a group

I Would like the first date of each group to repeat for the rest of the rows withing each group
You could use window expressions and grouping;
FIRST_VALUE (Transact-SQL)
You would need to partition by your first column. to get the split of A and B.
For example;
with cteTempData
(
[Code]
, [Date]
)
as
(
select 'A',cast('2015-9-4' as date)
union all select 'A','2015-9-4'
union all select 'A','2015-9-4'
union all select 'A','2015-9-16'
union all select 'B','2015-9-16'
union all select 'B','2015-9-22'
union all select 'B','2015-9-22'
union all select 'B','2015-10-26'
union all select 'B','2015-10-30'
)
select
[Code]
, [Date]
, FIRST_VALUE([Date]) over (partition by [Code] order by [Date]) as [First_Date]
from cteTempData
Using the first_value syntax also allows you to work with other columns in that ordered record....
with cteTempData
(
[Code]
, [Date]
, [Comment]
)
as
(
select 'A',cast('2015-9-4' as date),'One'
union all select 'A','2015-9-4','Two'
union all select 'A','2015-9-4','Three'
union all select 'A','2015-9-16','Four'
union all select 'B','2015-9-16','Five'
union all select 'B','2015-9-22','Six'
union all select 'B','2015-9-22','Seven'
union all select 'B','2015-10-26','Eight'
union all select 'B','2015-10-30','Nine'
)
select
[Code]
, [Date]
, FIRST_VALUE([Date]) over (partition by [Code] order by [Date]) as [First_Date]
, FIRST_VALUE([Comment]) over (partition by [Code] order by [Date]) as [First_Comment]
from cteTempData
Use MIN() Over ()
Declare #Table table (Grp varchar(25),Date date)
Insert into #Table values
('A','2015-09-04'),
('A','2015-09-05'),
('A','2015-09-10'),
('B','2015-10-04'),
('B','2015-10-05'),
('B','2015-10-10')
Select *
,GrpDate = min(Date) over (Partition By Grp)
From #Table
Returns
Grp Date GrpDate
A 2015-09-04 2015-09-04
A 2015-09-05 2015-09-04
A 2015-09-10 2015-09-04
B 2015-10-04 2015-10-04
B 2015-10-05 2015-10-04
B 2015-10-10 2015-10-04
You could use MIN with the OVER-clause
SELECT t.ColumnA,
DateCol = MIN( t.DateCol ) OVER ( PARTITION BY t.ColumnA ),
OtherColumns
FROM dbo.TableName t
you can go with a CROSS JOIN or FIRST_VALUE.
Declare #Yourtable table (groupCol varchar(25),firstDate date)
Insert into #Yourtable values
('A','2015-09-04'),
('A','2015-09-05'),
('A','2015-09-10'),
('B','2015-10-04'),
('B','2015-10-05'),
('B','2015-10-10')
SELECT a.*,b.firstDate
FROM #Yourtable a
CROSS JOIN (SELECT groupCol,MIN(firstDate) firstDate
FROM #Yourtable b
GROUP BY groupCol)b
WHERE a.groupCol =b.groupCol
OR
SELECT a.*,FIRST_VALUE(a.firstDate) OVER (PARTITION BY groupCol ORDER BY groupCol ASC) AS firstDate
FROM #Yourtable a

Counting the records of two columns from different tables

So I have this:
select count(InvoiceUniqueness) as [Nbr], CONVERT(varchar(12), ProcessDate, 101) as [Date]
from PreProcessTransLog
where (CONVERT(DATETIME, ProcessDate, 102)) >= dateadd (m,-24,getdate()) and CONVERT(DATETIME, ProcessDate, 120) <= GETDATE()
and InitialStatus =2
group by CONVERT(varchar(12), ProcessDate, 101)
order by CAST (CONVERT(varchar(12), ProcessDate, 101) as smalldatetime) desc
I want to count the records under the InvoiceUniqueness column from the PreProcessTransLog table and count the records under (column not in the code) ManualBillHeaderID from another table called Ex_Manual_Bill_Header. I want to display both count results in a new column called Nbr. So in simpler terms, lets say I count column1 from T1 and column2 from T2. column1 has 10 records and column2 has 5. I want to join both columns into one so I can display 15 records. Keep in mind the columns have different names. hope im explaining myself
Try something like this:
SELECT
COALESCE(t1.[Date], t2.[Date]) AS [Date],
ISNULL(t1.Nbr, 0), + ISNULL(t2.Nbr, 0) AS Total
FROM
(
SELECT COUNT(InvoiceUniqueness) AS [Nbr], CONVERT(VARCHAR(12), ProcessDate, 101) AS [Date]
FROM PreProcessTransLog
WHERE (CONVERT(DATETIME, ProcessDate, 102))>=DATEADD(m, -24, GETDATE()) AND CONVERT(DATETIME, ProcessDate, 120)<=GETDATE() AND InitialStatus=2
GROUP BY CONVERT(VARCHAR(12), ProcessDate, 101)
--ORDER BY [Date]
) t1,
FULL JOIN (
SELECT COUNT(ManualBillHeaderID ) AS [Nbr], CONVERT(VARCHAR(12), ProcessDate, 101) AS [Date]
FROM <whatever table>
WHERE (CONVERT(DATETIME, ProcessDate, 102))>=DATEADD(m, -24, GETDATE()) AND CONVERT(DATETIME, ProcessDate, 120)<=GETDATE() AND InitialStatus=2
GROUP BY CONVERT(VARCHAR(12), ProcessDate, 101)
--ORDER BY [Date]
) t2 ON t1.[Date] = t2.[Date]
ORDER BY
COALESCE(t1.[Date], t2.[Date])
If I'm understanding your request accurately, I would do something like this.
Do your counts, off each table, and merge the results with a UNION ALL, wrap that up in a sub-query, and SUM() the resulting counts.
declare #table1 table (id int identity(1,1), recorddate datetime)
declare #table2 table (id int identity(1,1), otherdate datetime)
insert into #table1
select '2014-08-01' union all
select '2014-08-01'
insert into #table2
select '2014-08-01' union all
select '2014-08-02'
select SUM(num), recorddate
from (select COUNT(1) num, recorddate
from #table1
group by recorddate
union all
select COUNT(1) num, otherdate
from #table2
group by otherdate ) x
group by recorddate
In the case you want the results separate, change the last query to this:
select COUNT(*) num, recorddate
from #table1
group by recorddate
union all
select COUNT(*) num, otherdate
from #table2
group by otherdate
Try using a UNION ALL. For example:
SELECT COUNT(Column1) AS [Nbr]
FROM Table1
GROUP BY ColumnToGroupOn1
UNION ALL
SELECT COUNT(Column2)
FROM Table2
GROUP BY ColumnToGroupOn2
Or if you want to include the date column/s:
SELECT COUNT(Column1) AS [Nbr], ColumnToGroupOn1 AS [Date]
FROM Table1
GROUP BY ColumnToGroupOn1
UNION ALL
SELECT COUNT(Column2), ColumnToGroupOn2
FROM Table2
GROUP BY ColumnToGroupOn2

How to query Open-high-low-close (OHLC) data from SQL Server

I'm trying to retrieve data for a Open-high-low-close (OHLC) chart directly from the database, it's the kind of chart you see of stocks. Is this possible, and if, how?
I have a table like this (simplified):
Date | Price | PriceType
A record is created for each day, I will report per month / year, not per day as used for stocks.
I would like to query something like this:
SELECT PriceType, MAX(Price) as High, MIN(Price) as Low, [Price of first item of month] as Open, [Price of last item of month] as Close GROUP BY PriceType, Year(Date), Month(Date)
To access the SQL Server I use LLBLGen, so an anwser based on that technology would be great, a generic SQL server will do too!
It's SQL 2005, but 2008 is also an option.
Thanks.
This appears to work. There may well be a less verbose way to do it.
--create test data
CREATE TABLE #t
(priceDate DATETIME
,price MONEY
,priceType CHAR(1)
)
INSERT #t
SELECT '20090101',100,'A'
UNION SELECT '20090102',500,'A'
UNION SELECT '20090103',20 ,'A'
UNION SELECT '20090104',25 ,'A'
UNION SELECT '20090105',28 ,'A'
UNION SELECT '20090131',150,'A'
UNION SELECT '20090201',501,'A'
UNION SELECT '20090203',21 ,'A'
UNION SELECT '20090204',26 ,'A'
UNION SELECT '20090205',29 ,'A'
UNION SELECT '20090228',151,'A'
UNION SELECT '20090101',100,'B'
UNION SELECT '20090102',500,'B'
UNION SELECT '20090103',20 ,'B'
UNION SELECT '20090104',25 ,'B'
UNION SELECT '20090105',28 ,'B'
UNION SELECT '20090131',150,'B'
UNION SELECT '20090201',501,'B'
UNION SELECT '20090203',21 ,'B'
UNION SELECT '20090204',26 ,'B'
UNION SELECT '20090205',29 ,'B'
UNION SELECT '20090228',151,'B'
--query
;WITH rangeCTE
AS
(
SELECT MIN(priceDate) minDate
,MAX(priceDate) maxDate
FROM #t
)
,datelistCTE
AS
(
SELECT CAST(CONVERT(CHAR(6),minDate,112) + '01' AS DATETIME) AS monthStart
,DATEADD(mm,1,CAST(CONVERT(CHAR(6),minDate,112) + '01' AS DATETIME)) -1 AS monthEnd
,1 AS monthID
FROM rangeCTE
UNION ALL
SELECT DATEADD(mm,1,monthStart)
,DATEADD(mm,2,monthStart) - 1
,monthID + 1
FROM datelistCTE
WHERE monthStart <= (SELECT maxDate FROM rangeCTE)
)
,priceOrderCTE
AS
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY monthID, priceType
ORDER BY priceDate
) AS rn1
,ROW_NUMBER() OVER (PARTITION BY monthID, priceType
ORDER BY priceDate DESC
) AS rn2
,ROW_NUMBER() OVER (PARTITION BY monthID, priceType
ORDER BY price DESC
) AS rn3
,ROW_NUMBER() OVER (PARTITION BY monthID, priceType
ORDER BY price
) AS rn4
FROM datelistCTE AS d
JOIN #t AS t
ON t.priceDate BETWEEN d.monthStart AND d.monthEnd
WHERE monthStart <= (SELECT maxDate FROM rangeCTE)
)
SELECT o.MonthStart
,o.priceType
,o.Price AS opening
,c.price AS closing
,h.price AS high
,l.price AS low
FROM priceOrderCTE AS o
JOIN priceOrderCTE AS c
ON c.priceType = o.PriceType
AND c.monthID = o.MonthID
JOIN priceOrderCTE AS h
ON h.priceType = o.PriceType
AND h.monthID = o.MonthID
JOIN priceOrderCTE AS l
ON l.priceType = o.PriceType
AND l.monthID = o.MonthID
WHERE o.rn1 = 1
AND c.rn2 = 1
AND h.rn3 = 1
AND l.rn4 = 1
This is a little query I wrote that seems to work nicely for one time span at a time. All you need to do is comment the select DATEPARTS in order to get to the timespan you are looking for. Or you could just make multiple views for different timespans. Also the underlying data table uses Bid Ask tick style data. If you are using mids or last prices you could eliminate the case statements from the selects.
Select
tmp.num,
rf.CurveName,
rf.Period as Period,
CASE WHEN (tmp2.Bid is null or tmp2.Ask is null) then isnull(tmp2.Bid,0)+isnull(tmp2.Ask,0) else (tmp2.Bid+tmp2.Ask)/2 end as [Open],
tmp.Hi,
tmp.Lo,
CASE WHEN (rf.Bid is null or Rf.Ask is null) then isnull(rf.Bid,0)+isnull(rf.Ask,0) else (rf.Bid+rf.Ask)/2 end as [Close],
tmp.OpenDate,
tmp.CloseDate,
tmp.yr,
tmp.mth,
tmp.wk,
tmp.dy,
tmp.hr
from BidAsk rf inner join
(SELECT count(CurveName)as num,CurveName,
Period,
max(CASE WHEN (Bid is null or Ask is null) then isnull(Bid,0)+isnull(Ask,0) else (Bid+Ask)/2 end) as Hi,
min(CASE WHEN (Bid is null or Ask is null) then isnull(Bid,0)+isnull(Ask,0) else (Bid+Ask)/2 end) as Lo,
max(CurveDateTime) as CloseDate, min(CurveDateTime) as OpenDate,
DATEPART(year, CurveDateTime) As yr,
DATEPART(month, CurveDateTime) As mth,
DATEPART(week, CurveDateTime) As wk,
DATEPART(Day, CurveDateTime) as dy,
DATEPART(Hour, CurveDateTime) as hr
--DATEPART(minute, CurveDateTime) as mnt
FROM
BidAsk
GROUP BY
CurveName,Period,
DATEPART(year, CurveDateTime),
DATEPART(month, CurveDateTime),
DATEPART(week, CurveDateTime),
DATEPART(Day, CurveDateTime) ,
DATEPART(Hour, CurveDateTime)
--DATEPART(minute, CurveDateTime)
) tmp on
tmp.CurveName=rf.CurveName and
tmp.CloseDate=rf.CurveDateTime and
tmp.Period=rf.Period
inner join BidAsk tmp2 on
tmp2.CurveName=rf.CurveName and
tmp2.CurveDateTime=tmp.Opendate and
tmp2.Period=rf.Period
ORDER BY
CurveName,Period,tmp.yr,tmp.mth
--DATEPART(year, CurveDateTime),
--DATEPART(month, CurveDateTime)
--DATEPART(day, CurveDateTime),
--DATEPART(Hour, CurveDateTime),
--DATEPART(minute, CurveDateTime) )

Resources