I have a large table with the following columns and sample values:
ID Ser Reg Date
1 12345 001 1/3/2011
1 12345 001 2/2/2011
1 12345 002 1/3/2011
1 12345 002 2/2/2011
2 23456 001 1/3/2011
2 23456 001 2/7/2011
2 23456 001 3/5/2011
I tried this query from a previous post SQL - Select next date query - but did not get the desired results:
SELECT
mytable.id,
mytable.date,
(
SELECT
MIN(mytablemin.date)
FROM mytable AS mytablemin
WHERE mytablemin.date > mytable.date
) AS NextDate
FROM mytable
This is what I am trying to accomplish:
ID Ser Reg curr_Date prev_Date
1 12345 001 2/2/2011 1/3/2011
1 12345 002 2/2/2011 1/3/2011
2 23456 001 2/7/2011 1/5/2011
2 23456 001 3/5/2011 2/7/2011
I would appreciate any help with this task.
if you are using oracle database (as you have not mentioned then I can assume anything)
then you can use lead and lag function/command for this ..
select id,ser, reg, curr_date ,prev_date
from
(
select id,ser, reg, ser, date curr_date
LEAD(date, 1, 0) OVER (PARTITION BY id,ser, reg, curr_date ORDER BY date DESC NULLS LAST) prev_date,
)
where prev_date is not null;
There was a condition missing from correlated subquery joining mytablemin copy of mytable table with mytable. Also you would eliminate records which do not have NextDate - but this might give incorrect results in case when only one record in group (Id, Ser, Reg) exists by eliminating it from result set.
select * from
(
SELECT
mytable.id,
mytable.date,
(
SELECT
MIN(mytablemin.date)
FROM mytable AS mytablemin
WHERE mytablemin.date > mytable.date
and mytablemin.id = mytable.id
and mytablemin.Ser = mytable.Ser
and mytablemin.Reg = mytable.Reg
) AS NextDate
FROM mytable
) a
where a.NextDate is not null
And here is version using derived table with aggregation:
SELECT
mytable.id,
mytable.date,
mytablemin.minDate
FROM mytable
inner join
(
SELECT mytablemin.id,
mytablemin.Ser,
mytablemin.Reg,
MIN(mytablemin.date) minDate
FROM mytable AS mytablemin
group by mytablemin.id,
mytablemin.Ser,
mytablemin.Reg
having MIN(mytablemin.date) is not null
) AS mytablemin
on mytablemin.id = mytable.id
and mytablemin.Ser = mytable.Ser
and mytablemin.Reg = mytable.Reg
Related
I'm attempting to create a new table based on the following table:
SubjectNumber TestDates
001 11/12/12
001 01/10/15
001 04/03/13
002 05/21/14
003 08/06/15
002 09/12/18
002 03/30/12
003 09/07/18
004 10/14/11
005 02/05/14
005 02/06/14
I need a new table that will include the following:
1) Subject number
2) Their first test date
3) Their last test date
4) A count of the total number of tests
5) A column with 0's and 1's indicating whether or not the subject had any two test dates that were at least 30 days apart
The new table should look like the following:
SubjectNumber FirstTestDate LastTestDate TestCount ThirtyDaysApart
001 11/12/12 01/10/15 3 1
002 03/30/12 09/12/18 3 1
003 08/06/15 09/07/18 2 1
004 10/14/11 1 0
005 02/05/14 02/06/14 2 0
I'm using SQL Server 2017.
I have a temporary table called #Temp1 that I'd like to store the data in. This table is called #Temp.
Insert into #Temp1
SELECT SubjectNumber, WHERE
CASE MIN(TestDates) then FirstTestDate = TestDates
END
CASE MAX(TestDates) then LastTestDate = TestDates
END
FROM #Temp;
You can use lag() and conditional aggregation:
select subjectnumber, min(testdate), max(testdate),
max(case when prev_testdate < dateadd(day, -30, testdate) then 1 else 0 end) as diff30
from (select t.*,
lag(testdate) over (partition by subjectnumber order by testdate) as prev_testdate
from t
) t
group by subjectnumber;
You can try using lag() function
select subjectnumber,min(TestDates),max(TestDates),count(TestDates),
case when count(case when pdatediff=30 then 1 end)>=2 then 1 else 0 end as ThirtyDaysApart
from
(
select subjectnumber,TestDates,COALESCE (DATEDIFF(DAY,
LAG(TestDates) OVER (PARTITION BY subjectnumber
ORDER BY TestDates), TestDates) ,0) as pdatediff
from tablenmae
)X group by subjectnumber
The only tricky part is checking if two dates within a group are 30 days apart. Note that the following query returns 1 if any two dates, not necessarily consecutive, are 30 days apart:
WITH cte AS (
SELECT SubjectNumber, MIN(TestDates) FirstTestDate, MAX(TestDates) LastTestDate, COUNT(TestDates) TestCount
FROM #yourdata
GROUP BY SubjectNumber
)
SELECT *
FROM cte AS t
CROSS APPLY (
SELECT CASE WHEN COUNT(*) = 0 THEN 0 ELSE 1 END AS ThirtyDaysApart
FROM #yourdata AS o
INNER JOIN #yourdata AS n ON o.SubjectNumber = n.SubjectNumber AND n.TestDates >= DATEADD(DAY, 30, o.TestDates)
WHERE o.SubjectNumber = t.SubjectNumber
) AS CA
DB Fiddle
I want to get a prior date for each row for each person (ID) in SQL Server 2008. I have several million rows. Example:
ID TransactionDate
1 01/01/2012
1 01/20/2012
1 01/22/2012
2 01/20/2012
2 01/23/2012
2 01/25/2012
Returns:
ID TransactionDate priorDate
1 01/01/2012 Null
1 01/20/2012 01/01/2012
1 01/22/2012 01/20/2012
2 01/20/2012 null
2 01/23/2012 01/20/2012
2 01/25/2012 01/23/2012
just use a self join CTE (left) with row_number:
WITH CTE AS (
SELECT ID, TransactionDate,
ROW_NUMBER() OVER ( PARTITION BY ID order by TransactionDate ) AS RowNumber
FROM table_name
)
SELECT a.ID,a.TransactionDate , aa.TransactionDate priorDate
FROM CTE a
LEFT OUTER JOIN CTE aa
ON a.RowNumber = aa.RowNumber + 1
And aa.ID=a.ID
SQLFIDDLE DEMO
i have a table that contains:
itemid inventdimid datephysical transrefid
10001 123 2015-01-02 300002
10002 123 2015-01-03 3566
10001 123 2015-02-05 55555
10002 124 2015-02-01 4545
The result i want
itemid inventdimid datephysical transrefid
10001 123 2015-02-05 555
10002 123 2015-01-03 3566
10002 124 2015-02-01 4545
MY query:
SELECT a.itemid,a.inventdimid,max(a.datephysical),a.transrefid
FROM a where dataareaid = 'ermi'
group by a.itemid,a.inventdimid
it is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Use the ANSI standard row_number() function:
select t.*
from (select t.*,
row_number() over (partition by itemid, inventdimid
order by datephysical desc) as seqnum
from table t
) t
where seqnum = 1;
Find max(a.datephysical) for each itemid, inventdimid combination, select all rows from that date.
SELECT itemid, inventdimid, datephysical, transrefid
FROM a a1
where dataareaid = 'ermi'
and datephysical = (select max(datephysical)
from a a2
where a1.itemid = a2.itemid
and a1.inventdimid = a2.inventdimid
and a2.dataareaid = 'ermi')
You have to create a temporary table with your GROUP BY and then join the original table with it.
Try this:
SELECT T1.*,T2.datephysical,T2.transrefid FROM
(SELECT itemid,inventdimid
FROM TableName
GROUP BY itemid,inventdimid) T1 JOIN
(SELECT itemid,inventdimid,datephysical,transrefid
FROM TableName) T2 ON T1.itemid=T2.itemid AND T1.inventdimid=T2.inventdimid
I'm assuming you want the transrefid corresponding with the a.datephysical shown? This would be done by turning the column into a subquery:
SELECT a.itemid,a.inventdimid,max(a.datephysical),
(SELECT b.transrefid FROM MY_TABLE b where
b.datareaid = 'ermi' and b.itemid = a.itemid and b.inventdimid = a.itemid
and b.datephysical = max(a.datephysical)) as transrefid
FROM MY_TABLE a where dataareaid = 'ermi'
group by a.itemid, a.inventdimid
Some databases may not support this syntax though and it will fail if there are more than one records with the same date.
Having a table TAB_A:
ID | Date | Value
--------------------------------
101 | 2014-03-01 | 101000001
101 | 2014-03-03 | 101000003
101 | 2014-03-06 | 101000006
102 | 2014-03-01 | 102000001
103 | 2014-03-01 | 103000001
And, for example, this single record in another table TAB_B:
ID | Date | TAB_A.Id
-----------------------------------
40002 | 2014-03-05 | 101
I need to get the closest (most recent) TAB_A.Value to TAB_B.Date field (which in this case would be '101000003' and NOT '101000006').
I've been searching for other responses with similar scenarios (like this one), but this is not exactly what I need.
Any suggestions? Thanks in advance for your help.
EDIT: I forgot to mention that TAB_A has over 200K records and TAB_B has over 55M records.
Seeing as the tag is sql-server, Limit won't work. Instead, use top
SELECT TOP 1 ID, Date, Value
FROM TAB_A
WHERE Date < (SELECT Date from TAB_B where ID=40002)
ORDER BY Date DESC
or
SELECT ID, Date, Value
FROM tab_a
WHERE date=
(SELECT MAX(date)
FROM TAB_A
WHERE Date <
(SELECT Date
FROM TAB_B
WHERE ID=40002)
)
If you want just 1 result in the last query, use DISTINCT. For example, if the date you were looking for is 2014-03-01, the 2nd query would show you 3 examples, with distinct just 1. In the first query, top 1 already ensures that you just have 1 result
.
EDIT: updated for the comment below:
SELECT b.id, b.date, a.value FROM
(SELECT TOP 1 ID, Date, Value
FROM TAB_A
WHERE Date < (SELECT Date from TAB_B B where ID=40002)
ORDER BY Date DESC) a
,
(SELECT id,date,[TAB_A.id] FROM tab_b )b
WHERE a.id=b.[TAB_A.id]
Excuse my capital letters/small letters inconsistency...
SELECT ID, Date, Value
FROM TAB_A
WHERE Date < (SELECT Date from TAB_B where ID=40002)
ORDER BY Date DESC LIMIT 1
First you select this date which you need from TAB_B in a subquery. Then you select all these dates that are earlier than this from TAB_B (you can modify to <= if you need). Then you order descending by date and select TOP 1 (the highest one). I think that you could also use MAX (but I am not sure).
Try this:
SELECT TOP 1 * FROM (
SELECT A.ID,A.Value, MIN(DATEDIFF(day,A.Date,B.Date)) as MinDiff
FROM TAB_A A, TAB_B B
GROUP BY A.ID,A.Value ) as T
WHERE MinDiff>0
ORDER BY MinDiff
Result:
ID VALUE MINDIFF
101 101000003 2
See result in SQL Fiddle.
Explanation:
Inner query will select ID,Value and minimum date difference. With the outer query, we can select the record having minimum date difference which is greater than 0.
You should have an index on date in tab_a for this to perform well (requires sqlserver 2008+):
declare #tab_a table(id int, Date date, value int)
insert #tab_a values (101,'2014-03-01',101000001),
(101,'2014-03-03',101000003),(101,'2014-03-06',101000006),
(102,'2014-03-01',102000001),(103,'2014-03-01',103000001)
declare #tab_b table(id int, Date date, tab_a_id int)
insert #tab_b
values
( 40002, '2014-03-05', 101 ), ( 40002, '2014-03-02', 101 )
select b.ID, b.Date bdate, x.Date adate, x.value
from #tab_b b
outer apply
(select top 1 value, date
from #tab_a a
where a.date <= b.date
and a.id = b.TAB_A_Id
order by a.date desc
) x
Result:
ID bdate adate value
40002 2014-03-05 2014-03-03 101000003
40002 2014-03-02 2014-03-01 101000001
The following code will show the first record for the given condition.In your case it will return what u needed..!
SELECT Value
FROM TAB_A
WHERE DATE < (SELECT Date from TAB_B WHERE ID= '40002') and ROWNUM <= 1
ORDER BY DATE;
I have a history of records (multiple records per update all with the exact same datetime) that share an IdString.
I want a query to determine which of these records are part of the most recent update group.
This query will show me one of the records having the most recent update date, but for each partition, I need all the records with that max date.
;with cte as(
select ROW_NUMBER() over (partition by IdString order by UpdateDate desc) as [rn], *
from MyTable
)
select CASE WHEN (cte.rn = 1) THEN 0 ELSE 1 END [IsOld], *
from MyTable m
inner join cte on cte.RecordId= m.RecordId
Would someone please help me figure out an appropriate query?
EDIT: Sample
(IsOld is the desired calculated value)
IsOld RecordId IdString UpdateDate
1 1 ABC 2011-06-16
1 2 ABC 2012-05-30
1 3 ABC 2008-12-31
0 4 ABC 2012-06-08
1 5 ABC 2011-01-16
0 6 ABC 2012-06-08
1 7 ABC 2012-06-07
1 8 XYZ 2001-01-16
1 9 XYZ 2013-01-30
0 10 XYZ 2001-01-31
1 11 XYZ 2013-06-01
1 12 XYZ 2001-05-04
0 13 XYZ 2013-01-30
SELECT CASE WHEN updateDate = maxDate THEN 0 ELSE 1 END isOldRecord, RecordID, IDString, UpdateDate
FROM
(
select m.RecordID, m.IDString, m.updateDate, MAX(UpdateDate) OVER (PARTITION BY IDString) maxDate
from MyTable m
) A
Try this -
;WITH cte AS(
SELECT RANK() OVER(PARTITION BY IdString ORDER BY UpdateDate DESC) AS [row_num], *
FROM MyTable
)
SELECT CASE WHEN m.[row_num] = 1 THEN 0 ELSE 1 END isOld, *
from cte m