Get last row by datetime in SQL Server - sql-server

I have below SQL table:
Id | Code | DateTime1 | DateTime2
1 3AA2 2017-02-01 14:23:00.000 2017-02-01 20:00:00.000
2 E323 2017-02-12 17:34:34.032 2017-02-12 18:34:34.032
3 DFG3 2017-03-08 09:20:10.032 2017-03-08 12:30:10.032
4 LKF0 2017-04-24 11:14:00.000 2017-04-24 13:40:00.000
5 DFG3 2017-04-20 13:34:42.132 2017-04-20 15:12:12.132
6 DFG3 2017-04-20 13:34:42.132 NULL
Id is an auto numeric field.
Code is string and Datetime1 and DateTime2 are datetime type. Also DateTime1 cannot be null but datetime2 can be.
I would like to obtain the last row by datetime1 (MAX datetime1, most recent one) that match a concrete code and it has datetime2 set to NULL.
For example, taken into account above table, for code DFG3 I would like to obtain row with Id=6, its max date for datetime1, that is "2017-04-20 13:34:42.132"
But now imagine the following case:
Id | Code | DateTime1 | DateTime2
1 3AA2 2017-02-01 14:23:00.000 2017-02-01 20:00:00.000
2 E323 2017-02-12 17:34:34.032 2017-02-12 18:34:34.032
3 DFG3 2017-03-08 09:20:10.032 2017-03-08 12:30:10.032
4 LKF0 2017-04-24 11:14:00.000 2017-04-24 13:40:00.000
5 DFG3 2017-04-20 13:34:42.132 NULL
6 DFG3 2017-05-02 16:34:34.032 2017-05-02 21:00:00.032
Again, taken into account above table, I would like to obtain the same, that is, the last row by datetime1 (Max datetime1, most recent one) that match a concrete code and it has datetime2 set to NULL.
Then, in this last case for code DFG3 no rows must be return because row with Id=6 is the last by datetime1 (most recent) for code DFG3 but is not NULL.
How can I do this?

Can you try this query and let me know if it works for your case
Select * From [TableName] where [Code]='DFG3' and [datetime2] is null and [datetime1] = (select max([datetime1]) from [TableName] where [Code]='DFG3')

This bring you all the latest code on your table, then you select only the one with datetime2 is null
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Code
ORDER BY DateTime1 Desc) as rn
FROM yourTable
) as T
WHERE rn = 1 -- The row with latest date for each code will have 1
and dateTime2 IS NULL
and code = 'DFG3' -- OPTIONAL

Related

Finding missing records in joined tables using ID and Dates in SQL?

I'm having trouble writing a check that will find missing records.
My tables are Provider, ProviderRelationship, Agreement, and AgreementProvider. I need to find missing records where the Provider has a ProviderRelationship, but no record for AgreementProvider for that specific time frame.
Both ProviderRelationship and AgreementProvider have StartDate and EndDate fields. Providers can only have 1 current ProviderRelationship, but they can have multiple ProviderRelationship entries. For example, ProviderA has ProviderRelationship1 on 3/17/2019, then ProviderRelationship2 from 3/18/2019 to 6/30/2020 and AgreementProvider2 for the same dates.
The closest I've come is this, which gets Providers that have never had any AgreementProvider entries at all. When I add in the date validation, it also includes the previous ProviderRelationships.
CREATE TABLE dbo.Provider
ProviderID INT IDENTITY(1,1) NOT NULL,
Name VARCHAR(200) NULL
ProviderID Name
9003055 ABC
6102 DEF
2743 Desired
9999 Ideal
CREATE TABLE dbo.ProviderRelationship
ProviderRelationshipID INT IDENTITY(1,1) NOT NULL,
ParentProviderID INT NOT NULL,
ProviderID INT NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
ProviderRelationshipID ParentProviderID ProviderID StartDate EndDate
1 9003055 9003055 2017-03-01 2017-03-27 --providers who are the parent provider should not appear
2 1021 9003055 2017-03-27 2017-03-27
3 1021 9003055 2017-03-28 2100-01-01 --should not appear
4 184 6102 2015-07-01 2015-07-01
5 6102 6102 2015-07-02 2100-01-01
6 244 2743 2015-07-01 2100-01-01 --there is no AgreementProviderID record for this one
7 1234 9999 2018-08-01 2019-09-01
8 4321 9999 2019-10-01 2100-01-01 --this is the ideal result, since there is a gap in AgreementProvider records
CREATE TABLE dbo.Agreement
AgreementID INT IDENTITY(1,1) NOT NULL
ProviderID INT NULL, --this is the ParentProviderID
RefRegionID INT NULL
AgreementID ProviderID
1 1021
2 1021
3 184
4 184
5 184
6 184
7 244
8 244
9 244
10 1234
11 4321
CREATE TABLE dbo.AgreementProvider
AgreementProviderID INT IDENTITY(1,1) NOT NULL
AgreementID INT NULL,
ProviderID INT NULL,
StartDate DATE NULL,
EndDate DATE NULL
AgreementProviderID AgreementID ProviderID StartDate EndDate
1 1 9003055 2017-03-28 2020-06-30
2 2 9003055 2017-03-28 2020-06-30
3 10 1234 2018-08-01 2020-06-30
SELECT DISTINCT o.*
FROM Provider p
JOIN dbo.ProviderRelationship prel ON prel.ProviderID = p.ProviderID
JOIN dbo.Agreement a ON a.ProviderID = prel.ParentProviderId
JOIN dbo.AgreementProvider ap ON ap.AgreementID = a.AgreementID
WHERE prel.ProviderID <> prel.ParentProviderId
AND p.ProviderID NOT IN (SELECT ap2.ProviderID FROM dbo.AgreementProvider ap2
JOIN dbo.ProviderRelationship prel2 ON prel2.ProviderID = ap2.ProviderID
WHERE prel.ParentProviderId = prel2.ParentProviderId)
It should not compare the date to a previous date, so ProviderID 9003055 should not appear. It should also not consider ProviderID when the ParentProviderID matches. It should get both Desired and Ideal ProviderIDs: Desired does not have a record, and Ideal does not have a current record for the current ProviderRelationship.
How would I rewrite this to include the specific time frame for each ProviderRelationship compared to the AgreementProvider information for those dates?
Ideal output would be this:
ProviderID ParentProviderID RelationshipStartDate RelationshipEndDate
2743 244 2015-07-01 2100-01-01
9999 4321 2019-10-01 2100-01-01
Maybe it's already doing what I want, because there are invalid entries for the ProviderIDs that I don't want included. Maybe this is good enough, I will have to confirm.

Compare a date range from a table with another table date range and find missing period

Please find below input and required output. i need a query/procedure/function in T-SQL to get this output.
Requirement: I have table a and table b.
get all the date ranges from table b and missing date ranges from table a (when compared with table b).
Basically we need to make sure all the date ranges in table a, need to be covered in the output
Input
table b
Start date End date ID
1/1/2009 9/30/2009 1
1/1/2013 9/30/2013 1
11/1/2014 11/30/2014 1
2/2/2015 12/31/2016 1
table a
1/1/2009 12/31/2011 1
1/1/2013 9/30/2013 1
1/1/2014 4/30/2014 1
10/1/2014 12/31/2014 1
2/2/2015 12/31/9999 1
Output
table b
Start date End date ID
1/1/2009 9/30/2009 1
1/1/2013 9/30/2013 1
11/1/2014 11/30/2014 1
2/2/2015 12/31/2016 1
table a
10/1/2009 12/31/2011 1
1/1/2014 4/30/2014 1
10/1/2014 10/31/2014 1
12/1/2014 12/31/2014 1
1/1/2017 12/31/9999 1
Table [a] contain 4 records and table[b] contain 5 records. And you need that 5th record to be inserted into table [b]. If this is correct then...
Do simple outer join on table [a] and [b] on start data and end data. Get the value from table [a] where you find respective row NULL in table [b]
seems simple as it is :-)
Happy coding!!!

check the first rows date is in between the next rows date

Each row has a DateEff and a DateExp. Lets say I return 5 rows. I need to check the DateEff from the first row to see if it is in between the DateEff and DateExp of the second, third, fourth and fifth row and so on. I need to check every DateEff to make sure it is not between any rows DateEff and DateExp.
Here is a sample of what the data looks like. As you can see, row 3 DateEff is (2013-03-30) and it is in between row 4 DateEff and DateExp and row 5DateEff and `DateExp.
Table
rowid DateEff DateExp
1 1969-01-01 2012-09-30
2 2012-10-01 2012-12-31
3 2013-03-30 2014-12-31
4 2013-01-01 2015-02-10
5 2013-01-01 2999-01-01
Results would look like this
Prob Id Problem Date Affected Id Aff Date Range
3 2013-03-30 4 2013-01-01 - 2015-02-10
3 2013-03-30 5 2013-01-01 - 2999-01-01
I think this should work for you:
select
[Prob Id] = t.rowid,
[Problem Date] = t.DateEff,
[Affected Id] = a.rowid,
[Aff Date Range] = concat(a.DateEff,' - ',a.DateExp)
from tbl t -- your table is called tbl
outer apply
(
select *
from tbl -- your table is called tbl
where t.DateEff between dateeff and DateExp and rowid > t.rowid
) a
where a.DateEff is not null
order by t.rowid, t.DateEff;
With your sample data this is the result:
Prob Id Problem Date Affected Id Aff Date Range
3 2013-03-30 4 2013-01-01 - 2015-02-10
3 2013-03-30 5 2013-01-01 - 2999-01-01
4 2013-01-01 5 2013-01-01 - 2999-01-01
To get the exact output from your example (excluding row 4) change the condition in the apply to t.DateEff > dateeff and t.DateEff < DateExp and rowid > t.rowid. The output would then be:
Prob Id Problem Date Affected Id Aff Date Range
3 2013-03-30 4 2013-01-01 - 2015-02-10
3 2013-03-30 5 2013-01-01 - 2999-01-01
Join the table to itself to return overlapping pairs of rows:
Select a.RowID, a.DateEff as ProblemDate
, b.RowID as OverlapID, b.DateEff as OverlapStart, b.DateExp as OverlapEnd
from MyTable a
left join MyTable b
on a.RowID <> b.RowID
and a.DateEff <= b.DateExp
and a.DateEff >= b.DateEff

How to convert a float column into a datetime

I have a table that contains a DateField(DataType : DateTime) and TimeField(DataType : Float)
My output should be DateTime . My tables are in SQL Server 2008
Here is an example :
Table A
ID StartDate StartTime
1 2012-06-08 00:00:00.000 1223
2 2012-08-07 00:00:00.000 910
3 2012-05-02 00:00:00.000 1614
4 0094-07-13 00:00:00.000 1245
5 1994-04-18 00.00:00.000 2573
I need to get my output in such a way that I should it should validate for the correct time and correct date and append these two and insert into table B
Table B :
ID StartDateTime
1 2012-06-06 12:23:00.000
2 2012-08-07 09:10:00.000
3 2012-05-02 16:14:00.000
Note that I intentionally left rows 4 and 5 out of the result set; these rows should be ignored because they don't contain valid datetime or time data.
Have you considered correcting the design, and storing the date/time together, or at least storing date and time using the proper data types? In the meantime:
SELECT StartDate + STUFF(RIGHT('0' + RTRIM(StartTime), 4), 3, 0, ':')
FROM dbo.table
WHERE ISDATE(StartDate) = 1 AND CONVERT(INT, StartTime) < 2400
-- wow what a bunch of absolute garbage data you have
-- what Government agency are you paying to provide this data?
AND CONVERT(INT, StartTime) % 100 BETWEEN 0 AND 59;

replace NULL values with latest non-NULL value in resultset series (SQL Server 2008 R2)

for SQL Server 2008 R2
I have a resultset that looks like this (note [price] is numeric, NULL below represents a
NULL value, the result set is ordered by product_id and timestamp)
product timestamp price
------- ---------------- -----
5678 2008-01-01 12:00 12.34
5678 2008-01-01 12:01 NULL
5678 2008-01-01 12:02 NULL
5678 2008-01-01 12:03 23.45
5678 2008-01-01 12:04 NULL
I want to transform that to a result set that (essentially) copies a non-null value from the latest preceding row, to produce a resultset that looks like this:
product timestamp price
------- ---------------- -----
5678 2008-01-01 12:00 12.34
5678 2008-01-01 12:01 12.34
5678 2008-01-01 12:02 12.34
5678 2008-01-01 12:03 23.45
5678 2008-01-01 12:04 23.45
I don't find any aggregate/windowing function that will allow me to do this (again this ONLY needed for SQL Server 2008 R2.)
I was hoping to find an analytic aggregate function that do this for me, something like...
LAST_VALUE(price) OVER (PARTITION BY product_id ORDER BY timestamp)
But I don't seem to find any way to do a "cumulative latest non-null value" in the window (to bound the window to the preceding rows, rather than the entire partition)
Aside from creating a table-valued user defined function, is there any builtin that would accomplish this?
UPDATE:
Apparently, this functionality is available in the 'Denali' CTP, but not in SQL Server 2008 R2.
LAST_VALUE http://msdn.microsoft.com/en-us/library/hh231517%28v=SQL.110%29.aspx
I just expected it to be available in SQL Server 2008. It's available in Oracle (since 10gR2 at least), and I can do something similar in MySQL 5.1, using a local variable.
http://download.oracle.com/docs/cd/E14072_01/server.112/e10592/functions083.htm
You can try the following:
* Updated **
-- Test Data
DECLARE #YourTable TABLE(Product INT, Timestamp DATETIME, Price NUMERIC(16,4))
INSERT INTO #YourTable
SELECT 5678, '20080101 12:00:00', 12.34
UNION ALL
SELECT 5678, '20080101 12:01:00', NULL
UNION ALL
SELECT 5678, '20080101 12:02:00', NULL
UNION ALL
SELECT 5678, '20080101 12:03:00', 23.45
UNION ALL
SELECT 5678, '20080101 12:04:00', NULL
;WITH CTE AS
(
SELECT *
FROM #YourTable
)
-- Query
SELECT A.Product, A.Timestamp, ISNULL(A.Price,B.Price) Price
FROM CTE A
OUTER APPLY ( SELECT TOP 1 *
FROM CTE
WHERE Product = A.Product AND Timestamp < A.Timestamp
AND Price IS NOT NULL
ORDER BY Product, Timestamp DESC) B
--Results
Product Timestamp Price
5678 2008-01-01 12:00:00.000 12.3400
5678 2008-01-01 12:01:00.000 12.3400
5678 2008-01-01 12:02:00.000 12.3400
5678 2008-01-01 12:03:00.000 23.4500
5678 2008-01-01 12:04:00.000 23.4500
I have a table containing the following data. I want to update all nulls in salary columns with previous value without taking null value.
Table:
id name salary
1 A 4000
2 B
3 C
4 C
5 D 2000
6 E
7 E
8 F 1000
9 G 2000
10 G 3000
11 G 5000
12 G
here is the query that works for me.
select a.*,first_value(a.salary)over(partition by a.value order by a.id) as abc from
(
select *,sum(case when salary is null then 0 else 1 end)over(order by id) as value from test)a
output:
id name salary Value abc
1 A 4000 1 4000
2 B 1 4000
3 C 1 4000
4 C 1 4000
5 D 2000 2 2000
6 E 2 2000
7 E 2 2000
8 F 1000 3 1000
9 G 2000 4 2000
10 G 3000 5 3000
11 G 5000 6 5000
12 G 6 5000
Try this:
;WITH SortedData AS
(
SELECT
ProductID, TimeStamp, Price,
ROW_NUMBER() OVER(PARTITION BY ProductID ORDER BY TimeStamp DESC) AS 'RowNum'
FROM dbo.YourTable
)
UPDATE SortedData
SET Price = (SELECT TOP 1 Price
FROM SortedData sd2
WHERE sd2.RowNum > SortedData.RowNum
AND sd2.Price IS NOT NULL)
WHERE
SortedData.Price IS NULL
Basically, the CTE creates a list sorted by timestamp (descending) - the newest first. Whenever a NULL is found, the next row that contains a NOT NULL price will be found and that value is used to update the row with the NULL price.

Resources