I am new to this website but I've referenced this website a lot in the past to guide me along my current class.
Our teacher assigned us some homework and we created a database that has online seminars. The members in the database pay for the online seminars. They can pay in either 4 different ways: Monthly, quarterly, yearly, or every 2 years. I created a renewal table that assigns a RenewalID for each of the subscription levels and is used as a foreign key in the Members table to indicate their level of subscription.
Here's the question that the teacher is asking:
Members are charged for renewals according to their payment plan (monthly, quarterly, etc..) on the anniversary of the date they joined. A user who joined on the 7th should always be billed on the 7th, whether it's every one, three or 12 months. Some method is needed to scan for current members who are up for renewal and to initiate the billing to their credit card.
I was thinking to create a view that would display their card information when it's time to bill to them. I couldn't figure out the monthly, quarterly, or 2 year cycles but I used the following to calculate the yearly renewal:
select pc.*
from Members m
inner join paymentcard pc on pc.memberid = m.MemberID
where
CurrentFlag <> 0
and DATEPART(month, startdate) = DATEPART(MONTH, getdate())
and datepart(day, startdate) = DATEPART(day, getdate())
and RenewalID = 2
However, after messing around more, I'm beginning to realize that it's going to be more than just a view. I believe it needs to be a stored procedure. I'm not exactly sure how I would go about doing this..
I'm turning to the wonderful members of this website to help me figure this out.. thanks!
I ended up figuring it out with another student. This is what we came up with. Also, the teacher said this works so I'm content.
I understand how useful this website is so I will post the solution I found and also the solution my instructor came up with to help anyone in the future.
My solution:
First, I created the view:
GO
CREATE VIEW vwRenewal
AS
select m.memberid, m.FirstName, m.LastName, m.StartDate, m.RenewalID, r.RenewalPrice, pc.CardID
from Members m
inner join
Renewal r
on m.RenewalID = r.RenewalID
inner join
PaymentCard pc
on pc.MemberID = m.MemberID
WHERE m.CurrentFlag <> 0 AND m.RenewalID = 4
AND DATEPART(day, startdate) = DATEPART(day, getdate())
UNION ALL
select m.memberid, m.FirstName, m.LastName, m.StartDate, m.RenewalID, r.RenewalPrice, pc.CardID
from Members m
inner join
Renewal r
on m.RenewalID = r.RenewalID
inner join
PaymentCard pc
on pc.MemberID = m.MemberID
WHERE m.CurrentFlag <> 0 AND m.RenewalID = 3
AND DATEPART(day, startdate) = DATEPART(day, getdate())
AND DATEPART(month, GETDATE()) IN
((select (datepart(month, m.startdate) + 3)),
(select (datepart(month, m.startdate) + 6)),
(select (datepart(month, m.startdate) + 9)))
UNION ALL
select m.memberid, m.FirstName, m.LastName, m.StartDate, m.RenewalID, r.RenewalPrice, pc.CardID
from Members m
inner join
Renewal r
on m.RenewalID = r.RenewalID
inner join
PaymentCard pc
on pc.MemberID = m.MemberID
WHERE m.CurrentFlag <> 0 AND m.RenewalID = 2
AND DATEPART(day, startdate) = DATEPART(day, getdate())
AND DATEPART(month, startdate) = DATEPART(month, getdate())
UNION
ALL
select m.memberid, m.FirstName, m.LastName, m.StartDate, m.RenewalID, r.RenewalPrice, pc.CardID
from Members m
inner join
Renewal r
on m.RenewalID = r.RenewalID
inner join
PaymentCard pc
on pc.MemberID = m.MemberID
WHERE m.CurrentFlag <> 0 AND m.RenewalID = 1
AND GETDATE() IN
((SELECT dateadd(month, 24, m.startdate)),
(select dateadd(month, 48, m.startdate)),
(select dateadd(month, 72, m.startdate)))
GO
Then, the stored procedure:
CREATE PROCEDURE sp_Renewal
AS BEGIN
IF EXISTS (select * from vwRenewal) BEGIN
INSERT INTO Transactions (CardID, TransactionDate, Charge, Result)
VALUES ( (select CardID
from vwRenewal),
getdate(),
(select RenewalPrice
from vwRenewal),
'Pending')
END SELECT * FROM Transactions WHERE Result = 'Pending'
END
Instructor solution:
-- Determine the member's next charge date based on the last time they were charged.
DECLARE #RETURNDATE DATE -- Return value
DECLARE #SUBLEVEL INT -- Member's subscription level
DECLARE #MEMBERDAYS INT -- Days in member subscription period
DECLARE #JOINDATE DATE -- Date member joined
DECLARE #LASTCHARGE DATE -- Last charge to member account
-- Get subscription level and days in subscription period.
SELECT #SUBLEVEL = m.SubscriptionLevel, #MEMBERDAYS = s.DaysPerPeriod, #JOINDATE = m.JoinDate
FROM Members m
INNER JOIN SubscriptionLevels s
ON s.SubLevelID = m.SubscriptionLevel
WHERE MemberID = #MemberID
-- If the member's subscription level is not free...
IF #SUBLEVEL NOT IN (SELECT SubLevelID FROM SubscriptionLevels WHERE RenewalAmt > 0)
BEGIN
-- Get the most recent account charge date for the member.
-- There should always be a charge date, even for new members.
SELECT TOP 1 #LASTCHARGE = ChargeDate
FROM AccountCharges
WHERE MemberID = #MemberID
ORDER BY ChargeDate DESC
-- If there is a charge date, add the number of days in the membership period
-- to get the new date. Otherwise, add it to the join date.
IF #LASTCHARGE IS NOT NULL
SET #RETURNDATE = DATEADD(DAY, #MEMBERDAYS, #LASTCHARGE)
ELSE
SET #RETURNDATE = DATEADD(DAY, #MEMBERDAYS, #JOINDATE) -- Just in case there's no previous charge.
END
ELSE
-- If the member has a free plan, just add one year to today so the member is never charged.
SET #RETURNDATE = DATEADD(YEAR, 1, GETDATE())
RETURN #RETURNDATE
END
GO
Note: I only post this to help people in the future in some way who may encounter a similar situation as me or whatever they need it for..
Hope this helps. Enjoy.
Like any code, this is only a couple ways to accomplish this task. There are many other ways I'm sure..
Related
I have a subscription table with a user ID, a subscription start date and a subscription end date. I also have a calendar table with a datestamp field, that is every single date starting from the first subscription date in my subscription table.
I am trying to write something that would give me a table with a date column and three numbers: number of total active (on that day), number of new subscribers, number of unsubscribers.
(N.B. I tried to insert sample tables using the suggested GitHub Flavoured Markdown but it just all goes into one row.)
Currently I am playing with a query that creates multiple joins between the two tables, one for each number:
select a.datestamp
,count(distinct case when b_sub.UserID is not null then b_sub.UserID end) as total_w_subscription
,count(distinct case when b_in.UserID is not null then b_in.UserID end) as total_subscribed
,count(distinct case when b_out.UserID is not null then b_out.UserID end) as total_unsubscribed
from Calendar as a
left join Subscription as b_sub -- all those with subscription on given date
on b_sub.sub_dt <= a.datestamp
and (b_sub.unsub_dt > a.datestamp or b_sub.unsub_dt is null)
left join Subscription as b_in -- all those that subscribed on given date
on b_in.sub_dt = a.datestamp
left join Subscription as b_out -- all those that unsubscribed on given date
on b_out.unsub_dt = a.datestamp
where a.datestamp > '2021-06-10'
group by a.datestamp
order by datestamp asc
;
I have indexed the date fields in both tables. If I only look at one day, it runs in 3 seconds. Two days already takes forever. The Sub table is over 2.6M records and ideally I'll need my timeline to begin sometime in 2012.
What would be the most time efficient way to do this?
You're on the right track. I created some table variables and assumed a data structure that has each subscription include a start and end date.
--Create #dates table variable for calendar
DECLARE #startDate DATETIME = '2018-01-01'
DECLARE #endDate DATETIME = '2021-06-18'
DECLARE #dates TABLE
(
reportingdate DATETIME
)
WHILE #startDate <= #endDate
BEGIN
INSERT INTO #dates SELECT #startDate
SET #startDate += 1
END
--Create #subscriptions table variable for subcriptions to join onto calendar
DECLARE #subscriptions TABLE
(
id INT
,startDate DATETIME
,endDate DATETIME
)
INSERT INTO #subscriptions
VALUES
(1,'2018-01-01 00:00:00.000','2019-10-07 00:00:00.000')
,(2,'2018-01-11 00:00:00.000','2019-12-21 00:00:00.000')
,(3,'2019-04-21 00:00:00.000','2020-03-19 00:00:00.000')
,(4,'2019-12-09 00:00:00.000','2020-05-14 00:00:00.000')
,(5,'2020-04-26 00:00:00.000','2020-07-06 00:00:00.000')
,(6,'2020-05-02 00:00:00.000',NULL)
,(7,'2020-08-31 00:00:00.000','2020-10-29 00:00:00.000')
,(8,'2020-12-13 00:00:00.000','2021-01-13 00:00:00.000')
,(9,'2021-02-12 00:00:00.000','2021-04-19 00:00:00.000')
,(10,'2021-06-10 00:00:00.000',NULL)
;
Then I join the subscription onto the calendar table.
--CTE to join subscription onto calendar and use ROW_NUMBER functions
WITH cte AS (
SELECT
s.id AS SubID
,d.ReportingDate
,ROW_NUMBER() OVER (PARTITION BY s.id ORDER BY d.ReportingDate) AS asc_rn --used to identify 1st
,ROW_NUMBER() OVER (PARTITION BY s.id ORDER BY d.ReportingDate DESC) AS desc_rn --used to identify last
,CASE WHEN s.endDate IS NULL THEN 1 ELSE 0 END AS ActiveSub
FROM #subscriptions s
LEFT JOIN #dates d ON
d.reportingdate BETWEEN s.startDate AND ISNULL(s.endDate,'9999-12-31')
)
I used ROW_NUMBER to identify the first and last date rows of the subscription, as well as checking if the subscription endDate is NULL (still active). I then query the CTE to count subscriptions grouped by day, as well as summing new and terminated subscriptions grouped by day.
--Query CTE using asc_rn, desc_rn, and ActiveSub to identify new subscribers and unsubscribers.
SELECT
ReportingDate
,COUNT(*) AS TotalSubscribers
,SUM(CASE WHEN asc_rn = 1 THEN 1 ELSE 0 END) AS NewSubscribers
,SUM(CASE WHEN desc_rn = 1 AND ActiveSub = 0 THEN 1 ELSE 0 END) AS UnSubscribers
FROM cte
GROUP BY ReportingDate
ORDER BY ReportingDate
I am building a report where I need to display the number of days an item was with a customer in the chosen month.
#Month is set as a parameter and the report is filtered based on the month a user chooses.
I have DATEDIFF(d, dbo.Rental.StartDateTime, { fn NOW() }) + 1 AS [Days Live] however this isn't what I need.
A rental could start before the chosen month or part way through. It could end and any date in the chosen month or not have been ended at all. so for example if running the report for November (assuming i am now in December) i could have a rental that started (dbo.Rental.StartDateTime) 15th October and ended (dbo.Rental.EndDateTime) 11th November. I would need the field to say 11 however if there is no end date i would need it to say 30
I have formatted both fields to be dd/mm/yyy. The dataset is also doing various other things in terms of displaying the relevant records.
My entire dataset is below.
SELECT TOP (100) PERCENT System_1.SystemTypeId, System_1.Id, System_1.CancellationCode, System_1.RentalId, Format(System_1.CreatedOnDateTime, 'dd/MM/yyyy') AS [Assembled Date],
Format(dbo.Rental.StartDateTime, 'dd/MM/yyyy') AS [Start Date], DATEPART(month, dbo.Rental.StartDateTime) AS [Start Date Month], dbo.Rental.Revision, dbo.Rental.RentalNumber, dbo.Rental.RentalStatus, dbo.Customer.Name AS Customer, dbo.Site.Name AS Site,
dbo.Location.Name AS Location, dbo.Rental.SpecialInstructions, DATEDIFF(d, dbo.Rental.StartDateTime, { fn NOW() }) + 1 AS [Days Live], dbo.SystemType.Description, Format(dbo.Rental.EndDateTime,
'dd/MM/yyyy') AS [End Date], DATEPART(month, dbo.Rental.EndDateTime) AS [End Date Month]
FROM dbo.SystemType INNER JOIN
dbo.System AS System_1 INNER JOIN
dbo.Contract ON System_1.ContractId = dbo.Contract.Id INNER JOIN
dbo.Customer ON dbo.Contract.CustomerId = dbo.Customer.Id ON dbo.SystemType.Id = System_1.SystemTypeId LEFT OUTER JOIN
dbo.Location INNER JOIN
dbo.Rental ON dbo.Location.Id = dbo.Rental.LocationId INNER JOIN
dbo.Site ON dbo.Location.SiteId = dbo.Site.Id ON System_1.Id = dbo.Rental.SystemId
WHERE (dbo.Rental.RentalStatus NOT LIKE 'PendingActivations') AND dbo.customer.ID = #Customer AND ((DATEPART(month, dbo.Rental.StartDateTime) = #Month) OR (DATEPART(month, dbo.Rental.EndDateTime) = #Month) OR ( (DATEPART(month, dbo.Rental.StartDateTime) < #Month) AND (dbo.Rental.EndDateTime IS NULL )))
ORDER BY System_1.Id
I have to get the list of months and year in between my dates. Currently it only returns month and year for dates that has data associated with it.
for example my dates is between: '8'+'/1'+'/'+'2015' and DATEADD(mm, 15, '8'+'/1'+'/'+'2016'
It only prints out: May2016, June2016, July2016, Auguest2016, September2016
I want it to print out all of the months and year in between. Here is my sql queries:
select d.id_base as case_id,
c.C_LAST_ACTION AS Docketed,
c.C_CASE_TYPE AS caseType,
ct.C_NAME As caseName,
ct.C_DESCRIPTION AS caseNameDescription,
case when d.c_mod_decision_id is not null then '' else DATENAME(mm, d.c_issue_date) + DATENAME(yyyy, d.c_issue_date) end as display
from t_case_decision d JOIN T_CASE_INPUT c on c.id = d.id_base JOIN T_CASE_TYPE ct on C_CASE_TYPE = ct.id
where cast(d.c_issue_date AS date) BETWEEN '8'+'/1'+'/'+'2015' and DATEADD(mm, 15, '8'+'/1'+'/'+'2016')
First, create a numbers table
CREATE TABLE Numbers(N INT)
insert into Numbers(N)
select top 1000000 row_number() over(order by t1.number) as N
from master..spt_values t1
cross join master..spt_values t2
then use DATEADD to list dates between desired values, like this
declare #iniDate as date
set #iniDate='20150801'
select dateadd(MONTH,N,#iniDate) dates
from Numbers
where N<15 order by N
These returns dates from #iniDate up to 15 months later
EDIT: try this, I don't have sql right now
select datename(mm, dateadd(MONTH,N,#iniDate))+datename(yyyy ,dateadd(MONTH,N,#iniDate)) display
from ( select top 15row_number() over(order by t1.number) as N
from master..spt_values t1
cross join master..spt_values t2) numbers right join (
select d.id_base as case_id,
c.C_LAST_ACTION AS Docketed,
c.C_CASE_TYPE AS caseType,
ct.C_NAME As caseName,
ct.C_DESCRIPTION AS caseNameDescription,
case when d.c_mod_decision_id is not null then '' else DATENAME(mm, d.c_issue_date) + DATENAME(yyyy, d.c_issue_date) end as display
from t_case_decision d JOIN T_CASE_INPUT c on c.id = d.id_base JOIN T_CASE_TYPE ct on C_CASE_TYPE = ct.id
where cast(d.c_issue_date AS date) BETWEEN '8'+'/1'+'/'+'2015' and DATEADD(mm, 15, '8'+'/1'+'/'+'2016')
sql-server
) qq
on datename(mm, dateadd(MONTH,N,#iniDate))+datename(yyyy ,dateadd(MONTH,N,#iniDate)) = qq.display
where N<15 order by N
If I understand what you're trying to accomplish, a recursive CTE might help. Here's a quick example of what you can do. The CTE will expand out into a list of dates, which you can then use as the base for your query.
The contents of the TargetData CTE may need to be adjusted, as I don't have a complete picture of your data structure.
DECLARE #startDate DATE = '1/1/2015';
DECLARE #endDate DATE = '7/31/2016';
-- Recursive CTE to generate a list of months within the date range:
WITH Months AS (
SELECT CONVERT(DATE, DATEADD(D, -(DAY(#startDate)) + 1, #startDate)) [MonthDate]
UNION ALL
SELECT DATEADD(M, 1, MonthDate)
FROM Months
WHERE MonthDate <= DATEADD(M, -1, #endDate)
),
TargetData AS (
-- This is a slightly modified version of the original query:
select
d.id_base as case_id,
c.C_LAST_ACTION AS Docketed,
c.C_CASE_TYPE AS caseType,
ct.C_NAME As caseName,
ct.C_DESCRIPTION AS caseNameDescription,
case when d.c_mod_decision_id is not null then '' else DATENAME(mm, d.c_issue_date) + DATENAME(yyyy, d.c_issue_date) end as display,
-- Return the "MonthDate" so that it can be left joined to the Months table:
DATEADD(D, -(DAY(d.c_issue_date)) + 1, d.c_issue_date) [MonthDate]
from t_case_decision d JOIN T_CASE_INPUT c on c.id = d.id_base JOIN T_CASE_TYPE ct on C_CASE_TYPE = ct.id
where cast(d.c_issue_date AS date) BETWEEN #startDate AND #endDate
)
SELECT
m.MonthDate,
DATENAME(mm, m.MonthDate) + DATENAME(yyyy, m.MonthDate),
td.*
FROM Months m
LEFT JOIN TargetData td ON td.MonthDate = m.MonthDate;
You need to join on primary keys between tables, I haven't seen a between statement with that syntax. So I suggest trying the following:
SELECT d.id_base as case_id, c.C_LAST_ACTION AS 'Docketed',c.C_CASE_TYPE AScaseType,ct.C_NAME As 'caseName', ct.C_DESCRIPTION AS 'caseNameDescription'
,CASE
WHEN d.c_mod_decision_id is not null THEN '' AS 'null_val'
ELSE CONCAT(YEAR(d.c_issue_dateDATENAME), MONTH(d.c_issue_date))
END AS 'display'
FROM t_case_decision d INNER JOIN T_CASE_INPUT c on c.id = d.id_base
INNER JOIN T_CASE_TYPE ct on c.id = ct.id
WHERE CONVERT(DATE,d.c_issue_date) BETWEEN '08/01/2015'
AND '08/01/2016';
I hope this helps or points you in the right direction :)
I have two queries. One query pulls the information based on the orders that have been scheduled to them in a day by employee. The other query pulls the number of orders that have been completed and paid in a day, along with the total amount of the revenue from the orders.
I have scraped the forums to get the code together to get these queries, but I need to have both queries joined together. I want to use this information in report builder once I get it done. It's probably simple for someone, but it's confusing me as I'm far from any sort of expert with SQL.
I only need one day at the moment, but found this code and modified for now and hope that I can use it in the future when needing week and month data.
Any help would be appreciated.
Query 1
declare #dt DATE = '20160823'
set DATEFIRST 7;
set #dt = dateadd(week, datediff(week, '19050101', #dt), '19050101');
;with dt as
(
select
Technician = (CASE emp_id
WHEN 'CW697' THEN 'Joe Biggs'
WHEN 'DZ663' THEN 'Mimi Cassidy'
END),
dw = datepart(weekday, DATE)
from
dbo.ordemps
where
date >= #dt and date <dateadd(day, 7, #dt)
),
x AS
(
select
Technician, dw = coalesce(dw,8),
c = convert(varchar(11), COUNT(*))
from
dt
group by
grouping sets((Technician), (Technician,dw))
)
select
Technician,
[Sun] = coalesce([1], '-'),
[Mon] = coalesce([2], '-'),
[Tue] = coalesce([3], '-'),
[Wed] = coalesce([4], '-'),
[Thu] = coalesce([5], '-'),
[Fri] = coalesce([6], '-'),
[Sat] = coalesce([7], '-'),
TOTAL =[8]
from
x
PIVOT
(MAX(c) FOR dw IN([1],[2],[3],[4],[5],[6],[7],[8])) as pvt;
Query 2
select
case
when grouping(d.m)=15 then 'Year ' + cast(max(d.y) as varchar(10))
when grouping(date)=15 then datename(m, max(DATE)) + ' ' + cast(max(d.y) as varchar(10))
else cast(cast([date] as date) as varchar(255))
end as DATE,
TotalOrders = /*sum(Amount)*/convert(varchar(11), COUNT(*)),
TotalSales = sum(Amount),
Technician = (CASE recv_by
WHEN 'CW697' THEN 'Joe Biggs'
WHEN 'DZ663' THEN 'Mimi Cassidy'
END)
from
ordpay
cross apply
(select
datepart(yy, [date]),
datepart(m, [date])
) d(y, m)
where
[DATE] >= dateadd(day, datediff(day, 1, getdate()), 0)
and [date] < dateadd(day, datediff(day, 0, getdate()), 0)
group by
recv_by, d.y, rollup (d.m, date)
order by
d.y desc, grouping(d.m), d.m, grouping(DATE), DATE
At the easiest level you can use can join sub-queries. Your example is a little trickier because you are using CTEs which can't go in the subquery. To get you going in the right direction it should generally look like this:
with cte1 as (),
cte2 as ()
select *
from (
select *
from tables
where criteria -- subquery 1 (cannot contain order by)
) a
join ( -- could be a left join
select *
from tables
where criteria -- subquery 2 (cannot contain order by)
) b
on b.x = a.x --join criteria
where --additional criteria
order by --final sort
where subquery 1 would be your query 1 from select Technician to the end
and subquery 2 would be everything from your query 2 except the order by.
For further information on joins see the MSDN documentation
Since you aren't going to research much it doesn't seem here's a clunky and quick way, and keeps it simple for you, without having to explain a lot if we were to join the queries together more elegantly.
The first set of code has 2 common table expressions (CTE). So, select those results into a temp. Notice the --addition here
....
select
Technician,
[Sun] = coalesce([1], '-'),
[Mon] = coalesce([2], '-'),
[Tue] = coalesce([3], '-'),
[Wed] = coalesce([4], '-'),
[Thu] = coalesce([5], '-'),
[Fri] = coalesce([6], '-'),
[Sat] = coalesce([7], '-'),
TOTAL =[8]
into #someTemp --Addition here
from
....
Do the same for your second query...
...
into #someOtherTemp --Addition here
from
ordpay
...
Then join them togehter...
select t1.someColumns, t2.someOtherColumns
from #someTemp t1
inner join #someOtherTemp t2 on t1.column = t2.column
Hi I have an SQL server database with 3 columns Activity[start_date(datetime),end_date(datetime),title(string)]
and I wish to count for a whole year, how many activities have start_date in each month, I mean I would like a return of 12 values(12 months) that count the activities within the months, thanks.
If there is an index on start_date and/or if you need to include months in the result even if there was no activity in that month, you might consider this one:
DECLARE #year INT;
SET #year = 2012;
;WITH n AS
(
SELECT TOP (12) m = DATEADD(MONTH, ROW_NUMBER() OVER
(ORDER BY name)-1, DATEADD(YEAR, #year-1900, 0))
FROM sys.all_objects ORDER BY name
)
SELECT [Month] = n.m, ActivityCount = COUNT(t.title)
FROM n
LEFT OUTER JOIN dbo.unspecified_table_name AS t
ON t.start_date >= n.m
AND t.start_date < DATEADD(MONTH, 1, n.m)
GROUP BY n.m
ORDER BY [Month];
(If you don't want a row when there were zero activities in a given month, then change LEFT OUTER to INNER.)
select month(start_date) as Month, count(*) as Count
from Activity
where year(start_date) = 2011
group by month(start_date)
order by month(start_date)