Related
I want to compare current year WTD and MTD with Last year WTD and MTD. How can I arrive last year WTD and MTD?.
Example :
If Current Year WTD is 06-04-2020 to 08-04-2020 then I have to compare last year same period like 06-04-2019 to 08-04-2019. The same way MTD also to be compared. Like given below. I need to check any WTD data without giving from date and to date.
Any one help?
Here is an example query that compares a current period to the same period in the previous year, using dateadd:
select
count(case when sale_dt >= dateadd(y,-1,'20200406') and sale_dt < dateadd(y,-1,'20200413') then sale_dt end) prev_year_week
, count(case when sale_dt >= '20200406' and sale_dt < '20200413' then sale_dt end) this_year_week
from Mytable
where sale_dt >= '20200406' and sale_dt < '20200413'
or sale_dt >= dateadd(y,-1,'20200406') and sale_dt < dateadd(y,-1,'20200413')
here is some sample data:
CREATE TABLE [myTable] (
[myTableID] INTEGER NOT NULL IDENTITY(1, 1),
[sale_dt] datetime,
[name] VARCHAR(255) NULL,
PRIMARY KEY ([myTableID])
);
INSERT INTO myTable([sale_dt],[name]) VALUES('2019-03-26 10:46:33','Oleg'),('2019-03-16 13:50:51','Brett'),('2019-04-05 19:35:43','Lester'),('2019-04-04 19:50:00','Kamal'),('2019-04-07 14:31:08','Finn'),('2019-03-04 22:54:04','Lewis'),('2019-03-27 10:54:45','Paki'),('2019-04-09 01:42:52','Louis'),('2019-04-10 11:19:24','Howard'),('2019-03-16 08:48:36','Felix');
INSERT INTO myTable([sale_dt],[name]) VALUES('2019-04-08 06:25:04','Addison'),('2019-04-04 09:10:06','Armando'),('2019-03-03 23:18:07','Dylan'),('2019-04-16 04:24:12','Austin'),('2019-03-19 13:46:05','Beau'),('2019-03-19 11:31:25','Boris'),('2019-04-19 09:20:29','Berk'),('2019-03-26 17:01:23','Kasper'),('2019-03-23 05:01:06','Hamilton'),('2019-04-06 17:23:07','Channing');
INSERT INTO myTable([sale_dt],[name]) VALUES('2019-03-14 08:38:33','Nasim'),('2019-04-06 12:42:27','Randall'),('2019-03-31 23:33:57','Kasimir'),('2019-04-07 22:11:27','Aquila'),('2019-04-15 21:06:57','Zeph'),('2019-03-12 08:00:01','Salvador'),('2019-04-19 19:12:09','Knox'),('2019-04-04 01:28:42','Chaim'),('2019-03-06 13:40:22','Orlando'),('2019-03-03 19:04:59','Quinlan');
INSERT INTO myTable([sale_dt],[name]) VALUES('2019-04-09 05:06:31','Jin'),('2019-03-03 18:32:56','Nero'),('2019-03-08 17:51:44','Linus'),('2019-04-17 17:40:56','Levi'),('2019-03-13 08:30:53','Dieter'),('2019-03-29 03:42:42','Nehru'),('2019-03-15 17:53:09','Harlan'),('2019-04-13 04:45:21','Ralph'),('2019-03-07 04:21:01','Tobias'),('2019-04-02 19:32:57','Derek');
INSERT INTO myTable([sale_dt],[name]) VALUES('2020-04-04 15:58:48','Holmes'),('2020-04-04 02:49:20','Len'),('2020-04-05 19:56:04','Yasir'),('2020-04-12 09:43:57','Quamar'),('2020-04-12 14:14:29','Dante'),('2020-04-11 08:04:20','Baxter'),('2020-03-31 22:30:12','Odysseus'),('2020-04-03 11:54:33','Levi'),('2020-04-10 21:36:42','Brian'),('2020-04-07 00:18:22','Sawyer');
INSERT INTO myTable([sale_dt],[name]) VALUES('2020-04-08 15:42:30','Hunter'),('2020-04-06 17:50:25','Kenyon'),('2020-03-27 03:20:14','Axel'),('2020-03-31 19:54:39','Trevor'),('2020-03-27 09:10:22','Griffin'),('2020-04-05 18:32:54','Gavin'),('2020-04-09 17:46:47','Oliver'),('2020-04-09 13:52:08','Nasim'),('2020-04-03 09:44:41','Zeus'),('2020-04-01 07:09:49','Kirk');
INSERT INTO myTable([sale_dt],[name]) VALUES('2020-04-01 20:02:11','Benjamin'),('2020-04-06 08:10:22','Rooney'),('2020-04-09 20:56:28','Hunter'),('2020-03-31 09:32:44','Jared'),('2020-04-01 22:20:06','Caesar'),('2020-03-27 18:33:16','Rooney'),('2020-04-12 02:37:09','Brock'),('2020-03-28 12:04:46','Channing'),('2020-04-12 23:41:21','Amery'),('2020-03-25 22:41:15','Brent');
Run the query above over that data results in:
prev_year_week this_year_week
11 13
But do note the day of week for 2020-04-06 (Mon) and 2019-04-06 (Sat) aren't the same.
see: https://rextester.com/LMRC80411
data generated by: https://www.generatedata.com/
I'm struggling to write a SQL query that will identify customers who have taken out multiple loans in a short period of time (say 60 days).
MyTable
CREATE TABLE [dbo].[MyTable](
[Customerid] [numeric](18, 0) NOT NULL,
[Date Open] [date] NOT NULL,
[Date Closed] [date] NOT NULL,
[Rank] [varchar](50) NOT NULL
) ON [PRIMARY]
E.g. - Customerid 1 has taken multiple loans with date open say 12/01/2017, 12/02/2017, 13/04/2018 etc. I need to find the customers who have taken next loan (Date Open) within 60 days.
It looks like you can do this with a simple INNER JOIN. I'm not entirely sure what Rank indicates in your table, but something like below should work if they can't take out two loans on the same day.
SELECT DISTINCT yt1.Customerid
FROM yourtable yt1
INNER JOIN yourtable yt2 ON yt2.Customerid = yt1.Customerid AND
yt2.[Date Open] > yt1.[Date Open] AND
yt2.[Date Open] <= DATEADD(DAY, 60, yt1.[Date Open])
I have a table like
CREATE TABLE [dbo].[Log](
[Id] [BIGINT] IDENTITY(1,1) NOT NULL,
[GuidId] [UNIQUEIDENTIFIER] NOT NULL,
[TableName] [VARCHAR](50) NULL,
[CreatedDate] [DATETIME] NULL,
[Operation] [VARCHAR](50) NULL,
[Status] [VARCHAR](50) NULL,
[UserId] [VARCHAR](50) NOT NULL)
Am trying to get query like
SELECT TOP 25 UserId, TableName, Operation, COUNT(1) Records
FROM dbo.Log
WHERE CreatedDate > GETDATE() - 1
AND Status='failed'
GROUP BY UserId, TableName,Operation
I need to add another column to have output of count that has GetDate() - 7 criteria too in the same select.
Share some thoughts
Something like this?
SELECT TOP 25
UserId
, TableName
, Operation
, SUM(CASE WHEN CreatedDate > GETDATE() - 1 THEN 1 ELSE 0 END) Records1Day
, COUNT(1) Records7Days
FROM dbo.Log
WHERE
CreatedDate > GETDATE() - 7
AND Status='failed'
GROUP BY
UserId
, TableName
, Operation
If I understand correctly, you want the most recent UserID fails that had a MAX(CreatedDate) > yesterday. The subquery dT, below does this using MAX(CreatedDate) for the user ID, and HAVING to filter. This is Ordered in descending faildate order. I subqueried because you added the condition that you want to know the date for 7 days prior to this, and the receiving query does this... using DATEADD to subtract 7 days from the [Most Recent UserID Failure] date.
SELECT dT.UserId
,dT.[Most Recent UserID Failure]
,DATEADD(DAY, -7, dT.[Most Recent UserID Failure]) AS [7 Days ago]
,(SELECT COUNT (*)
FROM #Log
WHERE UserId = dT.UserID
AND CreatedDate >= DATEADD(DAY, -7, CAST(dT.[Most Recent UserID Failure] as date))
AND CreatedDate <= dT.[Most Recent UserID Failure]
AND Status = 'failed'
) AS [Num Fails]
FROM (
SELECT TOP 25
UserID
,MAX(CreatedDate) AS [Most Recent UserID Failure]
FROM Log
WHERE Status = 'failed'
GROUP BY UserID
HAVING MAX(CreatedDate) > GETDATE() - 1
ORDER BY [Most Recent UserID Failure] DESC
) AS dT
I am way over my head with an SQL problem. I have a query which makes a temporary table, fills it with data from several other tables, makes some calculations and updates and provides this data to an app. The final step to do is calculate how many hours and how many minutes there are between two datetimes, but they should be divided in dayHours, dayMins, nightHours, nightMins (datetimes can be 20+ days in between). The following bulletpoints will visualize what I want to do:
Let say, night time is from 23:00 to 06:00.
We have DateTime1 = 20-04-2016 13:30.
We have DateTime2 = 21-04-2016 07:15.
NightTime: from 23:00 to 06:00 = 7 hours 0 minutes.
DayTime: from 13:30 to 23:00 (9h30m), and then again from 06:00 to 07:15(1h15m) the following day for a total of 10 hours 45 minutes.
I am providing a create table query, but I only need help with the calculation so you could ignore my table and data. Note, I have erased almost all formatting to reduce, as the post got really long.
CREATE TABLE [dbo].[myTestTable](
[JHID] [int] NULL, [ToDateTime] [datetime] NULL,
[startPayDateTime] [datetime] NULL, [opDayHour] [int] NULL,
[opDayMin] [int] NULL, [opNightHour] [int] NULL,
[opNightMin] [int] NULL, ) ON [PRIMARY] GO
Consider inserting this as a test data. The columns (for test purposes) are startPayDateTime and ToDateTime
INSERT INTO [myTestTable]
([JHID],[ToDateTime],[startPayDateTime],[opDayHour],[opDayMin],[opNightHour],[opNightMin])
VALUES (301533,'14-03-2016 01:54','14-03-2016 04:54',1,1,1,1),
(302488,'14-03-2016 01:54','14-03-2016 08:31',0,0,0,0),
(302676,'14-03-2016 01:54','28-03-2016 08:11',1,1,1,1) GO
So now I have to
UPDATE
SET opDayHour = (CASE WHEN ... THEN *value* ELSE 0 end),
opDayMin = (CASE WHEN ... THEN *value* ELSE 0 end),
opNightHour = (CASE WHEN ... THEN *value* ELSE 0 end),
opNightMin = (CASE WHEN ... THEN *value* ELSE 0 end),
How do I Thank you for your consideration, if my question is not clear enough leave a comment ! :)
The idea is to detect first day, a number of hole days (if present) and the last day (if not the same as first). So we need only one day long tally table for minutes. Drawback is more computations of those first/last intervals. When you need computations wich involve a number of intermidiate variables CROSS APPLY is a handy tool.
Try this, you may need to ajust +-1 logic to conform to your rules. This query computes minutes which can be easily converted to hours + minutes.
with myMinutes as (
select rn
-- day time is from 6:00 to 23:00
, mday = case when rn between 6*60 and 23*60-1 then 1 else 0 end
, mnight = 1 - case when rn between 6*60 and 23*60-1 then 1 else 0 end
from (select top(24*60) rn=row_number () over (order by (select null))
from sys.all_objects s1, sys.all_objects s2) t
)
select dayMinutes=r1.dayMin + case holedays when 0 then 0 else r2.dayMin + (holedays-1)*(23*60 - 6*60) end
, nightMinutes=r1.nightMin + case holedays when 0 then 0 else r2.nightMin + (holedays-1)*(24*60 -(23*60 - 6*60)) end
, totalMinutes= datediff(MINUTE, [FromDateTime], [ToDateTime]) -- control
,[JHID],[JetReg],[ArrFltID],[DepFltID],[ArrDateTime],[FromDateTime],[ToDateTime]
-- more columns sipped
from [myTestTable]
cross apply (select fD = dateadd(DAY,datediff(DAY,'19000101',[FromDateTime]),'19000101')
,tD = dateadd(DAY,datediff(DAY,'19000101',[ToDateTime]),'19000101')
,holedays = datediff(DAY,[FromDateTime],[ToDateTime]) ) xd
cross apply (select fFirstMin = datediff(MINUTE, fd, [FromDateTime])
,fLastMin = case holedays when 0 then datediff(MINUTE, td,[ToDateTime]) else 24*60 end - 1
,tFirstMin = 1
,tLastMin = datediff(MINUTE, td, [ToDateTime])
) xb
cross apply (select dayMin = sum(mm.mday)
, nightMin = sum(mm.mnight)
from myminutes mm
where mm.rn between fFirstMin and fLastMin ) r1
cross apply (select dayMin = sum(mm.mday)
, nightMin = sum(mm.mnight)
from myminutes mm
where mm.rn between tFirstMin and tLastMin ) r2
You can use cte for that count:
DECLARE
#DateTime1 datetime = '2016-04-20 13:30',
#DateTime2 datetime = '2016-04-21 07:15'
;WITH times AS(
SELECT #DateTime1 as d,
CASE WHEN DATEPART(hour,#DateTime1) between 6 and 22 then 'd' else 'n' end as a,
0 as m
UNION ALL
SELECT DATEADD(minute,1,d),
CASE WHEN DATEPART(hour,DATEADD(minute,1,d)) between 6 and 22 then 'd' else 'n' end as a,
DATEDIFF(minute,d,DATEADD(minute,1,d))
FROM times
WHERE DATEADD(minute,1,d) <= #DateTime2
)
SELECT CASE WHEN a = 'd' THEN 'DayTime' ELSE 'NightTime' END as TimePart,
sum(m)/60 as H,
sum(m) - (sum(m)/60)* 60 as M
FROM times
GROUP BY a
OPTION (MAXRECURSION 0)
Output be like:
TimePart H M
--------- ----------- -----------
DayTime 10 45
NightTime 7 0
(2 row(s) affected)
I'm working on a query for a rehab organization where tenants (client/patients) live in a building when they first arrive, as they progress in their treatment they move to another building and as they near the end of treatment they are in a third building.
For funding purposes we need to know how many nights a tenant spent in each building in each month.
I can use DateDiff to get the total number of nights, but how do I get the total for each client in each month in each building?
For example, John Smith is in Building A 9/12-11/3; moves to Building B 11/3-15; moves to Building C on and is still there: 11/15 - today
What query returns a result that show the number of nights he spent in:
Building A in Septmeber, October and November.
Buidling B in November
Building C in November
Two tables hold the client's name, building name and move-in date and move-out date
CREATE TABLE [dbo].[clients](
[ID] [nvarchar](50) NULL,
[First_Name] [nvarchar](100) NULL,
[Last_Name] [nvarchar](100) NULL
) ON [PRIMARY]
--populate w/ two records
insert into clients (ID,First_name, Last_name)
values ('A2938', 'John', 'Smith')
insert into clients (ID,First_name, Last_name)
values ('A1398', 'Mary', 'Jones')
CREATE TABLE [dbo].[Buildings](
[ID_U] [nvarchar](50) NULL,
[Move_in_Date_Building_A] [datetime] NULL,
[Move_out_Date_Building_A] [datetime] NULL,
[Move_in_Date_Building_B] [datetime] NULL,
[Move_out_Date_Building_B] [datetime] NULL,
[Move_in_Date_Building_C] [datetime] NULL,
[Move_out_Date_Building_C] [datetime] NULL,
[Building_A] [nvarchar](50) NULL,
[Building_B] [nvarchar](50) NULL,
[Building_C] [nvarchar](50) NULL
) ON [PRIMARY]
-- Populate the tables with two records
insert into buildings (ID_U,Move_in_Date_Building_A,Move_out_Date_Building_A, Move_in_Date_Building_B,
Move_out_Date_Building_B, Move_in_Date_Building_C, Building_A, Building_B, Building_C)
VALUES ('A2938','2010-9-12', '2010-11-3','2010-11-3','2010-11-15', '2010-11-15', 'Kalgan', 'Rufus','Waylon')
insert into buildings (ID_U,Move_in_Date_Building_A,Building_A)
VALUES ('A1398','2010-10-6', 'Kalgan')
Thanks for your help.
I'd use a properly normalized database schema, your Buildings table is not useful like this. After splitting it up I believe that getting your answer will be pretty easy.
Edit (and updated): Here's a CTE which will take this strange table structure and split it into a more normalized form, displaying the user id, building name, move in and move out dates. By grouping on the ones you want (and using DATEPART() etc.) you should be able to get the data you need with that.
WITH User_Stays AS (
SELECT
ID_U,
Building_A Building,
Move_in_Date_Building_A Move_In,
COALESCE(Move_out_Date_Building_A, CASE WHEN ((Move_in_Date_Building_B IS NULL) OR (Move_in_Date_Building_C<Move_in_Date_Building_B)) AND (Move_in_Date_Building_C>Move_in_Date_Building_A) THEN Move_in_Date_Building_C WHEN Move_in_Date_Building_B>=Move_in_Date_Building_A THEN Move_in_Date_Building_B END, GETDATE()) Move_Out
FROM dbo.Buildings
WHERE Move_in_Date_Building_A IS NOT NULL
UNION ALL
SELECT
ID_U,
Building_B,
Move_in_Date_Building_B,
COALESCE(Move_out_Date_Building_B, CASE WHEN ((Move_in_Date_Building_A IS NULL) OR (Move_in_Date_Building_C<Move_in_Date_Building_A)) AND (Move_in_Date_Building_C>Move_in_Date_Building_B) THEN Move_in_Date_Building_C WHEN Move_in_Date_Building_A>=Move_in_Date_Building_B THEN Move_in_Date_Building_A END, GETDATE())
FROM dbo.Buildings
WHERE Move_in_Date_Building_B IS NOT NULL
UNION ALL
SELECT
ID_U,
Building_C,
Move_in_Date_Building_C,
COALESCE(Move_out_Date_Building_C, CASE WHEN ((Move_in_Date_Building_B IS NULL) OR (Move_in_Date_Building_A<Move_in_Date_Building_B)) AND (Move_in_Date_Building_A>Move_in_Date_Building_C) THEN Move_in_Date_Building_A WHEN Move_in_Date_Building_B>=Move_in_Date_Building_C THEN Move_in_Date_Building_B END, GETDATE())
FROM dbo.Buildings
WHERE Move_in_Date_Building_C IS NOT NULL
)
SELECT *
FROM User_Stays
ORDER BY ID_U, Move_In
This query run on your sample data produces he following output:
ID_U Building Move_In Move_Out
-------- ----------- ----------------------- -----------------------
A1398 Kalgan 2010-10-06 00:00:00.000 2010-11-23 18:35:59.050
A2938 Kalgan 2010-09-12 00:00:00.000 2010-11-03 00:00:00.000
A2938 Rufus 2010-11-03 00:00:00.000 2010-11-15 00:00:00.000
A2938 Waylon 2010-11-15 00:00:00.000 2010-11-23 18:35:59.050
(4 row(s) affected)
As you can see, from here on it will be much easier to isolate the days per patient or building, and also to find the records for specific months and calculate the correct stay duration in that case. Note that the CTE displays the current date for patients which are still in a building.
Edit (again): In order to get all months including their start and end dates for all relevant years, you can use a CTE like this:
WITH User_Stays AS (
[...see above...]
)
,
Months AS (
SELECT m.IX,
y.[Year], dateadd(month,(12*y.[Year])-22801+m.ix,0) StartDate, dateadd(second, -1, dateadd(month,(12*y.[Year])-22800+m.ix,0)) EndDate
FROM (
SELECT 1 IX 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 UNION ALL
SELECT 11 UNION ALL
SELECT 12
)
m
CROSS JOIN (
SELECT Datepart(YEAR, us.Move_In) [Year]
FROM User_Stays us UNION
SELECT Datepart(YEAR, us.Move_Out)
FROM User_Stays us
)
y
)
SELECT *
FROM months;
So since we now have a tabular representation of all date ranges which can be of interest, we simply join this together:
WITH User_Stays AS ([...]),
Months AS ([...])
SELECT m.[Year],
DATENAME(MONTH, m.StartDate) [Month],
us.ID_U,
us.Building,
DATEDIFF(DAY, CASE WHEN us.Move_In>m.StartDate THEN us.Move_In ELSE m.StartDate END, CASE WHEN us.Move_Out<m.EndDate THEN us.Move_Out ELSE DATEADD(DAY, -1, m.EndDate) END) Days
FROM Months m
JOIN User_Stays us ON (us.Move_In < m.EndDate) AND (us.Move_Out >= m.StartDate)
ORDER BY m.[Year],
us.ID_U,
m.Ix,
us.Move_In
Which finally produces this output:
Year Month ID_U Building Days
----------- ------------ -------- ---------- -----------
2010 October A1398 Kalgan 25
2010 November A1398 Kalgan 22
2010 September A2938 Kalgan 18
2010 October A2938 Kalgan 30
2010 November A2938 Kalgan 2
2010 November A2938 Rufus 12
2010 November A2938 Waylon 8
-- set the dates for which month you want
Declare #startDate datetime
declare #endDate datetime
set #StartDate = '09/01/2010'
set #EndDate = '09/30/2010'
select
-- determine if the stay occurred during this month
Case When #StartDate <= Move_out_Date_Building_A and #EndDate >= Move_in_Date_Building_A
Then
(DateDiff(d, #StartDate , #enddate+1)
)
-- drop the days off the front
- (Case When #StartDate < Move_in_Date_Building_A
Then datediff(d, #StartDate, Move_in_Date_Building_A)
Else 0
End)
--drop the days of the end
- (Case When #EndDate > Move_out_Date_Building_A
Then datediff(d, #EndDate, Move_out_Date_Building_A)
Else 0
End)
Else 0
End AS Building_A_Days_Stayed
from Clients c
inner join Buildings b
on c.id = b.id_u
Try using a date table. For example, you could create one like so:
CREATE TABLE Dates
(
[date] datetime,
[year] smallint,
[month] tinyint,
[day] tinyint
)
INSERT INTO Dates(date)
SELECT dateadd(yy, 100, cast(row_number() over(order by s1.object_id) as datetime))
FROM sys.objects s1
CROSS JOIN sys.objects s2
UPDATE Dates
SET [year] = year(date),
[month] = month(date),
[day] = day(date)
Just modify the initial Dates population to meet your needs (on my test instance, the above yielded dates from 2000-01-02 to 2015-10-26). With a dates table, the query is pretty straight forward, something like this:
select c.First_name, c.Last_name,
b.Building_A BuildingName, dA.year, dA.month, count(distinct dA.day) daysInBuilding
from clients c
join Buildings b on c.ID = b.ID_U
left join Dates dA on dA.date between b.Move_in_Date_Building_A and isnull(b.Move_out_Date_Building_A, getDate())
group by c.First_name, c.Last_name,
b.Building_A, dA.year, dA.month
UNION
select c.First_name, c.Last_name,
b.Building_B, dB.year, dB.month, count(distinct dB.day)
from clients c
join Buildings b on c.ID = b.ID_U
left join Dates dB on dB.date between b.Move_in_Date_Building_B and isnull(b.Move_out_Date_Building_B, getDate())
group by c.First_name, c.Last_name,
b.Building_B, dB.year, dB.month
UNION
select c.First_name, c.Last_name,
b.Building_C, dC.year, dC.month, count(distinct dC.day)
from clients c
join Buildings b on c.ID = b.ID_U
left join Dates dC on dC.date between b.Move_in_Date_Building_C and isnull(b.Move_out_Date_Building_C, getDate())
group by c.First_name, c.Last_name,
b.Building_C, dC.year, dC.month
If you can't restructure the Building table you can create a query that will normalize it for you and allow for easier calculations:
SELECT "A" as Building, BuidlingA as Name, Move_in_Date_Building_A as MoveInDate,
Move_out_Date_Building_A As MoveOutDate
UNION
SELECT "B", BuidlingB, Move_in_Date_Building_B, Move_out_Date_Building_B
UNION
SELECT "C", BuidlingC, Move_in_Date_Building_C, Move_out_Date_Building_C