I'm working on a SQL server (used by BMC) to grab the uptime of some devices.
I've got a query that display me results like this:
| DeviceName | EventDate | EventType |
| ---------- | ----------------------- | ---------- |
| 1 | 2021-02-15 08:06:12.000 | getting up |
| 1 | 2021-02-12 15:07:26.000 | going down |
| 2 | 2021-02-16 08:12:54.000 | getting up |
| 2 | 2021-02-12 15:43:00.000 | going down |
| 3 | 2021-02-15 07:47:42.000 | getting up |
| 3 | 2021-02-12 15:38:41.000 | going down |
| 4 | 2021-02-15 08:10:07.000 | getting up |
| 5 | 2021-02-18 06:41:40.000 | getting up |
| ... | ... | ... |
I would like to get a result that looks like that:
| DeviceName | TotalUpTime (min) |
| ---------- | ----------------- |
| 1 | 16543 |
| 2 | 13639 |
| 3 | 13524 |
| 4 | 19235 |
| 5 | 12347 |
Here is my current query:
SELECT
DeviceName,
EventDate,
EventType
FROM **irrelevant complexe SELECT query**
ORDER BY DeviceName, EventDate DESC
Any help would be great!!
Many thx in advance!
SOLUTION:
Ok, here's what worked for me:
SELECT
DeviceName,
DATEDIFF(s, EventDate, EndTime)/60 AS [TotalUpTime (min)]
FROM (
SELECT *,
LEAD(CASE WHEN EventType = 32 THEN EventDate END, 1, GETDATE())
OVER (PARTITION BY DeviceName ORDER BY EventDate) AS EndTime
FROM (
**Irrelevant SELECT query**
) r
) s
WHERE EventType = 16 AND EndTime IS NOT NULL
Many thanks to #Charlieface whos response helped me a lot.
Hope this help someone someday, even if it's very specific.
SELECT
DeviceName,
SUM(DATEDIFF(ms, EventDate, EndTime) / 60000.0) AS [TotalUpTime (min)]
FROM (
SELECT *,
LEAD(CASE WHEN EventType = 'going down' THEN EventDate END, 1, GETDATE()) OVER
OVER (PARTITION BY DeviceName ORDER BY EventDate) AS EndTime
FROM table
)
WHERE EventType = 'getting up' AND EndTime IS NOT NULL
GROUP BY DeviceName
Related
I am reviewing reports that contain date ranges by a member ID and upload date. This looks like the following:
+--------------------+---------------------+---------------------+--------------------+
| UploadDate | MemberID | StartDate | EndDate |
| | | | |
+-------------------------------------------------------------------------------------+
| 08/01/2020 | 12345 | 04/01/2020 | 10/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 08/01/2020 | 12345 | 01/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 12345 | 01/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 98765 | 02/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 98765 | 05/01/2020 | 08/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 07/01/2020 | 34568 | 01/01/2020 | 12/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 07/01/2020 | 34568 | 03/31/2020 | 06/01/2020 |
| | | | |
+--------------------+---------------------+---------------------+--------------------+
I need to merge rows with the same UploadDate and the same MemberID where their are no gaps in the date range StartDate - EndDate. If there are gaps the rows will not be merged.
The expected output would be:
+--------------------+---------------------+---------------------+--------------------+
| UploadDate | MemberID | StartDate | EndDate |
| | | | |
+-------------------------------------------------------------------------------------+
| 08/01/2020 | 12345 | 01/01/2020 | 10/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 12345 | 01/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 98765 | 02/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 98765 | 05/01/2020 | 08/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 07/01/2020 | 34568 | 01/01/2020 | 12/31/2020 |
| | | | |
+--------------------+---------------------+---------------------+--------------------+
I had been trying the following without success:
SELECT
ROW_NUMBER() OVER(ORDER BY [MemberID],[StartDate],[EndDate]) AS RN,
[MemberID],
[StartDate],
[EndDate],
LAG([EndDate],1) OVER (ORDER BY [MemberID],[StartDate], [EndDate]) AS PreviousEndDate
FROM
[dbo].[RCNI]
SELECT
*,
CASE WHEN Groups.PreviousEndDate >= [StartDate] THEN 0 ELSE 1 END AS IslandStartInd,
SUM(CASE WHEN Groups.PreviousEndDate >= [StartDate] THEN 0 ELSE 1 END) OVER (ORDER BY Groups.RN) AS IslandId
FROM
(
SELECT
ROW_NUMBER() OVER(ORDER BY [UploadDate], [MemberID],[StartDate], [Benefit End Date]) AS RN,
[UploadDate],
[MemberID],
[StartDate],
[EndDate],
LAG([EndDate],1) OVER (ORDER BY [UploadDate],[MemberID],[StartDate], [EndDate]) AS PreviousEndDate
FROM
[dbo].[RCNI]
) Groups
The solution below appears to work for the given sample data. In words with would be something like:
Filter out rows that have a period that falls completely within another row for the same UploadDate and MemberId (the not exists clause in the common table expression cte).
Look at the remaining rows for each UploadDate and MemberId combination (over(partition by r.UploadDate, r.MemberId ...) and sort them by StartDate (... order by r.StartDate)).
If the start date of a row comes before, is equal to or comes one day after the end date of the previous row for the combination (lag(r.EndDate) over(partition by r.UploadDate, r.MemberId order by r.StartDate)), then they must be merged.
If rows must be merged, then the start dates becomes the smallest start date of the combination (min(r.StartDate) over(partition by r.UploadDate, r.MemberId)). All rows that must be merged now have the same start date (StartDateNew).
Determine the new end date by grouping on UploadDate, MemberId and StartDateNew and taking the maximum value for EndDate.
Sample data
create table rcni
(
UploadDate date,
MemberId int,
StartDate date,
EndDate date
);
insert into rcni (UploadDate, MemberId, StartDate, EndDate) values
('08/01/2020', 12345, '04/01/2020', '10/31/2020'),
('08/01/2020', 12345, '01/01/2020', '03/31/2020'),
('06/01/2020', 12345, '01/01/2020', '03/31/2020'),
('06/01/2020', 98765, '02/01/2020', '03/31/2020'),
('06/01/2020', 98765, '05/01/2020', '08/31/2020'),
('07/01/2020', 34568, '01/01/2020', '12/31/2020'),
('07/01/2020', 34568, '03/31/2020', '06/01/2020');
Solution
with cte as
(
select r.UploadDate,
r.MemberId,
case
when r.StartDate <= dateadd(dd, 1, lag(r.EndDate) over(partition by r.UploadDate, r.MemberId order by r.StartDate))
then min(r.StartDate) over(partition by r.UploadDate, r.MemberId)
else r.StartDate
end as StartDateNew,
r.EndDate
from rcni r
where not exists ( select 'x'
from rcni r2
where r2.UploadDate = r.UploadDate
and r2.MemberId = r.MemberId
and r2.StartDate < r.StartDate
and r2.EndDate > r.EndDate )
)
select c.UploadDate,
c.MemberId,
c.StartDateNew,
max(c.EndDate) as EndDateNew
from cte c
group by c.UploadDate,
c.MemberId,
c.StartDateNew;
Fiddle
Hard to phrase the title for this one.
I have a table of data which contains a row per invoice. For example:
| Invoice ID | Customer Key | Date | Value | Something |
| ---------- | ------------ | ---------- | ------| --------- |
| 1 | A | 08/02/2019 | 100 | 1 |
| 2 | B | 07/02/2019 | 14 | 0 |
| 3 | A | 06/02/2019 | 234 | 1 |
| 4 | A | 05/02/2019 | 74 | 1 |
| 5 | B | 04/02/2019 | 11 | 1 |
| 6 | A | 03/02/2019 | 12 | 0 |
I need to add another column that counts the number of previous rows per CustomerKey, but only if "Something" is equal to 1, so that it returns this:
| Invoice ID | Customer Key | Date | Value | Something | Count |
| ---------- | ------------ | ---------- | ------| --------- | ----- |
| 1 | A | 08/02/2019 | 100 | 1 | 2 |
| 2 | B | 07/02/2019 | 14 | 0 | 1 |
| 3 | A | 06/02/2019 | 234 | 1 | 1 |
| 4 | A | 05/02/2019 | 74 | 1 | 0 |
| 5 | B | 04/02/2019 | 11 | 1 | 0 |
| 6 | A | 03/02/2019 | 12 | 0 | 0 |
I know I can do this using either a CTE like this...
(
select
count(*)
from table
where
[Customer Key] = t.[Customer Key]
and [Date] < t.[Date]
and Something = 1
)
But I have a lot of data and that's pretty slow. I know I can also use cross apply to achieve the same thing, but as far as I can tell that's not any better performing than just using a CTE.
So; is there a more efficient means of achieving this, or do I just suck it up?
EDIT: I originally posted this without the requirement that only rows where Something = 1 are counted. Mea culpa - I asked it in a hurry. Unfortunately I think that this means I can't use row_number() over (partition by [Customer Key])
Assuming you're using SQL Server 2012+ you can use Window Functions:
COUNT(CASE WHEN Something = 1 THEN CustomerKey END) OVER (PARTITION BY CustomerKey ORDER BY [Date]
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) -1 AS [Count]
Old answer before new required logic:
COUNT(CustomerKey) OVER (PARTITION BY CustomerKey ORDER BY [Date]
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) -1 AS [Count]
If you're not using 2012 an alternative is to use ROW_NUMBER
ROW_NUMBER() OVER (PARTITION BY CustomerKey ORDER BY [Date]) - 1 AS Count
First of all please correct me if my title are not specific/clear enough.
I have use the following code to generate the start dates and end dates :
DECLARE #start_date date, #end_date date;
SET #start_date = '2016-07-01';
with dates as
(
select
#start_date AS startDate,
DATEADD(DAY, 6, #start_date) AS endDate
union all
select
DATEADD(DAY, 7, startDate) AS startDate,
DATEADD(DAY, 7, endDate) AS endDate
from
dates
where
startDate < '2017-03-31'
)
select * from dates
Below is part of the output from above query :
+------------+------------+
| startDate | endDate |
+------------+------------+
| 2016-07-01 | 2016-07-07 |
| 2016-07-08 | 2016-07-14 |
| 2016-07-15 | 2016-07-21 |
| 2016-07-22 | 2016-07-28 |
| 2016-07-29 | 2016-08-04 |
+------------+------------+
Now I have another table named sales, which have 3 columns sales_id,sales_date and sales_amount as below :
+----------+------------+--------------+
| sales_ID | sales_date | sales_amount |
+----------+------------+--------------+
| 1 | 2016-07-04 | 10 |
| 2 | 2016-07-06 | 20 |
| 3 | 2016-07-13 | 30 |
| 4 | 2016-07-19 | 15 |
| 5 | 2016-07-21 | 20 |
| 6 | 2016-07-25 | 25 |
| 7 | 2016-07-26 | 40 |
| 8 | 2016-07-29 | 20 |
| 9 | 2016-08-01 | 30 |
| 10 | 2016-08-02 | 30 |
| 11 | 2016-08-03 | 40 |
+----------+------------+--------------+
How can I create the query to show the total sales amount of each week (which is between each startDate and endDate from the first table)? I suppose I will need to use a recursive query with WHERE clause to check if the dates are in between startDate and endDate but I cant find a working example.
Here are my expected result (the startDate and endDate are the records from the first table) :
+------------+------------+--------------+
| startDate | endDate | sales_amount |
+------------+------------+--------------+
| 2016-07-01 | 2016-07-07 | 30 |
| 2016-07-08 | 2016-07-14 | 30 |
| 2016-07-15 | 2016-07-21 | 35 |
| 2016-07-22 | 2016-07-28 | 65 |
| 2016-07-29 | 2016-08-04 | 120 |
+------------+------------+--------------+
Thank you!
Your final Select (after the cte) should be something like this
Select D.*
,Sales_Amount = sum(Sales)
From dates D
Join Sales S on (S.sales_date between D.startDate and D.endDate)
Group By D.startDate,D.endDate
Order By D.startDate
EDIT: You could use a Left Join if you want to see missing dates from
Sales
I'm trying to achieve a report that will show all daily count as well weekly count in the same table. I've tried different techniques that I know but it seems that I wasn't able to get what I want.
I'm trying to show a similar table below.
+-----------+-----+-------+--------+--+--+--+
| August | | Count | | | | |
+-----------+-----+-------+--------+--+--+--+
| 8/1/2013 | Thu | 1,967 | | | | |
| 8/2/2013 | Fri | 1,871 | | | | |
| 8/3/2013 | Sat | 1,950 | | | | |
| 8/4/2013 | Sun | 2,013 | 7801 | | | |
| 8/5/2013 | Mon | 2,039 | | | | |
| 8/6/2013 | Tue | 1,871 | | | | |
| 8/7/2013 | Wed | 1,611 | | | | |
| 8/8/2013 | Thu | 1,680 | | | | |
| 8/9/2013 | Fri | 1,687 | | | | |
| 8/10/2013 | Sat | 1,649 | | | | |
| 8/11/2013 | Sun | 1,561 | 12,098 | | | |
+-----------+-----+-------+--------+--+--+--+
Please let me if there's an existing code or technique that I could to achieve something like this. Thanks.
Sherwin
Try something like this but make sure to check which WEEKDAY is Sunday on your server since this can be modified.
select T1.August, T1.[Count],
case DATEPART(WEEKDAY, O.Order_Date)
WHEN 1 THEN (SELECT CONVERT(varchar(10), SUM(T2.[Count]) FROM TableName T2 WHERE T2.August BETWEEN DATEADD(d,-7,T1.August) and T1.August))
ELSE ''
end as Weekly_Count
FROM TablleName T1
ORDER BY T.August
If you don't mind having those subtotal on a new row instead of on a new column, GROUP BY WITH ROLLUP could be the solution for you:
SET LANGUAGE GERMAN is used for setting monday as first day of the week and allowing us to sum up until sunday
SET LANGUAGE GERMAN;
WITH first AS
(
SELECT
date,
day,
DATEPART(dw, date) AS dayweek,
DATEPART(wk, date) AS week,
count
FROM example
)
SELECT
CASE WHEN (GROUPING(dayweek) = 1) THEN 'TOT' ELSE CAST(MAX(date) AS VARCHAR(20)) END AS date,
CASE WHEN (GROUPING(dayweek) = 1) THEN 'TOT' ELSE MAX(day) END AS day,
SUM(count) AS count
FROM first
GROUP BY week,dayweek WITH ROLLUP
see the complete example on sqlfiddle
If you use stored procedures, then make a temp table and loop it through with a cursor, and make the sums.
You could also do something like this:
SELECT CreatedDate, Amount, CASE WHEN DATENAME(dw , CreatedDate) = 'Sunday' THEN (SELECT SUM(Amount) FROM AmountTable at2 WHERE CreatedDate <= at1.CreatedDate AND CreatedDate > DATEADD(Day, -7, at1.CreatedDate)) ELSE 0 END AS 'WeekTotal'
from AmountTable at1
Would something along these lines work, the syntax may not be 100%
select
[Date],
DOW,
[Count],
Case When DOW = 'Mon' then 1 else 2 end as Partition_DOW,
SUM([Count]) OVER (PARTITION BY (Case When DOW = 'Mon' then 1 else 0 end) ORDER BY [Date]) AS 'Monthly_Total'
from My_table
where Month([Date]) = Month(Date()) AND Year([Date]) = Year(Date())
being a novice sql user:
I have a simple table storing some records over night daily. table:
Table: T1
+----+-----+----+-----------+------------+
| Id | A | AB | Value | Date |
+----+-----+----+-----------+------------+
| 1 | abc | I | -48936.08 | 2013-06-24 |
| 2 | def | A | 431266.19 | 2013-06-24 |
| 3 | xyz | I | -13523.90 | 2013-06-24 |
| 4 | abc | A | 13523.90 | 2013-06-23 |
| 5 | xyz | I | -13523.90 | 2013-06-23 |
| 6 | def | A | 13523.90 | 2013-06-22 |
| 7 | def | I | -13523.90 | 2013-06-22 |
+----+-----+----+-----------+------------+
I would like to get all values of columns A,AB, Value for the latest Date on Column A filtered on AB = I
basically the result should look like:
+----+-----+----+-----------+------------+
| Id | A | AB | Value | Date |
+----+-----+----+-----------+------------+
| 1 | abc | I | -48936.08 | 2013-06-24 |
| 3 | xyz | I | -13523.90 | 2013-06-24 |
| 7 | def | I | -13523.90 | 2013-06-22 |
+----+-----+----+-----------+------------+
I have tried to use inner join twice on the same table but failed to come up with correct result.
any help would be appreciated.
thanks :)
This will work with sqlserver 2005+
;WITH a as
(
SELECT id, A,AB, Value, Date
, row_number() over (partition by A order by Date desc) rn
FROM t1
WHERE AB = 'I'
)
SELECT id, A,AB, Value, Date
FROM a WHERE rn = 1
; WITH x AS (
SELECT id
, a
, ab
, "value"
, "date"
, Row_Number() OVER (PARTITION BY a ORDER BY "date" DESC) As row_num
FROM your_table
WHERE ab = 'I'
)
SELECT *
FROM x
WHERE row_num = 1