Most efficient way to get booking data for time period? - sql-server

I have a booking system that allows a user to book places for 30 min timeslots (e.g. 1pm, 1:30pm, 2pm etc...)
In the sql database I may have one booking for 10am, a booking for 1pm and two for 2pm. I am trying to display a view of all 30 min booking slots in between a date time range displaying number of current bookings for each slot.
I am not storing each slot explicitly as it's not very efficient. Is there a way to make sql return 'empty' timeslots in a single query? I don't want to create a timeslot array then query each timeslot individually for the total count of bookings.
I am using sql server and asp.net mvc6 as my technology base. Some suggestions on technique would be appreciated.
Thanks.

you need to build a 30 minute interval time range table and do left join with your table to get all time slots
This query generates 30 minute interval times starting from startDate , total 12 time slots are generated, you can modify it accordingly.
declare #startDate datetime ='2014-01-12 12:00:00'
;with cte(value,nextval,n)
as
(
select CONVERT(VARCHAR(5),#startDate,108) as value,
dateadd(minute, datediff(minute, 0, #startDate)+30, 0) as nextval, 1 as n
union all
select CONVERT(VARCHAR(5),cte.nextval,108) as value,
dateadd(minute, datediff(minute, 0, cte.nextval)+30, 0) as nextval, n+1
from cte
where n <=12
)
select * from cte
left join Table1
on cte.nextval = Table1.timeslotvalue

Related

Count by days, with all days

I need to count records by days, even if in the day were no records.
Count by days, sure, easy.
But how i can make it to print information, that 'in day 2018-01-10 was 0 records)
Should I use connect by level? Please, any help would be good. Can't use plsql, just oracle sql
First you generate every date that you want in an inline view. I chose every date for the current year because you didn't specify. Then you left outer join on date using whichever date field you have in that table. If you count on a non-null field from the source table then it will count 0 rows on days where there is no join.
select Dates.r, count(tablename.id)
from (select trunc(sysdate,'YYYY') + level - 1 R
from dual
connect by level <= trunc(add_months(sysdate,12),'YYYY') - trunc(sysdate,'YYYY')) Dates
left join tablename
on trunc(tablename.datefield) = Dates.r
group by Dates.r

Running concurrent /parallel update statements (T-SQL)

I have a table that is basically records of items, with columns for each day of the month. So basically each row is ITEM , Day1, Day2, Day3, ....I have to run update statements that basically trawl through each row day by day with the current day information requiring some info from the previous day.
Basically, we have required daily quantities. Because the order goes out in boxes (which are a fixed size) and the calculated quantities are in pieces, the system has to calculate the next largest number of boxes. Any "extra quantity" is carried over to the next day to reduce boxes.
For example, for ONE of those records in the table described earlier (the box size is 100)
My current code is basically getting the record, calculate the requirements for that day, increment by one and repeat. I have to do this for each record. It's very inefficient especially since it's being run sequentially for each record.
Is there anyway to parallel-ize this on SQL Server Standard? I'm thinking of something like a buffer where I will submit each row as a job and the system basically manages the resources and runs the query
If the buffer idea is not feasible, is there anyway to 'chunk' these rows and run the chunks in parallel?
Not sure if this helps, but I played around with your data and was able to calculate the figures without row-by-row handling as such. I transposed the figures with unpivot and calculated the values using running total + lag, so this requires SQL Server 2012 or newer:
declare #BOX int = 100
; with C1 as (
SELECT
Day, Quantity
FROM
(SELECT * from Table1 where Type = 'Quantity') T1
UNPIVOT
(Quantity FOR Day IN (Day1, Day2, Day3, Day4)) AS up
),
C2 as (
select Day, Quantity,
sum(ceiling(convert(numeric(5,2), Quantity) / #BOX) * #BOX - Quantity)
over (order by Day asc) % #BOX as Extra
from C1
),
C3 as (
select
Day, Quantity,
Quantity - isnull(Lag(Extra) over (order by Day asc),0) as Required,
Extra
from C2
)
select
Day, Quantity, Required,
ceiling(convert(numeric(5,2), Required) / #BOX) as Boxes, Extra
from C3
Example in SQL Fiddle

Querying a running-percentage over a date range from MSSQL?

I want to graph the % of users over time that have their Twitter account connected. The number of users changes constantly, and so does the % of them that connect their Twitter account.
The table has a user account specific createDateTime column as well as a tw_connectDateTime column.
Let's say I'm interested in the trend of % connected over the last 7 days. Is there a way I can have MSSQL calculate the percentage for every day in the specified range, or do I need to do it myself using multiple queries?
Doing it in app logic would look something like (pseudocode):
for day in days:
query:
select
count(userId) as totalUsers
,c.connected
,cast(c.connected as float)/count(userId) as percentage
from
Users
outer apply (
select
count(userId) as connected
from
Users
where
tw_connectDateTime <= $day
) as c
where
createDateTime <= $day
group by
c.connected
What I'm unsure of is how, if it's possible, to expand this to run for each day, so that the results include the date as a column and the same values that I would get from running the above query for each date in the range.
Is it possible? If so, how?
actually you can use your query joined with days, like this:
with cte_days as (
select #DateStart as day
union all
select dateadd(dd, 1, c.[day]) as [day]
from cte_days as c
where c.[day] < #DateEnd
)
select
d.[day],
count(u.userId) as totalUsers,
c.connected,
cast(c.connected as float)/count(u.userId) as percentage
from cte_days as d
inner join Users as u on u.createDateTime <= d.[day]
outer apply (
select
count(T.userId) as connected
from Users as T
where T.tw_connectDateTime <= d.[day]
) as c
group by d.[day], c.connected

SQL Server Retrieving Recurring Appointments By Date

I'm working on a system to store appointments and recurring appointments. My schema looks like this
Appointment
-----------
ID
Start
End
Title
RecurringType
RecurringEnd
RecurringTypes
---------------
Id
Name
I've keeped the Recurring Types simple and only support
Week Days,
Weekly,
4 Weekly,
52 Weekly
If RecurringType is null then that appointment does not recur, RecurringEnd is also nullable and if its null but RecurringType is a value then it will recur indefinatly. I'm trying to write a stored procedure to return all appointments and their dates for a given date range.
I've got the stored procedure working for non recurring meetings but am struggling to work out the best way to return the recurrences this is what I have so far
ALTER PROCEDURE GetAppointments
(
#StartDate DATETIME,
#EndDate DATETIME
)
AS
SELECT
appointment.id,
appointment.title,
appointment.recurringType,
appointment.recurringEnd,
appointment.start,
appointment.[end]
FROM
mrm_booking
WHERE
(
Start >= #StartDate AND
[End] <= #EndDate
)
I now need to add in the where clauses to also pick up the recurrences and alter what is returned in the select to return the Start and End Dates for normal meetings and the calculated start/end dates for the recurrences.
Any pointers on the best way to handle this would be great. I'm using SQL Server 2005
you need to store the recurring dates as each individual row in the schedule. that is, you need to expand the recurring dates on the initial save. Without doing this it is impossible to (or extremely difficult) to expand them on the fly when you need to see them, check for conflicts, etc. this will make all appointments work the same, since they will all actually have a row in the table to load, etc. I would suggest that when a user specifies their recurring date, you make them pick an actual number of recurring occurrences. When you go to save that recurring appointment, expand them all out as individual rows in the table. You could use a FK to a parent appointment row and link them like a linked list:
Appointment
-----------
ID
Start
End
Title
RecurringParentID FK to ID
sample data:
ID .... RecurringParentID
1 .... null
2 .... 1
3 .... 2
4 .... 3
5 .... 4
if in the middle of the recurring appointments schedule run, say ID=3, they decide to cancel them, you can follow the chain and delete the remaining ID=3,4,5.
as for expanding the dates, you could use a CTE, numbers table, while loop, etc. if you need help doing that, just ask. the key is to save them as regular rows in the table so you don't need to expand them on the fly every time you need to display or evaluate them.
I ended up doing this by creating a temp table of everyday between the start and end date along with their respective day of the week. I limited the recurrence intervals to weekdays and a set amount of weeks and added where clauses like this
--Check Week Days Reoccurrence
(
mrm_booking.repeat_type_id = 1 AND
#ValidWeeklyDayOfWeeks.dow IN (1,2,3,4,5)
) OR
--Check Weekly Reoccurrence
(
mrm_booking.repeat_type_id = 2 AND
DATEPART(WEEKDAY, mrm_booking.start_date) = #ValidWeeklyDayOfWeeks.dow
) OR
--Check 4 Weekly Reoccurences
(
mrm_booking.repeat_type_id = 3 AND
DATEDIFF(d,#ValidWeeklyDayOfWeeks.[Date],mrm_booking.start_date) % (7*4) = 0
) OR
--Check 52 Weekly Reoccurences
(
mrm_booking.repeat_type_id = 4 AND
DATEDIFF(d,#ValidWeeklyDayOfWeeks.[Date],mrm_booking.start_date) % (7*52) = 0
)
In case your interested I built up a table of the days between the start and end date using this
INSERT INTO #ValidWeeklyDayOfWeeks
--Get Valid Reoccurence Dates For Week Day Reoccurences
SELECT
DATEADD(d, offset - 1, #StartDate) AS [Date],
DATEPART(WEEKDAY,DATEADD(d, offset - 1, #StartDate)) AS Dow
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY s1.id) AS offset
FROM syscolumns s1, syscolumns s2
) a WHERE offset <= DATEDIFF(d, #StartDate, DATEADD(d,1,#EndDate))
Its not very elegant and probably very specific to my needs but it does the job I needed it to do.

Count number of 'overlapping' rows in SQL Server

I've been asked to look at a database that records user login and logout activity - there's a column for login time and then another column to record logout, both in OLE format. I need to pull together some information about user concurrency - i.e. how many users were logged in at the same time each day.
Do anyone know how to do this in SQL? I don't really need to know the detail, just the count per day.
Thanks in advance.
Easiest way is to make a times_table from an auxiliary numbers table (by adding from 0 to 24 * 60 minutes to the base time) to get every time in a certain 24-hour period:
SELECT MAX(simul) FROM (
SELECT test_time
,COUNT(*) AS simul
FROM your_login_table
INNER JOIN times_table -- a table/view/subquery of all times during the day
ON your_login_table.login_time <= times_table.test_time AND times_table.test_time <= your_login_table.logout_time
GROUP BY test_time
) AS simul_users (test_time, simul)
I think this will work.
Select C.Day, Max(C.Concurrency) as MostConcurrentUsersByDay
FROM
(
SELECT convert(varchar(10),L1.StartTime,101) as day, count(*) as Concurrency
FROM login_table L1
INNER JOIN login_table L2
ON (L2.StartTime>=L1.StartTime AND L2.StartTime<=L1.EndTime) OR
(L2.EndTime>=L1.StartTime AND L2.EndTime<=L1.EndTime)
WHERE (L1.EndTime is not null) and L2.EndTime Is not null) AND (L1.ID<>L2.ID)
GROUP BY convert(varchar(10),L1.StartTime,101)
) as C
Group BY C.Day
Unchecked... but lose date values, count time between, use "end of day" for still logged in.
This assumes "logintime" is a date and a time. If not, the derived table can be removed (Still need ISNULL though). of course, SQL Server 2008 has "time" to make this easier too.
SELECT
COUNT(*)
FROM
(
SELECT
DATEADD(day, DATEDIFF(day, logintime, 0), logintime) AS inTimeOnly,
ISNULL(DATEADD(day, DATEDIFF(day, logouttime, 0), logintime), '1900-01-01 23:59:59.997') AS outTimeOnly
FROM
mytable
) foo
WHERE
inTimeOnly >= #TheTimeOnly AND outTimeOnly <= #TheTimeOnly

Resources