Find Difference between end dates by comparing rows - c#

i have a table
----------
User
----------
userID(pk)
startdate // update : i am not using this field.
enddate
i need to compare between the end_dates between the rows comparing whether it is more than 3 days and count the userid.
i am doing something similar to this
WHILE ##FETCH_STATUS = 0
BEGIN
select #lastrowID = max(rowid) from #User
if (#userid = (select userId from #User where rowid = #lastRowID))
begin
update #User set NextEndDate= #endDate where rowid = #lastRowID and userid = #userid
end
else
begin
insert #UserTable (userid, EndDate,NextEndDate) values (#userid, #endDate,#NextEndDate)
end
END
my idea is to loop around the table and create a nextend date and then find the comparing using datediff. i am stuck with the first part of creating nextend date and i believe the 2nd part of comparing would be easy. i am stuck in between.
my question is am i doing the right thing, its getting pretty complicated.
i am confused whether to get the result through sql query or use C# at code behind using Linq or something similar.
Update:
sorry If i wasn't clear in explaining my scenario : i am trying to find the count, no of times a client has visited.
ex:
userid: 1 have may a visited daily or once in a month. so i need to get the count(frequency of the user visit). so if the users visited end date was
userid enddate
1 1/1/2010
1 1/2/2010 count 1
1 1/10/2010 count 2 difference is more than 3 days
1 1/13/2010 count 2 ( because diff is less than 3 days)
thats how i should count, thats y i was trying to use cursor which was too complicate for me to solve. I appreciate for your guidance.

Ok, I understand your problem now. I know there is a better way to do this in SQL, maybe with CTEs, but this solution should work and doesn't use cursors. This will give you a full table with the datediff of the previous enddate (where applicable). You can then select from it based on the datediff.
select u1.*, datediff(day, u2.enddate, u1.enddate) as days from
(
select userid, enddate, row_number() over(partition by userid order by userid, enddate) as rownumber
from [user]
) u1
left join
(
select userid, enddate, row_number() over(partition by userid order by userid, enddate) as rownumber
from [user]
)u2
on u1.userid = u2.userid
and u1.rownumber = u2.rownumber + 1

EDIT
declare #table table (userid int, startdate datetime, enddate datetime)
insert into #table (userid, startdate, enddate) values (1, '01-JAN-2010', '2-JAN-2010')
insert into #table (userid, startdate, enddate) values (2, '01-JAN-2010', '3-JAN-2010')
insert into #table (userid, startdate, enddate) values (3, '01-JAN-2010', '4-JAN-2010')
insert into #table (userid, startdate, enddate) values (4, '01-JAN-2010', '5-JAN-2010')
insert into #table (userid, startdate, enddate) values (5, '01-JAN-2010', '6-JAN-2010')
insert into #table (userid, startdate, enddate) values (6, '01-JAN-2010', '7-JAN-2010')
insert into #table (userid, startdate, enddate) values (7, '01-JAN-2010', '8-JAN-2010')
select SUM(yn) as dueinmorethanthreedays from
(select
(case when DATEADD(day,3,startdate) < enddate then 1 else 0 end) as yn
from #table
) as derived
A subquery which returns 1 for each row where the startdate is less than three days of the enddate (and 0 otherwise) can be summed to get the total.

Related

Counting field by day in mssql

I have a table with Scheduling slots called:
ScheduleSlots
Fields:
id (int)
scheduleID (int)
time (datetime)
availableslots (int)
CalendarGroupID (int)
Level (int)
enabled (bit)
I want to setup a gridview where I take all of the dates and count enabled and disabled for each day.
I am not sure how to go about writing the sql statement to do this.
ie.
Date Enabled Disabled
3/31/2021 20 20
4/1/2021 10 30
SELECT Time, scheduleID,
(SELECT COUNT(Enabled) FROM [dbo].[ScheduleSlots]
WHERE Cast(Time as Date)>='2021-03-31' AND Cast(Time as Date)<='2021-04-01' AND CalendarGroupID=1 AND Level=1 AND Enabled=1) as Enabled,
(SELECT COUNT(Enabled) FROM [dbo].[ScheduleSlots]
WHERE Cast(Time as Date)>='2021-03-31' AND Cast(Time as Date)<='2021-04-01' AND CalendarGroupID=1 AND Level=1 AND Enabled=0) as Disabled
FROM [dbo].[ScheduleSlots]
WHERE Cast(Time as Date)>='2021-03-31' AND Cast(Time as Date)<='2021-04-01' AND CalendarGroupID=1 AND Level=1
GROUP BY scheduleID, Time
The results I end up with:
[Results][1]
You could do it without inner selects:
SELECT [Time],
SUM([Enabled]) as [Enabled],
SUM([Disabled]) as [Disabled]
FROM [dbo].[ScheduleSlots]
WHERE
[Time]>='2021-03-31'
AND [Time]<='2021-04-01'
AND CalendarGroupID=1
AND Level=1
GROUP BY Cast([Time] as Date)
It is an easy grouping query:
SELECT
CONVERT(DATE, "Time") AS "Date",
SUM(CONVERT(INT, "Enabled")) AS "Enabled",
COUNT() - SUM(CONVERT(INT, "Enabled")) AS "Disabled"
FROM "dbo"."ScheduleSlots"
WHERE "CalendarGroupID" = 1
AND "Level" = 1
GROUP BY CONVERT(DATE, "Time")
I hope I understand you correctly where you want the enable slot and diable slot for each day.
;With CTE(Transdate) as (Select Distinct([time]) from ScheduleSlots)
Select CTE.Transdate,(Select COUNT(Id) from ScheduleSlots where enabled= 1
and Transdate = CTE.Transdate) as Enable,
(Select COUNT(Id) from ScheduleSlots where enabled= 0
and Transdate = CTE.Transdate) as Disable from CTE
I inserted some records to make it clear. If I am wrong with inserted data please send me the insert scripts so, that I can test the results.
CREATE TABLE ScheduleSlots
(
id int,
scheduleID int,
[time] datetime,
availableslots int,
CalendarGroupID int,
[Level] int,
[enabled] bit
)
----insert into ScheduleSlots values(1,1,'2021-03-31',40,1,1,20)
INSERT INTO ScheduleSlots VALUES(1,1,'2021-03-31',20,1,1,0)
INSERT INTO ScheduleSlots VALUES(1,1,'2021-03-31',20,1,1,1)
INSERT INTO ScheduleSlots VALUES(1,1,'2021-03-31',20,1,1,1)
INSERT INTO ScheduleSlots VALUES(1,1,'2021-04-01',40,1,1,1)
INSERT INTO ScheduleSlots VALUES(1,1,'2021-04-01',40,1,1,0)
SELECT DISTINCT
[time] AS [Date]
,SUM(availableslots) OVER(PARTITION BY [time] ORDER BY [time]) AS [avaibale]
,SUM(CAST([enabled] AS INT)) OVER(PARTITION BY [time] ORDER BY [time]) AS [Enabled]
,SUM(availableslots) OVER(PARTITION BY [time] ORDER BY [time]) -
SUM(CAST([enabled] AS INT)) OVER(PARTITION BY [time] ORDER BY [time]) AS [Disabled]
FROM ScheduleSlots
Answer I got:

sql query to count data entry of each hour

I want to display the count of data entry of the present day during each hour(1 hour time interval) for a particular line and finally the cumulative of all the hours.
Table details:
CREATE TABLE [dbo].[ProductTable] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[ProductID] INT NOT NULL,
[EmployeeID] INT NOT NULL,
[Operation] VARCHAR (50) NOT NULL,
[Section] NCHAR (10) NOT NULL,
[Line] INT NOT NULL,
[Date] DATETIME DEFAULT (getdate()) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC),
);
I am using Microsoft Visual Studio SQL server
I have written so much but don't know to generalise for every hour
SELECT COUNT(Id) AS Expr1
FROM ProductTable
WHERE (Line = 2)
AND (CONVERT(VARCHAR(10), Date, 105) = CONVERT(VARCHAR(10), GETDATE(), 105))
AND (DATEPART(hour, GETDATE()) BETWEEN 9 AND 10)`
ProductTable data
Error on using "case"
it sounds like you want to group by the hour of the day
SELECT DATEPART(hour, date) as TimeOfDay, COUNT(Id) AS Entries
FROM ProductTable
WHERE (Line = 2) AND cast ([date] as date) =cast (getdate() as date)
GROUP BY DATEPART(hour, date)
To get the cumulative of all hours of the day, use grouping sets
SELECT ISNULL(cast(DATEPART(hour,[date]) as varchar(5)),'Total') as TimeOfDay, COUNT(Id) AS Entries
FROM ProductTable
WHERE (Line = 2) AND cast ([date] as date) =cast (getdate() as date)
GROUP BY GROUPING SETS (DATEPART(hour, [date]) , ())
ORDER BY ISNULL(cast(DATEPART(hour, [date]) as varchar(5)),'Total')
simply use group by clause
SELECT COUNT(Id) AS Expr1
FROM ProductTable
where (Line = 2) AND (CONVERT(Date,[Date])) = CONVERT(DATE,GETDATE())
GROUP BY DATEPART(HOUR, Date)

How to find next (n) open days from SQL opening Hours Database

I am using a SQL database schema similar to the one found on this link.
Best way to store working hours and query it efficiently
I am storing the Opening hours for a location using this basic schema
Shop - INTEGER
DayOfWeek - INTEGER (0-6)
OpenTime - TIME
CloseTime - TIME
What i am trying to do however is for the current DateTime (i.e. today) get the NEXT (n) number of days that the shop is open. So for example if i wasnted to find the next three days that the shop was open and configured in the opening hours the shop is closed on a Sunday and todays date is 21/02/2015 (Saturday) I would like to return the days (21/02/2015)Saturday, (23/02/2015)Monday and (23/02/2015)Tuesday.
If it was Sunday i would return (23/02/2015)Monday, (24/02/2015)Tuesday and (25/02/2015)Wednesday (as its closed on sunday) and finally if it was (20/02/2015)Friday it would return (20/02/2015)Friday, (21/02/2015)Saturday, (23/02/2015)Monday.
I dont know if this is easier to do in SQL or C# but i am mentally struggling in if figuring out how to calculate this.
Any pointers, guidance would be great.
Thank you
This will give you up to 10 days ahead in a fairly efficient way. First test data:
DECLARE #DaysAhead TABLE (
Delta INT
)
INSERT INTO #DaysAhead (Delta)
SELECT 0
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 8
UNION ALL SELECT 9
UNION ALL SELECT 10
DECLARE #Opening TABLE (
Shop INT,
DayOfWk INT,
DayNm varchar(10),
OpenTime TIME,
CloseTime TIME
)
INSERT INTO #Opening (Shop, DayOfWk, DayNm, OpenTime, CloseTime)
SELECT 1, 5, 'Fri', '09:00', '17:00' --
UNION ALL SELECT 1, 6, 'Sat' ,'09:00', '17:00'
--UNION ALL SELECT 0, 'Sun', '09:00', '17:00' -- Not open on Sunday
UNION ALL SELECT 1, 1, 'Mon', '09:00', '17:00'
UNION ALL SELECT 1, 2, 'Tue', '09:00', '17:00'
UNION ALL SELECT 1, 3, 'Wed', '09:00', '17:00'
Which can be queried like this:
DECLARE #dt datetime='21-Feb-2015'
DECLARE #dow int=datepart(dw, #dt)-1
SELECT TOP 3 o.Shop, o.DayOfWk, o.DayNm, o.OpenTime, o.CloseTime FROM (
SELECT Delta, ((#dow+Delta)%7) as DayOfWk
FROM #DaysAhead
) daysAhead
INNER JOIN #Opening o on o.DayOfWk=daysAhead.DayOfWk
ORDER BY daysAhead.Delta
Results:
DECLARE #dt datetime='20-Feb-2015' -- Fri
1 5 Fri 09:00:00.0000000 17:00:00.0000000
1 6 Sat 09:00:00.0000000 17:00:00.0000000
1 1 Mon 09:00:00.0000000 17:00:00.0000000
DECLARE #dt datetime='21-Feb-2015' -- Sat
1 6 Sat 09:00:00.0000000 17:00:00.0000000
1 1 Mon 09:00:00.0000000 17:00:00.0000000
1 2 Tue 09:00:00.0000000 17:00:00.0000000
DECLARE #dt datetime='22-Feb-2015' -- Sun
1 1 Mon 09:00:00.0000000 17:00:00.0000000
1 2 Tue 09:00:00.0000000 17:00:00.0000000
1 3 Wed 09:00:00.0000000 17:00:00.0000000
First you can use a simple query like the following to get the days of the week that the shop is open
Select DayOfWeek
From OpenHours
Where ShopId = #ShopID
This assumes that there will not be entries for days that are not open. Adjust this query if instead the open hour column is null, or less than or equal to the close time for days that are not open.
After you run that query and get the results back and preferably translate them into a List<DayOfWeek> you can do the following in your code.
List<Day0fWeek> openDays = GetOpenDaysFromDB();
DateTime start = DateToStartFrom;
int n = numberOfDays;
List<DateTime> nextNOpenDays = new List<DateTime>();
while(nextNOpenDays.Count < n)
{
if(openDays.Contains(start.DayOfWeek))
nextNOpenDays.Add(start);
start = start.AddDays(1);
}
You can use a case to make a day earlier in this week look like that day next week. Here's an example to look up the next open day:
select top 1 dateadd(day, day_diff, #dt) as dt
from (
select case
when dayofweek <= datepart(dw, #dt) then dayofweek + 7
else dayofweek
end - datepart(dw, #dt) as day_diff
, *
from dbo.OpeningHours
) sub1
order by
day_diff
You can then recurse to find more than one day. If we store the above snippet in a function called get_next_open_day, the recursive common table expression could look like:
; with cte as
(
select dbo.get_next_open_day(#dt) as open_day
, 1 as day_number
union all
select dbo.get_next_open_day(prev_day.open_day)
, prev_day.day_number + 1
from cte as prev_day
where prev_day.day_number < #number_of_days
)
select cte.open_day
, datename(dw, cte.open_day)
from cte
option (maxrecursion 100)
;
Here's a full working example:
use Test
if object_id('OpeningHours') is not null
drop table OpeningHours;
if object_id('dbo.get_next_open_day') is not null
drop function dbo.get_next_open_day;
create table OpeningHours (dayofweek int, opentime time, closetime time);
insert dbo.OpeningHours values
(2, '9:00', '17:00'),
(3, '9:00', '17:00'),
(4, '9:00', '17:00'),
(5, '9:00', '17:00'),
(6, '9:00', '21:00'),
(7, '10:00', '17:00')
;
go
create function dbo.get_next_open_day(
#dt date)
returns date
as begin return
(
select top 1 dateadd(day, day_diff, #dt) as dt
from (
select case
when dayofweek <= datepart(dw, #dt) then dayofweek + 7
else dayofweek
end - datepart(dw, #dt) as day_diff
, *
from dbo.OpeningHours
) sub1
order by
day_diff
)
end
go
--declare #dt date = '2015-02-18' -- Wed
--declare #dt date = '2015-02-20' -- Fri
declare #dt date = '2015-02-22' -- Sun
declare #number_of_days int = 10
; with cte as
(
select dbo.get_next_open_day(#dt) as open_day
, 1 as day_number
union all
select dbo.get_next_open_day(prev_day.open_day)
, prev_day.day_number + 1
from cte as prev_day
where prev_day.day_number < #number_of_days
)
select cte.open_day
, datename(dw, cte.open_day)
from cte
option (maxrecursion 100)
;
The implementation of multiple shops is left as an exercise for the reader.
Try this:
DECLARE #t TABLE(WeekID INT, OpenTime time)
DECLARE #c INT = 10
INSERT INTO #t VALUES
(1, '10:00'),--sunday
(2, '10:00'),--monday
(4, '10:00'),--wednsday
(5, '10:00')--thursday
;WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
SELECT TOP (#c) DATEADD(dd, t.n, GETDATE())
FROM Tally t
JOIN #t s ON DATEPART(w, DATEADD(dd, t.n, GETDATE())) = s.WeekID
Output:
Date
2015-02-22 --sunday
2015-02-23 --monday
2015-02-25 --wednsday
2015-02-26 --thursday
2015-03-01 --sunday
2015-03-02 --monday
2015-03-04 --wednsday
2015-03-05 --thursday
2015-03-08 --sunday
2015-03-09 --monday
PS: You can replace GETDATE() with any date to look from.
I managed to find a solution:
public List<DateTime> getDaysOpen(int numberOfDays, DateTime start)
{
List<byte> openDays = this.getOpeningHoursDays();
List<DateTime> nextNOpenDays = new List<DateTime>();
while (nextNOpenDays.Count < numberOfDays)
{
if (openDays.Contains(Convert.ToByte(start.DayOfWeek)))
nextNOpenDays.Add(start);
start = start.AddDays(1);
}
return nextNOpenDays;
}
public List<byte> getOpeningHoursDays()
{
return db.OpeningHours.Where(oh => oh.LocationId == this.Id).Select(oh => oh.DateOfWeek).ToList();
}
This was in my opinion the easiest method to find a solution. Thank you for all your help.

How do I find week number of a date according to DATEFIRST

How to find week number of a date if start day is Tuesday and end day is Monday?
Here is the example criteria
Date Output
...... ..........
01-Dec-2014 (Monday) - 2014_DECEMBER_WEEK_NO_1
04-Dec-2014 (Thursday) - 2014_DECEMBER_WEEK_NO_1
29-Dec-2014 (Monday) - 2014_DECEMBER_WEEK_NO_4
30-Dec-2014 (Tuesday) - 2014_DECEMBER_WEEK_NO_5
31-Dec-2014 (Tuesday) - 2014_DECEMBER_WEEK_NO_5
I tried following query:
select CAST(UPPER(DATENAME(YEAR, #FROMDATE)) AS VARCHAR(20))
+'_'+CAST(UPPER(DATENAME(MONTH, #FROMDATE)) AS VARCHAR(20))
+'_WEEK_NO_'+CAST((DAY(#FROMDATE)
+ (DATEPART(DW, DATEADD (MONTH, DATEDIFF (MONTH, 0, #FROMDATE), 0))-1) -1)/7 + 1 AS VARCHAR(10))
This working fine if start day is Sunday and end day is Saturday.
Set your first day to Tuesday
SET DATEFIRST 2
Execute the following query
DECLARE #FROMDATE DATE='31-Dec-2014'
SELECT CAST(UPPER(DATENAME(YEAR, #FROMDATE)) AS VARCHAR(20))
+'_'+CAST(UPPER(DATENAME(MONTH, #FROMDATE)) AS VARCHAR(20))
+'_WEEK_NO_'+REPLACE(CAST((DAY(#FROMDATE)
+ (DATEPART(DW, DATEADD (MONTH, DATEDIFF (MONTH, 0, #FROMDATE), 0))-1) -1)/7 AS VARCHAR(10)),'0','1')
UPDATE
SET DATEFIRST 2
DECLARE #FROMDATE DATE='12-JAN-2015'
DECLARE #ALLDATE DATE=DATEADD(month, DATEDIFF(month, 0, #FROMDATE), 0)
DECLARE #FIRSTDATE DATE
; WITH CTE as
(
SELECT 1 RNO,CAST(#ALLDATE AS DATE) as DATES
UNION ALL
SELECT RNO+1, DATEADD(DAY,1,DATES )
FROM CTE
WHERE DATES < DATEADD(MONTH,1,#ALLDATE)
)
SELECT TOP 1 #FIRSTDATE = DATES
FROM CTE
WHERE DATEPART(W,DATES)=1
SELECT CAST(UPPER(DATENAME(YEAR, #FROMDATE)) AS VARCHAR(20))
+'_'+CAST(UPPER(DATENAME(MONTH, #FROMDATE)) AS VARCHAR(20))
+'_WEEK_NO_'+CAST((DATEDIFF(DAY,#FIRSTDATE,#FROMDATE)/7)+1 AS VARCHAR(10))
set datefirst 2
DECLARE #StartDate DATE = '2014-12-01',
#EndDate DATE = '2014-12-31',
#DayCount int
SELECT #DayCount = count(* )
FROM (SELECT TOP ( datediff(DAY,#StartDate,#EndDate) + 1 )
[Date] = dateadd(DAY,ROW_NUMBER()
OVER(ORDER BY c1.name),
DATEADD(DD,-1,#StartDate))
FROM [master].[dbo].[spt_values] c1 ) x
WHERE datepart(dw,[Date]) = 1;
select CAST(UPPER(DATENAME(YEAR, #EndDate)) AS VARCHAR(20))
+'_'+CAST(UPPER(DATENAME(MONTH, #EndDate)) AS VARCHAR(20))
+'_WEEK_NO_'+ Cast(#DayCount as varchar)

SQL Insert into two tables

I am using the following SQL statement to insert new project iterations to the Iterations table...I can insert many iterations at once. The Iterations table has the folllowing fields: {ProjectIteratationID(PK), ProjectID, StartDate, EndDate}
INSERT INTO Iterations (ProjectID, StartDate, EndDate) VALUES (...)
What I also want to do is assign people to the iterations that I am adding, so I need to insert into the ProjectIterationMember table.
"INSERT INTO ProjectIterationMember (ProjectIterationID, MemberID) VALUES ((SELECT ProjectIterationID AS pro_it_id FROM Iterations WHERE ProjectID = '" + proj_id + "'), #member_id)";
I am getting an error. My nested select statement retrieves more than one result.
For example, if I am adding two iterations, the PK 13 and 14 will be generated. I then want to copy the PK to the ProjectIterationMember table and assign a few MemberIDs to those iteration IDs. Thanks for your help!
Use:
INSERT INTO ProjectIterationMember
(ProjectIterationID, MemberID)
SELECT ProjectIterationID AS pro_it_id, #member_id
FROM Iterations
WHERE ProjectID = '" + proj_id + "'
SQL allows you to provide statically assigned values in the SELECT clause.
If you use insert in combination with select you have to omit the values keyword.
Simply use the select after the column list.
Use SCOPE_IDENTITY to get last identity value.
DECLARE #iterationID int;
INSERT INTO Iterations (ProjectID, StartDate, EndDate)
VALUES (#projectID, #startDate, #endDate);
SET #iterationID = SCOPE_IDENTITY();
INSERT INTO ProjectIterationMember (ProjectIterationID, MemberID)
VALUES (#iterationID, #memberID);

Categories

Resources