Group By Data at 15 minutes of interval with 1 minute data - sql-server

I am using SQL Server 2014.
I am having following table which contains data for stocks minute wise.
CREATE TABLE [dbo].[HISTORICALDATA]
(
[ID] [INT] IDENTITY(1,1) NOT NULL,
[SYMBOL] [NVARCHAR](50) NULL,
[DATETIME] [DATETIME] NULL,
[O] [DECIMAL](18, 2) NULL,
[H] [DECIMAL](18, 2) NULL,
[L] [DECIMAL](18, 2) NULL,
[C] [DECIMAL](18, 2) NULL,
[V] [INT] NULL,
CONSTRAINT [PK_HISTORICALDATA]
PRIMARY KEY CLUSTERED ([ID] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Tables contains data as following sample shown
SYMBOL DATETIME O H L C V
-----------------------------------------------------------------
ACC 2019-01-01 09:16:00.000 1512.30 1512.30 1507.00 1507.00 7398
ACC 2019-01-01 09:17:00.000 1506.35 1507.80 1503.00 1504.05 8120
ACC 2019-01-01 09:18:00.000 1504.25 1506.00 1504.00 1505.50 3487
ACC 2019-01-01 09:19:00.000 1505.00 1505.00 1503.10 1504.00 3138
ACC 2019-01-01 09:20:00.000 1504.95 1504.95 1501.20 1501.20 4567
ACC 2019-01-01 09:21:00.000 1500.30 1500.95 1498.00 1498.00 8522
ACC 2019-01-01 09:22:00.000 1498.00 1500.90 1498.00 1500.05 4462
ACC 2019-01-01 09:23:00.000 1500.85 1501.40 1500.00 1500.95 2006
ACC 2019-01-01 09:24:00.000 1500.85 1500.95 1500.00 1500.05 1400
ACC 2019-01-01 09:25:00.000 1500.90 1500.95 1499.00 1500.00 3484
ACC 2019-01-01 09:26:00.000 1499.20 1500.00 1499.00 1499.00 1960
ACC 2019-01-01 09:27:00.000 1499.60 1499.95 1498.00 1498.90 1903
ACC 2019-01-01 09:28:00.000 1498.50 1498.50 1496.40 1498.00 3060
ACC 2019-01-01 09:29:00.000 1497.05 1499.10 1496.75 1499.10 3522
ACC 2019-01-01 09:30:00.000 1499.35 1499.50 1498.35 1498.35 741
I tried with following query but it gave me wrong results
SELECT
SYMBOL,
DATEADD(MINUTE, 1, MAX(DATETIME)),
MAX(H),
MIN(L),
SUM(V)
FROM
HISTORICALDATA
WHERE
SYMBOL = 'ACC' AND MONTH(DATETIME) = 1
GROUP BY
SYMBOL,
DATEDIFF(MINUTE, 0 ,DATETIME) / 15
ORDER BY
DATEDIFF(MINUTE, 0, DATETIME) / 15
I need out with each 15 minute interval with
SYMBOL, DATETIME, OPENOF15MIN, HIGHOF15MIN, LOWOF15MIN, CLOSEOF15MIN
If someone can help.
Thanks

Perhaps this can help.
Example dbFiddle
Select [SYMBOL]
,DTR1 = min(case when RNO=1 then [DateTime] end)
,DTR2 = min(case when RNC=1 then [DateTime] end)
,O = min(case when RNO=1 then O end)
,C = min(case when RNC=1 then C end)
,H = max(H)
,L = min(L)
,V = sum(V)
From (
Select *
,Grp = Dense_Rank() over (Partition By Symbol Order By tMin )
,RNO = Row_Number() over (Partition By Symbol,tMin Order By [DateTime])
,RNC = Row_Number() over (Partition By Symbol,tMin Order By [DateTime] Desc)
From YourTable A1
Cross Apply ( values ( (DateDiff(MINUTE,0,[DATETIME]) - 1) / 15 ) ) A2(tMin)
) A
Group By Symbol,Grp
Returns
SYMBOL DTR1 DTR2 O C H L V
ACC 2019-01-01 09:16:00 2019-01-01 09:30:00 1512.30 1498.35 1512.30 1496.40 57770.00

Related

Check in and check out time in SQL

simple data of table
My table is:
SELECT TOP (1000)
[ID]
,[UserName]
,[CheckTime]
,[Checktype]
,[CheckinLocation]
,[lat]
,[lng]
FROM
[dbo].[CheckTime]
INSERT INTO [dbo].[CheckTime] ([UserName], [CheckTime], [Checktype],[CheckinLocation], [lat], [lng])
VALUES (<UserName, nchar(10),>
,<CheckTime, datetime,>
,<Checktype, nvarchar(50),>
,<CheckinLocation, nvarchar(50),>
,<lat, float,>
,<lng, float,>)
GO
Create table script:
CREATE TABLE [dbo].[CheckTime]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[UserName] [nchar](10) NULL,
[CheckTime] [datetime] NULL,
[Checktype] [nvarchar](50) NULL,
[CheckinLocation] [nvarchar](50) NULL,
[lat] [float] NULL,
[lng] [float] NULL,
CONSTRAINT [PK_CheckTime]
PRIMARY KEY CLUSTERED ([ID] ASC)
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
I need to select each distinct home holding the maximum value of datetime.
max CheckTime as check out
min CheckTime as check in
I need a result like this:
id | Username | check in | check out
---+----------+-------------------+-------------------
1 | 10 | 2017-1-2 08:02:05 | 2017-1-2 10:02:05
1 | 12 | 2017-1-2 08:02:05 | 2017-1-2 10:02:05
1 | 12 | 2017-1-3 08:02:05 | 2017-1-3 10:02:05
1 | 10 | 2017-1-3 08:02:05 | 2017-1-3 10:02:05
I have tried:
You can try the following query.
Select Username
, Cast(CheckTime as Date) as CheckDate
, min(CheckTime) as [check in]
, max(CheckTime) as check out
From CheckInTable
Group by id, Username, Cast(CheckTime as Date)

How to get the year wise employee as per the promotion

How to get the year wise employee as per the promotion.
Example:
Employee Number 'A-001' Join on '01-07-2013' then the O/P will be showing in image.
As Employee Number 'A-001' there is no promotion in 2014 then in Last_Designation,Promoted_Designation, Last_Gross and Promoted_Gross
need to be same as 2013 or previous one.
Below is my SQL Query with Data.
CREATE TABLE [dbo].[Employee](
[Emp_No] [numeric](18, 0) NULL,
[Emp_Number] [nvarchar](50) NULL,
[Emp_Name] [nvarchar](50) NULL,
[Emp_JoiningDate] [date] NULL,
[Emp_ResignDate] [date] NULL,
[Emp_Status] [nvarchar](50) NULL,
[Emp_CurrentDesignation] [nvarchar](50) NULL,
[Emp_CurrentGross] [numeric](18, 0) NULL
) ON [PRIMARY]
GO
INSERT INTO [UserDB].[dbo].[Employee]
([Emp_No]
,[Emp_Number]
,[Emp_Name]
,[Emp_JoiningDate]
,[Emp_ResignDate]
,[Emp_Status]
,[Emp_CurrentDesignation]
,[Emp_CurrentGross])
VALUES
(1,'A-001','Alex','2012-07-01',null,'On Board','Trainee3',2000)
GO
INSERT INTO [UserDB].[dbo].[Employee]
([Emp_No]
,[Emp_Number]
,[Emp_Name]
,[Emp_JoiningDate]
,[Emp_ResignDate]
,[Emp_Status]
,[Emp_CurrentDesignation]
,[Emp_CurrentGross])
VALUES
(2,'A-002','Smith','2014-07-01','2015-07-01','Resigned','HR1',1500)
GO
CREATE TABLE [dbo].[Promotion](
[Prom_No] [numeric](18, 0) NULL,
[Prom_EmpNo] [numeric](18, 0) NULL,
[Last_Designation] [nvarchar](500) NULL,
[Promoted_Designation] [nvarchar](500) NULL,
[WEF_Date] [date] NULL,
[Promoted_Gross] [numeric](18, 0) NULL,
[Last_Gross] [numeric](18, 0) NULL
) ON [PRIMARY]
GO
INSERT INTO [UserDB].[dbo].[Promotion]
([Prom_No]
,[Prom_EmpNo]
,[Last_Designation]
,[Promoted_Designation]
,[WEF_Date]
,[Promoted_Gross]
,[Last_Gross])
VALUES
(1,1,'Trainee1','Trainee2','2013-11-01',1000,500)
GO
INSERT INTO [UserDB].[dbo].[Promotion]
([Prom_No]
,[Prom_EmpNo]
,[Last_Designation]
,[Promoted_Designation]
,[WEF_Date]
,[Promoted_Gross]
,[Last_Gross])
VALUES
(2,1,'Trainee2','Trainee3','2015-03-01',2000,1000)
GO
Try my script with various sample data and let me know if it not working any particular sample data.
First you can create and populate one year table which can be
permanent .In my example it is temporary.
create table #Year(yr int primary key)
insert into #Year
select (ROW_NUMBER()over(order by number)+1900) from master..spt_values
--select * from #Year
Main query start from here.
;WITH CTE
AS (
SELECT Emp_No NewEmpNo
,min(year(emp_joiningdate)) minYear
,max(year(case when Emp_ResignDate is not null then Emp_ResignDate else getdate() END)) maxYear
FROM [dbo].[Employee]
GROUP BY Emp_No
)
,CTE1
AS (
SELECT c.yr
,p.NewEmpNo
,e.*
FROM cte p
CROSS APPLY (
SELECT yr
FROM #Year c
WHERE c.yr >= minYear
AND c.yr <= maxYear
) c
LEFT JOIN (
SELECT e.*
,p.*
,year(isnull(p.WEF_Date,e.Emp_JoiningDate)) PYr
FROM dbo.employee E
LEFT JOIN [dbo].[Promotion] P ON e.Emp_No = p.Prom_EmpNo
) e ON p.NewEmpNo = e.Emp_No
AND c.yr = e.pyr
)
--select * from CTE1
,CTE2 as
(
SELECT yr
,NewEmpNo
,isnull(c.Emp_Number, prv.Emp_Number) Emp_Number
,isnull(c.Emp_Name, prv.Emp_Name) Emp_Name
,isnull(c.Emp_JoiningDate, prv.Emp_JoiningDate) Emp_JoiningDate
,isnull(c.Emp_ResignDate, prv.Emp_ResignDate) Emp_ResignDate
,isnull(c.Emp_Status, prv.Emp_Status) Emp_Status
,isnull(c.Emp_CurrentDesignation, prv.Emp_CurrentDesignation) Emp_CurrentDesignation
,isnull(c.Emp_CurrentGross, prv.Emp_CurrentGross) Emp_CurrentGross
,COALESCE(c.Last_Designation, prv.Last_Designation
,c.Emp_CurrentDesignation,prv.Emp_CurrentDesignation) Last_Designation
,COALESCE(c.Promoted_Designation, prv.Promoted_Designation
,c.Emp_CurrentDesignation,prv.Emp_CurrentDesignation) Promoted_Designation
,COALESCE(c.Last_Gross, prv.Last_Gross
,c.Emp_CurrentGross, prv.Emp_CurrentGross) Last_Gross
,COALESCE(c.Promoted_Gross, prv.Promoted_Gross
,c.Emp_CurrentGross, prv.Emp_CurrentGross) Promoted_Gross
,prv. PYr
--,prv1.*
FROM cte1 c
outer APPLY (
SELECT TOP 1 prve.*
,prvp.*
,year(isnull(WEF_Date,Emp_JoiningDate))Pyr
FROM dbo.employee prve
LEFT JOIN [dbo].[Promotion] prvp ON prve.Emp_No = prvp.Prom_EmpNo
WHERE (c.yr >= year(isnull(WEF_Date,Emp_JoiningDate))
)
AND c.NewEmpNo = prve.Emp_No
ORDER BY isnull(WEF_Date,Emp_JoiningDate) DESC
) prv
)
select
yr
,NewEmpNo
,isnull(c.Emp_Number, prv.Emp_Number) Emp_Number
,isnull(c.Emp_Name, prv.Emp_Name) Emp_Name
,isnull(c.Emp_JoiningDate, prv.Emp_JoiningDate) Emp_JoiningDate
,isnull(c.Emp_ResignDate, prv.Emp_ResignDate) Emp_ResignDate
,isnull(c.Emp_Status, prv.Emp_Status) Emp_Status
,isnull(c.Emp_CurrentDesignation, prv.Emp_CurrentDesignation) Emp_CurrentDesignation
,isnull(c.Emp_CurrentGross, prv.Emp_CurrentGross) Emp_CurrentGross
,COALESCE(c.Last_Designation, prv.Last_Designation
,c.Emp_CurrentDesignation,prv.Emp_CurrentDesignation) Last_Designation
,COALESCE(c.Promoted_Designation, prv.Promoted_Designation
,c.Emp_CurrentDesignation,prv.Emp_CurrentDesignation) Promoted_Designation
,COALESCE(c.Last_Gross, prv.Last_Gross
,c.Emp_CurrentGross, prv.Emp_CurrentGross) Last_Gross
,COALESCE(c.Promoted_Gross, prv.Promoted_Gross
,c.Emp_CurrentGross, prv.Emp_CurrentGross) Promoted_Gross
,prv. PYr
from cte2 c
outer apply(
SELECT TOP 1 prve.*
,prvp.*
,year(isnull(WEF_Date,Emp_JoiningDate))Pyr
FROM dbo.employee prve
LEFT JOIN [dbo].[Promotion] prvp ON prve.Emp_No = prvp.Prom_EmpNo
WHERE (c.yr <= year(isnull(WEF_Date,Emp_JoiningDate))
)
AND c.NewEmpNo = prve.Emp_No
ORDER BY isnull(WEF_Date,Emp_JoiningDate)
)prv
ORDER BY yr
DROP TABLE #Year
The CTE [Year] is to get the list of years from the Emp_JoiningDate and ResignDate. After that just CROSS join to the Employee table
For the promotion information (prev and promoted), use OUTER APPLY to get the last row by WEF_Date
; with
[Year] as
(
select [Year] = min(datepart(year, e.Emp_JoiningDate))
from [Employee] e
union all
select [Year] = y.[Year] + 1
from [Year] y
where [Year] < datepart(year, getdate())
)
select y.[Year], e.Emp_No, e.Emp_Number, e.Emp_Name, e.Emp_JoiningDate,
e.Emp_ResignDate, e.Emp_Status,
e.Emp_CurrentDesignation, e.Emp_CurrentGross,
Last_Designation = coalesce(p.Last_Designation, e.Emp_CurrentDesignation),
Promoted_Designation= coalesce(p.Promoted_Designation, e.Emp_CurrentDesignation),
Last_Gross = coalesce(p.Last_Gross, e.Emp_CurrentGross),
Promoted_Gross = coalesce(p.Promoted_Gross, e.Emp_CurrentGross)
from [Employee] e
cross join [Year] y
outer apply
(
select top 1 *
from [Promotion] x
where x.Prom_EmpNo = e.Emp_No
and datepart(year, x.[WEF_Date]) <= y.[Year]
order by x.WEF_Date desc
) p
where y.[Year] >= datepart(year, e.Emp_JoiningDate)
and y.[Year] <= datepart(year, isnull(e.Emp_ResignDate, getdate()))
order by y.[Year], e.Emp_No
RESULT
2013 1 A-001 Alex 2013-07-01 NULL On Board Trainee3 2000 Trainee1 Trainee2 500 1000
2014 1 A-001 Alex 2013-07-01 NULL On Board Trainee3 2000 Trainee1 Trainee2 500 1000
2014 2 A-002 Smith 2014-07-01 2015-07-01 Resigned HR1 1500 HR1 HR1 1500 1500
2015 1 A-001 Alex 2013-07-01 NULL On Board Trainee3 2000 Trainee2 Trainee3 1000 2000
2015 2 A-002 Smith 2014-07-01 2015-07-01 Resigned HR1 1500 HR1 HR1 1500 1500
2016 1 A-001 Alex 2013-07-01 NULL On Board Trainee3 2000 Trainee2 Trainee3 1000 2000
2017 1 A-001 Alex 2013-07-01 NULL On Board Trainee3 2000 Trainee2 Trainee3 1000 2000

Generate an excel like report from SQL

I am novice to SQL and
I have two tables Ticket and TicketAttributes with following Schema
CREATE TABLE [dbo].[Ticket](
[TicketID] [int] IDENTITY(1,1) NOT NULL, --Primary key
[Category] [varchar](256) NOT NULL,
[Description] [varchar](256) NULL,
[LibID] [int] NOT NULL,
[Status] [smallint] NULL,
[LogID] [int] NULL)
Ticket Attributes
CREATE TABLE [dbo].[TicketAttributes](
[TicketID] [int] NOT NULL,
[TicketAttrID] [int] IDENTITY(1,1) NOT NULL,
[AttributeID] [int] NOT NULL,
[AttributeGroup] [varchar](255) NULL,
[AttributeValue] [nvarchar](max) NULL,
[Status] [smallint] NULL,
[LogID] [int] NULL)
Where Ticket Attribute is another table that stores different attributes of a ticket like TicketStatus, TicketCategory etc..
Now I need to generate a report that looks like
TicketStatus1 TicketStatus 2 TicketStatus3
-----------------------------------------------------------------
TicketCategory1 7 3
Ticketcategory2 4
TicketCategory3 8
I want to see the count of each of the status of each ticket category.
For Eg:-
I have the following Data in TicketTable
----------------------------------------------
TicketID Name Price Date
------------------------------------------------
155 Ticket4 $20 16 Jan 2016
157 Ticket3 $300 17 Jan 2016
158 Ticket1 $100 18 Jan 2016
159 Ticket2 $500 19 Jan 2016
Now in the TicketAttribute Table
----------------------------------------------
TicketID AttributeID AttributeValue
------------------------------------------------
155 500 Joe
155 600 Reserved
155 700 Economy
155 800 San Jose
where AttributeIDs
500=Nameofthe Passenger
600= Status of Ticket
700= Class
800= Destination
Now lets say I want to see what is the count of number of active tickets in each of the class per status
Booked Cancelled PaymentPending ............
-----------------------------------------------------------------
Economy 7 3
Economy Plus 4
Business 8
Hope I am clear now.
how to go about this using SQL Query
USING PIVOT
;WITH cte AS (
SELECT
c.AttributeValue as Class
,s.AttributeValue as StatusOfTicket
FROM
Ticket t
LEFT JOIN TicketAttributes c
ON t.TicketId = c.TicketId
AND c.AttributeID = 700
LEFT JOIN TicketAttributes s
ON t.TicketId = s.TicketId
AND s.AttributeID = 600
)
SELECT *
FROM
cte
PIVOT (
COUNT(StatusOfTicket) FOR StatusOfTicket IN (Reserved,Cancelled,PaymentPending)
) p
USING Conditional Aggregation:
SELECT
c.AttributeValue as Class
,COUNT(DISTINCT CASE WHEN s.AttributeValue = 'Reserved' THEN c.TicketId END) as Reserved
,COUNT(DISTINCT CASE WHEN s.AttributeValue = 'Cancelled' THEN c.TicketId END) as Cancelled
,COUNT(DISTINCT CASE WHEN s.AttributeValue = 'PaymentPending' THEN c.TicketId END) as PaymentPending
FROM
Ticket t
LEFT JOIN TicketAttributes c
ON t.TicketId = c.TicketId
AND c.AttributeID = 700
LEFT JOIN TicketAttributes s
ON t.TicketId = s.TicketId
AND s.AttributeID = 600
GROUP BY
c.AttributeValue

sql server 2014 group by date extract several fields

I have a table that looks like this:
CREATE TABLE dbo.Mails (
ID int IDENTITY(1, 1) NOT NULL,
Reference nvarchar(20) COLLATE Latin1_General_CI_AS NULL,
Email nvarchar(70) NOT NULL,
ETS datetime NULL, --Estimated Time of Shipping
ATS datetime NULL, --Actual Time of Shipping
ReadOn datetime NULL,
Unsubscribed datetime NULL,
Bounced datetime NULL,
BouncedReason nvarchar(30) COLLATE Latin1_General_CI_AS NULL,
Active bit DEFAULT 1 NULL
)
I need to show info on a chart, and I need to group by Date.
therefore if I want to group details by ReadOn field for a certain campaign I build the following query
Select
CAST(readOn as date) [date],
COUNT(*) [read]
FROM Mails m
WHERE m.Reference=#Reference
GROUP BY CAST(readOn as date)
ORDER BY CAST(readOn as date) ASC
and I get something like this:
sDate read
NULL 360
2016-05-05 67
2016-05-06 123
2016-05-07 84
2016-05-08 62
2016-05-09 89
2016-05-10 17
2016-05-11 12
2016-05-12 8
2016-05-13 4
2016-05-14 4
But I would like to extract, in the same query, not only ReadOn field, but also other fields like ETS, ATS, Unsubscribed/Read & Unread and Bounced
and get something like this
sDate read ETS ATS Bounced Unsub./Read Unsub/Unread
NULL 360
2016-05-05 67 830 570 27 7 3
2016-05-06 123 0 260 4 9 5
2016-05-07 84 0 0 0 2 2
2016-05-08 62 0 0 0 2 4
2016-05-09 89 0 0 0 7 1
2016-05-10 17 0 0 0 5 6
2016-05-11 12 0 0 0 8 2
2016-05-12 8 0 0 0 1 3
2016-05-13 4 0 0 0 0 2
2016-05-14 4 0 0 0 0 2
Is there an easier way than building 6 different queries?
can at least indicate the path to follow?
Thanks
Joe
You can do it with some pre-processing and a PIVOT. In this example, I've put the query into a stored procedure, so that it is contained and easy to test. I'm doing the pre-processing in a CTE, to keep the main query tidy.
First, create the table and populate it.
CREATE TABLE dbo.Mails
(
ID int IDENTITY(1, 1) NOT NULL,
Reference nvarchar(20) COLLATE Latin1_General_CI_AS NULL,
ETS datetime NULL, --Estimated Time of Shipping
ATS datetime NULL, --Actual Time of Shipping
ReadOn datetime NULL,
Unsubscribed datetime NULL,
Bounced bit DEFAULT 0 NULL,
BouncedReason nvarchar(30) COLLATE Latin1_General_CI_AS NULL,
Active bit DEFAULT 1 NULL
);
GO
INSERT INTO dbo.Mails (Reference, ETS, ATS, ReadOn, Unsubscribed, Bounced)
VALUES
(N'ABC', '2015-05-05', '2015-05-05', '2015-05-05', NULL, 0),
(N'ABC', '2015-05-06', '2015-05-07', '2015-05-08', NULL, 0),
(N'ABC', '2015-05-05', '2015-05-05', '2015-05-07', NULL, 0),
(N'ABC', '2015-05-07', '2015-05-08', '2015-05-09', NULL, 0),
(N'ABC', '2015-05-06', '2015-05-07', '2015-05-09', '2015-05-09', 0),
(N'ABC', '2015-05-06', '2015-05-07', NULL, '2015-05-08', 0);
Then create a stored procedure with a parameter #Reference. I'm using a CTE to create a two column row set, with Date and Type as the columns. Then, in the main SELECT statement it's being pivoted to give the result you want.
The row set produced by the CTE looks like this.
Note: I haven't included the Bounced column, because I'm not clear what the requirement is for that; it's not a date column. However you should be able to extend this example quite easily.
CREATE PROCEDURE dbo.up_ReportMails
(
#Reference nvarchar(20)
)
AS
WITH cte AS
(
SELECT CAST(ReadOn AS date) AS 'Date', 'R' AS 'Type'
FROM dbo.Mails
WHERE Reference = #Reference AND ReadOn IS NOT NULL
UNION ALL
SELECT CAST(ETS AS date), 'E'
FROM dbo.Mails
WHERE Reference = #Reference AND ETS IS NOT NULL
UNION ALL
SELECT CAST(ATS AS date), 'A'
FROM dbo.Mails
WHERE Reference = #Reference AND ATS IS NOT NULL
UNION ALL
SELECT CAST(Unsubscribed AS date), 'U'
FROM dbo.Mails
WHERE Reference = #Reference AND UNSUBSCRIBED IS NOT NULL AND ReadOn IS NOT NULL
UNION ALL
SELECT CAST(Unsubscribed AS date), 'V'
FROM dbo.Mails
WHERE Reference = #Reference AND UNSUBSCRIBED IS NOT NULL AND ReadOn IS NULL
)
SELECT [Date], [R] AS 'Read', [E] AS 'ETS', [A] AS 'ATS', [U] AS 'Unsub/Read', [V] As 'Unsub/Unread'
FROM
(SELECT [Date], [Type]
FROM cte) AS C
PIVOT
(
COUNT([Type])
FOR [Type] IN ([R], [E], [A], [U], [V])
) AS PivotTable
ORDER BY [Date];
Then we can test it.
EXEC dbo.up_ReportMails #Reference=N'ABC';
I've tested this code and it works. Assuming you have a calendar table (if you don't have one, you can Google it and make one in about 10 minutes - they're very simple and will save you loads of time):
DECLARE #StartDate date = '01/01/2016'
DECLARE #EndDate date = '05/06/2016'
SELECT C.BaseDate,
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ETS AS DATE) THEN 1 END), 0) AS [ETS],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ATS AS DATE) THEN 1 END), 0) AS [ATS],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.ReadOn AS DATE) THEN 1 END), 0) AS [Read On],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.Unsubscribed AS DATE) THEN 1 END), 0) AS [Unsubscribed],
ISNULL(SUM(CASE WHEN C.BaseDate = CAST(M.Bounced AS DATE) THEN 1 END), 0) AS [Bounced]
FROM Calendar C
LEFT OUTER JOIN Mails M
ON C.BaseDate = CAST(M.ETS AS DATE)
OR C.BaseDate = CAST(M.ATS AS DATE)
OR C.BaseDate = CAST(M.ReadOn AS DATE)
OR C.BaseDate = CAST(M.Unsubscribed AS DATE)
OR C.BaseDate = CAST(M.Bounced AS DATE)
WHERE C.BaseDate BETWEEN #StartDate AND #EndDate
GROUP BY C.BaseDate
Basically what you're doing is selecting every date from the calendar table within your date range, and then joining it to your mails table if ANY of the datetimes match that date. The purpose of the left join is so that dates on which nothing occurs are still returned in your result set. They will all be zeros, but it's better for consistency and in case someone wants to calculate averages from your report.
Once you have all the dates - and all of the records that have a matching datetime, you just need to count how many, for each date, have a matching ETS, how many have a matching ATS, so on and so forth. Last, you group by the calendar date and you're all done.

CTE - recursively update quantity until total consumed

I've been researching CTEs trying to determine if it's possible to recursively update inventory quantity records with an order quantity until the order quantity is consumed.
Here are the tables and records:
CREATE TABLE [dbo].[myOrder](
[Account] [float] NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL
) ON [PRIMARY]
insert into dbo.myOrder values (12345, 1, 50)
CREATE TABLE [dbo].[myInventory](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Account] [float] NOT NULL,
[InvDate] [numeric](18, 0) NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL,
[QuantitySold] [float] NOT NULL
) ON [PRIMARY]
insert into dbo.myInventory values (12345, 111287, 1, 45, 40)
insert into dbo.myInventory values (12345, 111290, 1, 40, 0)
insert into dbo.myInventory values (12345, 111290, 1, 12, 0)
insert into dbo.myInventory values (12345, 111291, 1, 25, 0)
The record in the myOrder table indicates that an order is to be created for account 12345 for item #1, quantity 50:
Account Item Quantity
------- ---- --------
12345 1 50
The inventory table shows that we have plenty of item #1 on hand for account 12345:
ID Account InvDate Item Quantity QuantitySold
-- ------- ------- ---- -------- ------------
1 12345 111287 1 45 40
2 12345 111290 1 40 0
3 12345 111290 1 12 0
4 12345 111291 1 25 0
The goal is to start plugging in the order quantity of 50 into the inventory records until all 50 are consumed. Inventory records are ordered by the value in the InvDate column. Record 1 has 5 remaining quantity (45 - 40 = 5), which would leave us with 45 more to consume for the order. Record 2 can consume 40. Record 3 can consume the last 5. When the query completes the inventory records would look like this:
ID Account InvDate Item Quantity QuantitySold
-- ------- ------- ---- -------- ------------
1 12345 111287 1 45 45
2 12345 111290 1 40 40
3 12345 111290 1 12 5
4 12345 111291 1 25 0
Note: The inventory table stores QuantitySold, not QuantityRemaining, so you have to do the math (Quantity - QuantitySold) to determine how much quantity remains per inventory record.
I've gotten almost nowhere with the CTE. I've found plenty of examples for doing selects where you have 2 parts to your CTE - an initialization part and the recursive part UNIONed together. I could write this with a cursor, but I think it's possible to do with a CTE and I'd like to learn how.
If anyone can confirm this is possible with a CTE or explain how to set up the CTE, I'd appreciate it. Thanks!
--#inserted table mimics inserted virtual table from AFTER INSERT triggers on [dbo].[myOrder] table
DECLARE #inserted TABLE
(
[Account] [float] NOT NULL,
[Item] [float] NOT NULL,
[Quantity] [float] NOT NULL
);
INSERT #inserted
VALUES (12345, 1, 50);
WITH CteRowNumber
AS
(
SELECT inv.ID
,inv.Account
,inv.Item
,inv.Quantity
,inv.QuantitySold
,i.Quantity QuantityOrdered
,ROW_NUMBER() OVER(PARTITION BY inv.Account,inv.Item ORDER BY inv.ID ASC) RowNumber
FROM myInventory inv
INNER JOIN #inserted i ON inv.Account = i.Account
AND inv.Item = i.Item
WHERE inv.Quantity > inv.QuantitySold
), CteRecursive
AS
(
SELECT a.ID
,a.Account
,a.Item
,a.RowNumber
,CASE
WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold
ELSE a.QuantityOrdered
END QuantitySoldNew
,CASE
WHEN a.Quantity - a.QuantitySold < a.QuantityOrdered THEN a.Quantity - a.QuantitySold
ELSE a.QuantityOrdered
END RunningTotal
FROM CteRowNumber a
WHERE a.RowNumber = 1
UNION ALL
SELECT crt.ID
,crt.Account
,crt.Item
,crt.RowNumber
,CASE
WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN crt.Quantity - crt.QuantitySold
ELSE crt.QuantityOrdered - prev.RunningTotal
END QuantitySoldNew
,CASE
WHEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold) < crt.QuantityOrdered THEN prev.RunningTotal + (crt.Quantity - crt.QuantitySold)
ELSE crt.QuantityOrdered
END RunningTotal
FROM CteRecursive prev
INNER JOIN CteRowNumber crt ON prev.Account = crt.Account
AND prev.Item = crt.Item
AND prev.RowNumber + 1 = crt.RowNumber
WHERE prev.RunningTotal < crt.QuantityOrdered
)
SELECT cte.ID
,cte.Account
,cte.Item
,cte.QuantitySoldNew
FROM CteRecursive cte;
--or CteRecursive can be used to update QuantitySold column from [dbo].[myInventory] table
--UPDATE myInventory
--SET QuantitySold = inv.QuantitySold + cte.QuantitySoldNew
--FROM myInventory inv
--INNER JOIN CteRecursive cte ON inv.ID = cte.ID;

Resources