I'm looking at a snapshot table in Microsoft SQL Server Management Studio with distinct dates captured every few days. The table has records stretching back to 2014, and should continue updating with new snapshots for the foreseeable future. As an example, the dates for 2022 are as follows:
2022-01-03 00:00:00
2022-01-10 00:00:00
2022-01-12 00:00:00
2022-01-18 00:00:00
2022-01-24 00:00:00
2022-02-03 00:00:00
2022-02-05 00:00:00
2022-02-09 00:00:00
2022-02-14 00:00:00
2022-02-21 00:00:00
2022-03-01 00:00:00
2022-03-07 00:00:00
2022-03-14 00:00:00
What I'm looking to do is select data (stretching back to 2019) from only the date captured dates closest to the beginning of the month (or, perhaps, quarter), and have the query work so that it will function when April/May/June/etc. roll around. In the above example, essentially, I'd be looking to return data from the 1/03, 2/03 and 3/01 snapshots with nothing else considered.
How do I go about doing this? Thank you!
Try an APPLY ... TOP 1 query, like this example against AdventureWorksDW2017:
select d.CalendarYear, d.EnglishMonthName, firstSale.*
from DimDate d
outer apply
(
select top 1 *
from FactInternetSales s
where s.OrderDate >= d.FullDateAlternateKey
and s.OrderDate < dateadd(month,1,d.FullDateAlternateKey)
order by s.OrderDate
) firstSale
where d.DayNumberOfMonth = 1
and d.CalendarYear in (2012,2013)
order by d.FullDateAlternateKey
That's not a periodic snapshot fact, but the query is the same.
Another approach that doesn't require a calendar is to use the ROW_NUMBER() window function to assign ordinal values to each row within each month, which is then used to filter out all but the first (ordinal = 1). Something like:
SELECT S.Date
FROM (
SELECT *, Ordinal = ROW_NUMBER() OVER(PARTITION BY YEAR(Date), MONTH(Date) ORDER BY Date)
FROM SnapshotDates
) S
WHERE S.Ordinal = 1
ORDER BY S.Date
The equivalent query using a CTE (Common Table Expression) is:
WITH CTE_SnapshotDates AS (
SELECT *, Ordinal = ROW_NUMBER() OVER(PARTITION BY YEAR(Date), MONTH(Date) ORDER BY Date)
FROM #SnapshotDates
)
SELECT S.Date
FROM CTE_SnapshotDates S
WHERE S.Ordinal = 1
ORDER BY S.Date
See this db<>fiddle for a demo.
Related
How to query to accumulate two datetime columns in two tables in SQL Server 2014? This is an example for your reference:
Check-In table
InID UserID CheckInTime
---------------------------------
IN-001 1 2018-11-10 08:00:00
IN-002 2 2018-11-15 07:00:00
Check-Out table
OutID UserID CheckOutTime
----------------------------------
OUT-001 1 2018-11-10 12:00:00
OUT-002 2 2018-11-15 14:00:00
Result set (expected)
ResultID UserID InID OutID WorkTimeinHour
--------------------------------------------------------
1 1 IN-001 OUT-001 4
2 2 IN-002 OUT-002 7
Similar to #PSK, I used STUFF function to replace "IN-" and "OUT-" characters
But since these are in JOIN conditions, those operations will cause performance loss
It is better to use a numeric column in both tables instead of useless "IN-" and "OUT-" containing string columns
select
i.UserId, i.InID, CheckInTime, o.OutID, CheckOutTime,
dbo.fn_CreateTimeFromSeconds(DATEDIFF(ss, CheckInTime, CheckOutTime)) as TotalTime
from CheckIn i
inner join CheckOut o
on i.UserId = o.UserId and
STUFF (i.InID,1,3,'') = STUFF (o.OutID,1,4,'')
Additionally, I used a custom user-defined fn_CreateTimeFromSeconds function to format time for HH:MI:SS format
Hope it helps
For your current scenario, you can try like following.
Assuming that IN and OUT id after the "-" will be same as one entry.
SELECT ROW_NUMBER()
OVER(
ORDER BY (SELECT NULL)) AS ResultIt,
T1.inid,
T2.outid,
DATEDIFF(hh, T2.checkouttime, T1.checkintime)
FROM checkin T1
INNER JOIN checkout T2
ON REPLACE(T1.inid, 'IN-', '') = REPLACE(T2.outid, 'OUT-', '')
This query will not perform good for huge data as REPLACE is being used in the JOIN. Ideally you should have a single identifier to identify the IN and OUT transaction.
Fairly new to Access 2016 and I'm writing a query to obtain the most common user within the database per month.
So a record would be
Table1
ID Date1
1 2019-02-28
This is my code grouping the totals per month:
Month: Format([Date1],"mmmm")
TopUser: (SELECT TOP 1 [Table1]![ID] FROM [Table1] GROUP BY
[Table1]![ID] order by COUNT([Table1]![ID]) DESC)
Expectation:
Month TopUser
January 2
February 1
March 2
April 3
Result:
Month TopUser
January 2
February 2
March 2
April 2
So my code is returning back the most common user overall instead of for each month. I'm not sure if this is an access aspect that I'm misinterpreting or if its my queries.
Try filtering on the month:
Select
Format([Date1], "yyyymm") As YearMonth,
(Select Top 1 T.ID
From Table1 As T
Where Format(T.[Date1], "yyyymm") = Format(Table1.[Date1], "yyyymm")
Group By ID
Order By Count(T.ID) Desc) As TopID
From
Table1
Group By
Format([Date1], "yyyymm")
I need date between minimum date and maximum date in the SQL Server in the table containing 3 rows.
ex:01 Jan 1992
02 Jan 1992
03 Jan 1992
i need that 02 Jan 1992
Please note that the query below is on a table with only 3 rows as your question depicts.
;WITH DateCTE AS
(
SELECT MAX(CreatedDate) AS MaxDate, MIN(CreatedDate) AS MinDate
FROM [metadata].[BuildVersion]
)
SELECT CreatedDate
FROM [metadata].[BuildVersion]
WHERE CreatedDate <> (SELECT MaxDate FROM DateCTE)
AND CreatedDate <> ( SELECT MinDate FROM DateCTE)
This should work just fine
select date from table_name where date between '01 Jan 1992' and '03 Jan 1992';
You can use between to extract a value between any 2 values. It will be better if you give the column names and table name, so that it will be easy for other who try to answer your question.
I want to select records from SQL server table 'employeelogdetails' which has field 'logintime' (which registers time in GMT when employee swipes the access card while entering or leaving office) which is datetime datetype. Now I want to select records say from yesterday 17 October 2012, 14:00 GMT
SELECT * FROM EMPLOYEELOGDETAILS WHERE LOGINTIME > ' ..........' LOGINTIME DESC
Don't know how to go forward guys, have been trying this for a while
Help me out
The following query will give you records from the 17th after 14:00 for the rest of the day. I'm not certain what time window you are looking for though, so this may need slight adjustment.
SELECT *
FROM EmployeeLogDetails AS eld
WHERE eld.LoginTime > '2012-10-17 14:00:00'
AND eld.LoginTime < '2012-10-18'
ORDER BY eld.LoginTime DESC;
Use this query:
select * from EmployeeLogDetails where LoginTime between '17/10/2012' and Now();
This can also work this way:
SELECT * FROM EmployeeLogDetails
WHERE LoginTime BETWEEN ('20121017 9:00:00.000' AND '20121018 3:37:00.000')
ORDER BY LoginTime DESC;
Thank you all for the responses
SELECT * FROM EMPLOYEELOGDETAILS WHERE
LOGINTIME > CONVERT(DateTime,'2012/10/17 14:00:00') ORDER BY LOGINTIME DESC
So I have a reference table which stores the primary key, description and update date columns. Something like this
SELECT * FROM tblReasonRef
ReasonCode Description UpdateDate
27 Lunch 2010-12-01
24 Meeting 2010-12-01
20 SpecialProj 2010-12-01
The other day, the code description was changed. So now the query returns the following...
ReasonCode Description UpdateDate
27 Lunch 2010-12-01
24 Meeting 2010-12-01
20 SpecialProj 2010-12-01
27 Training 2012-06-22
24 Meeting 2012-06-22
20 Lunch 2012-06-22
The source data tracks every 30 minutes what state a staff member might go into, so you would have the following query...
SELECT * FROM tblhActivity
MemberID Date Time ReasonCode ReasonDuration
10922 2012-06-21 1200 27 100
10922 2012-06-21 1500 24 1800
10922 2012-06-25 1230 27 100
So originally, the query I had was...
SELECT a.MemberID, a.Date, a.Time, r.Description, a.ReasonDuration
FROM tblhActivity a
INNER JOIN tblReasonRef r ON a.ReasonCode = r.ReasonCode
Which worked fine until the change on the 22nd. Now I have two definitions of each code. The question is, how can create a query that will pick the right code depending on the date.
For example, I know that when the date is the 21st, the description for code 27 should be lunch. On the 25th, the description returned should be Training.
Keep in mind also, that this will probably happen again where codes are added to the reference table. I am trying to think the join should also be on UpdateDate but I have to know the start and end date of each reference code. Is there a simple solution?
You really need the start and end dates for the period in which a particular reason is applicable. You can either modify your tblReasonRef to include these dates (best option) or you will need to calculate them.
The following query will calculate an end date for each reason as the day before a new entry for the ReasonCode is added.
SELECT ReasonCode
,Description
,UpdateDate StartDate
,DATEADD(d, -1, UpdateDate) PreviousEntryEndDate
,ROW_NUMBER() OVER(PARTITION BY ReasonCode ORDER BY UpdateDate) AS Row
INTO #reason
FROM tblReasonRef
SELECT a.MemberID
,a.Date
,a.Time
,reason.ReasonCode
,a.ReasonDuration
FROM tblhActivity a
INNER JOIN #reason reason
ON a.ReasonCode = reason.ReasonCode
LEFT JOIN #reason nextReason
ON reason.Row = nextReason.Row - 1
AND reason.ReasonCode = nextReason.ReasonCode
WHERE a.Date BETWEEN reason.StartDate AND ISNULL(nextReason.PreviousEntryEndDate, a.Date)
DROP TABLE #reason
If you modify your table tblReasonRef, like this:
ReasonCode, Description, StarDate, EndDate
you can do this SQL Query:
SELECT a.MemberID, a.Date, a.Time, r.Description, a.ReasonDuration
FROM tblhActivity a
INNER JOIN tblReasonRef r ON a.ReasonCode = r.ReasonCode
WHERE a.Date between r.StartDate and r.EndDate
Remember that you need your code and your model simple.