Query to count living population each year - sql-server

I have a table with a list of databases, about 10k rows, 1 row for each database. The table contains attributes for:
ServerId (int)
DBID (int)
DBName (varchar(255))
Createtime(datetime)
LastCheckTime(datetime).
LastCheckTime is frequently updated until the database is removed on the SQL Server, and then it stops being updated.
I would like to count how many "living" databases there have been each year. If the LastCheckTime < getdate()-1 then I assume it's alive and should be counted for each year from Createtime until LastCheckTime is more than 1 day.
The table resides in a SQL Server 2008R2 database. I can upgrade to SQL Server 2016 if it's easier to accomplish this.

The following query returns the desired output:
CREATE TABLE [DB]
(
ServerId INT,
[DBID] INT,
DBName varchar(255),
Createtime DATETIME,
LastCheckTime DATETIME
)
INSERT INTO [DB] VALUES
(1,1, 'DB1', '2010-09-10', '2013-09-01'),
(1,2, 'DB2', '2010-09-10', GETDATE()),
(1,3, 'DB3', '2010-09-12', GETDATE()),
(1,4, 'DB4', '2011-09-13', GETDATE()),
(1,5, 'DB5', '2011-09-10', GETDATE()),
(1,6, 'DB6', '2013-09-10', GETDATE()),
(1,7, 'DB7', '2014-09-10', GETDATE()),
(1,8, 'DB8', '2015-09-10', GETDATE()),
(1,9, 'DB9', '2016-09-10', GETDATE()),
(1,10, 'DB10', '2017-09-10', GETDATE());
--CTE holds all the years starting from the oldest DB
;WITH CTE
AS ( SELECT DATEPART(YEAR, ( SELECT MIN(CreateTime)
FROM [DB]
)) AS year
UNION ALL
SELECT year + 1
FROM CTE
WHERE year < DATEPART(YEAR, GETDATE())
)
SELECT year ,
COUNT(*) DBCount
FROM CTE
LEFT JOIN [DB] ON CTE.year BETWEEN DATEPART(YEAR, [DB].Createtime)
AND DATEPART(YEAR,
[DB].LastCheckTime + 1)
WHERE [DB].LastCheckTime > DATEADD(DAY, -1, GETDATE()) -- filter only live DBs
GROUP BY year;
And the result would be:

Related

Populate Missing Data is SQL with CTE

I am stuck with a problem related to SQL while implementing data densification.
I have a table containing weather data for different stations. This table updates every 10 minutes. But sometimes not all the station data is available after the 10 minute update. Additionally, I want to drill down it's granularity to 1 minute i.e all data points should be available every minute.
I have implemented a CTE to make a column of DateTime with 1 minute granularity. Now I need to populate the NULL values with the station names and sum of rainfall data wrt to it's station name and previous data.
I am trying to use windowing partition functions but it's not working out yet.
enter image description here
The SQL query is this:
DECLARE #FromDate DateTime = '2021-01-01 00:00:00.000',
#ToDate DateTime = '2021-08-01 00:00:00.000'
;WITH DateCte (DateTime) AS
(
SELECT #FromDate
UNION ALL
SELECT DATEADD(MINUTE, 1, DateTime)
FROM DateCte
WHERE DateTime < #ToDate
)
SELECT
DateTime,
DATEADD(mi, DATEDIFF(mi, 0, [LastObservations].[Local_Time]), 0) AS [Local Time],
[LastObservations].[Name_En],
[LastObservations].[Rainfall] AS [Value]
FROM
DateCte
LEFT OUTER JOIN
[ODW].[QMD].[LastObservations] ON DateCte.DateTime = DATEADD(mi, DATEDIFF(mi, 0, [LastObservations].[Local_Time]), 0)
AND [LastObservations].[Local_Time] >= '2021-01-01 00:00:00.000'
AND [LastObservations].[Local_Time] <= '2021-08-01 00:00:00.000'
ORDER BY
DateCte.DateTime
OPTION (MaxRecursion 0)

SSRS 2017 Cascading parameters with dates

I have a parameter called Year and you can choose one year at a time. I have a date/time parameter with a calendar called start date and another one called end date.
I would like it to work such that if I pick the year to be 2017 it will show the calendar start and end dates for 2017.
Since my start date and end date parameters are not defaulted from a dataset I am not sure how to control that.
Any help will be appreciated.
You can do it as an expression, but as its easier in SQL you can just create a second dataset with the following:
select convert(date, '1 jan ' + convert(varchar(4), #Year)), dateadd(day, -1, dateadd(year, 1, convert(date, '1 jan ' + convert(varchar(4), #Year))))
I used a recursive CTE common table expression that references itself to solve this. Then you can set the Dataset of the start_date and end_date parameters.
Dataset for the default values of the start and end date parameters
DECLARE #year AS INT
SET #year = 2018
SELECT [start_date] = DATEFROMPARTS(#year, 1, 1), [end_date] = DATEFROMPARTS(#year, 12, 31)
Dataset for the available values of the start and end date parameters
DECLARE #year AS INT
SET #year = 2018
;WITH
source_data_dates
AS
(
SELECT
[date_start] = DATEFROMPARTS(#year, 1, 1)
, [date_end] = DATEFROMPARTS(#year, 12, 31)
)
,
year_date_list([rn], [date_value])
AS
(
SELECT
[rn] = 1
, [date_value] = CAST([date_start] AS DATETIME)
FROM
source_data_dates
UNION ALL
SELECT
[rn] = [rn] + 1
, [date_start] = CAST(DATEADD(DAY, [rn], [date_start]) AS DATETIME)
FROM
year_date_list
, source_data_dates
WHERE
[rn] <= DATEDIFF(DAY, [date_start], [date_end])
)
SELECT
[rn]
, [date_value]
FROM
year_date_list
OPTION (MAXRECURSION 0)
Results:

Transpose SQL Result

I have following sql query
SELECT [POINT],[Timestamp],AvgValue=AVG(ValueNumeric)
FROM [myDatabase].[dbo].[Value]
WHERE [Point] In (98,99,100,101,102,103,104,105)
AND Timestamp between DATEADD(DAY, -90, GETDATE()) and GETDATE()
Group by [timestamp],[Point]
order by [Timestamp], [Point]
which returns table as below
but I need to transpose this as below
Timestamp|Point1|Value1|Point2|Value2|Point3|Value3|
Is this doable?

Select missing months between 2 dates (in same year)

I'm trying to get all months in which no transaction is placed for the same year (If different years is not possible)
This is my query to get transactions between 2 dates, but don't know how can I select only months for which transaction in database is missing
SELECT *
FROM Installment
WHERE OrderId = 1
AND InstallmentDate
BETWEEN cast('8/02/2014' as date)
AND cast('12/25/2014' as date)
InstallmentId OrderId CustomerKey InstallmentAmount InstallmentDate
18 1 INS-1 3000 2014-09-03
92 1 INS-1 3000 2014-10-13
137 1 INS-1 3000 2014-11-05
in this case record for the 12th month and 8th month is missing, how can I get this with SQL Server Query ?
Update
select yymm.yy, yymm.mm
from (select distinct year(InstallmentDate) as yy, month(InstallmentDate) as mm
from Installment
where InstallmentDate BETWEEN '2014-09-02' and '2015-01-15'
) yymm left join
Installment i
on i.OrderId = 1 and
year(i.InstallmentDate) = yymm.yy and
month(i.InstallmentDate) = yymm.mm
where i.OrderId is not null;
Gordon's query is returning all the years and months from table between 2 dates, just by changing i.OrderId is null to i.OrderId is not null here is the out of his query
yy mm
2014 9
2014 10
2014 11
Expected Output (if possible)
yy mm
2014 12
2015 1
Using the following recursive CTE:
DECLARE #start DATE = '2014-09-02'
DECLARE #end DATE = '2015-01-15'
;WITH IntervalDates (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MONTH, 1, date)
FROM IntervalDates
WHERE DATEADD(MONTH, 1, date)<=#end
)
SELECT YEAR(date) AS Year, MONTH(date) AS Month
FROM IntervalDates
you can get a list of all Years/Months between the two dates of interest:
Year Month
==============
2014 9
2014 10
2014 11
2014 12
2015 1
Using EXCEPT on the above CTE:
;WITH IntervalDates (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MONTH, 1, date)
FROM IntervalDates
WHERE DATEADD(MONTH, 1, date)<=#end
)
SELECT YEAR(date) AS Year, MONTH(date) AS Month
FROM IntervalDates
EXCEPT
SELECT DISTINCT YEAR(InstallmentDate) AS yy, MONTH(InstallmentDate) AS mm
FROM Installment
WHERE OrderId = 1 AND InstallmentDate BETWEEN #start AND #end
yields the required result set:
Year Month
=============
2014 12
2015 1
To do this in SQL, you need to start with a list of months. Assuming you have at least one record for each month in the table, you can then get the missing dates easily using a subquery. The rest of the query is just a left join and checking for non-matches:
select yymm.yy, yymm.mm
from (select distinct year(InstallmentDate) as yy, month(InstallmentDate) as mm
from Installment
where InstallmentDate BETWEEN '2014-09-02' and '2015-01-15'
) yymm left join
Installment i
on i.OrderId = 1 and
year(i.InstallmentDate) = yymm.yy and
month(i.InstallmentDate) = yymm.mm
where i.OrderId is null;
Simplest way I can think of is to have a date dimension table that contains (at least) date, and 1st of month then. For creating one take a look at something like https://dba.stackexchange.com/questions/74957/best-approach-for-populating-date-dimension-table , although that one doesn't have firstOfMonthDate in it as my example show but the idea is the same.
then your query becomes
SELECT DISTINCT
firstOfMonthDate
FROM
dateRef dr
LEFT OUTER JOIN
InstallmentDate i ON dr.date = i.InstallmentDate AND i.OrderId = 1
WHERE
i.InstallmentDate IS NULL
AND
dr.date BETWEEN #startDate and #endDate
change firstOfMonthDate for fiscal month etc. as required. This would work across any range of dates you have in your table so different years wouldn't be an issue.
Try the below script. I retrieve all dates between the specified dates and use a LEFT JOIN to get those which are not present in your table:
DECLARE #startDate AS DATETIME, #endDate AS DATETIME
DECLARE #dates AS TABLE (CurrentDate DATETIME)
SET #startDate = '2014-01-01'
SET #endDate = '2014-01-31';
with GetDates AS
(
SELECT #startDate AS TheDate
UNION ALL
SELECT DATEADD("DD", 1, TheDate) FROM GetDates
WHERE TheDate < #endDate
) INSERT INTO #dates SELECT TheDate FROM GetDates
OPTION (MAXRECURSION 0)
SELECT DISTINCT YEAR(d.CurrentDate), MONTH(d.CurrentDate) FROM #dates d
LEFT JOIN InstallmentDate i ON i.InstallmentDate BETWEEN #startDate AND #endDate AND OrderId = 1
WHERE i.InstallmentDate IS NULL
Hope this helps...

sql server query group by item

I have a table with 3 columns,sql server 2008
item, count , date
I would like a query that selects the total count per item for current day with
date colums having the format 2011-03-30 09:00:00.000
select
date,item, sum(count) as total
from
table1
group by
date,item
EDIT:
select item, dateadd(day,datediff(day,0,date),0) as dateInserted, sum(count)
as total from Table1 group by item, dateadd(day,datediff(day,0,date),0) order by 1
the first select returns totals but grouped by the datetime instead of date...then the second returns totals correctly but I find some items appearing twice with the same date but different totals
Sounds like you're looking for 'current day' results?
select item, dateadd(day, datediff(day, 0, date), 0) as dateInserted,
sum(count) as total
from Table1
WHERE
dateadd(day, datediff(day, 0, [date]), 0) =
dateadd(day, datediff(day, 0, getdate()), 0)
group by
item, dateadd(day, datediff(day, 0, date), 0)
order by 1
If you're using SQL Server 2008, use the DATE datatype.
select item,
CAST([date] as DATE)) as dateInserted,
sum(count) as total
from Table1
WHERE
CAST([date] as DATE)) = CAST(getdate() as DATE)
group by
item, CAST([date] as DATE))
order by 1

Resources