I've got a query in which I am returning multiple rows for a given event, for example:
ID | DateTime | String
1017436 | 2013-09-13 05:19:20.000 | Hello
1017436 | 2013-09-13 11:49:00.000 | World
I want the result to contain only the earliest occurrences of the row for any given ID, but am running into trouble.
I originally, thought a query like this would be the answer:
; WITH cte AS
( SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY ixBug ORDER BY dt)
FROM dbo.BugEvent
)
SELECT ixBug, dt, s
FROM cte
WHERE
ixBug IN (SELECT Bug.ixBug
FROM
dbo.Bug
JOIN
dbo.Mailbox ON Mailbox.ixMailbox = Bug.ixMailbox
WHERE
ixBug = '1017436'
AND
YEAR(dtOpened) >= '2013'
AND
MONTH(dtOpened) = '09'
AND
sOriginalTitle NOT LIKE '\[web\]%' ESCAPE '\'
AND
dbo.Bug.ixProject = (SELECT ixProject
FROM dbo.Project
WHERE sProject = 'Support')
AND
dbo.Bug.ixCategory = (SELECT ixCategory
FROM dbo.Category
WHERE sCategory = '.inquiry')
AND
Bug.ixBug NOT IN(SELECT Bug.ixBug
FROM
dbo.Bug
JOIN
dbo.Mailbox ON Mailbox.ixMailbox = Bug.ixMailbox
WHERE
YEAR(dtOpened) >= '2013'
AND
MONTH(dtOpened) <= '09'
AND
sOriginalTitle LIKE '\[web\]%' ESCAPE '\'
AND
dbo.Bug.ixProject = (SELECT ixProject
FROM dbo.Project
WHERE sProject = 'Support')
AND
dbo.Bug.ixCategory = (SELECT ixCategory
FROM dbo.Category
WHERE sCategory = '.inquiry')))
AND
sVerb = 'Incoming Email';
But, for some reason the result keeps both rows.
You can use window functions for this, either ROW_NUMBER() or MIN(). The idea is to partition the rows by the ID - OVER (PARTITION BY id) - and then either assign row numbers (ordered by the datetime) or find the minimum datetime per ID.
Solution with ROW_NUMBER():
; WITH cte AS
( SELECT id, datetime, string,
rn = ROW_NUMBER() OVER (PARTITION BY id ORDER BY datetime)
FROM tableX
)
SELECT id, datetime, string
FROM cte
WHERE rn = 1 ;
and with MIN():
; WITH cte AS
( SELECT id, datetime, string,
min_datetime = MIN(datetime) OVER (PARTITION BY id)
FROM tableX
)
SELECT id, datetime, string
FROM cte
WHERE datetime = min_datetime ;
The second version has slightly different behaviour. If there are two (or more) rows with exactly same datetime for an ID, they will be both in the result.
You can use ROW_NUMBER() which generates sequential number that you can filter with.
SELECT ID, DateTime, String
FROM
(
SELECT ID, DateTime, String,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY DateTime) RN
FROM tableName
) a
WHERE RN = 1
SQLFiddle Demo
Select the min date for each ID in a common table expression CTE, then join back to it.
with minDates (id, date)
as
(
select id, min(date) as date from YourTable
group by id
)
select yt.*
from YourTable yt
inner join minDates md on yt.id = md.id and yt.date = md.date
select top 1 id, date_time, string
from table
where id = ?
order by date_time
Select
ID
, DateTime
, String
From MyTable t1
Where t1.DateTime =
(
Select Min(t2.DateTime)
From MyTable t2
Where td.ID = t1.ID
)
Use a subquery to get the min DateTime for each ID group, then use that to qualify which of the IDs you pull. In case of tie, though, you will get both. If the IDs are in time sequence, you could avoid this by using the ID rather than the DateTime as the selector.
Related
For the below table
ID DATE FIELD1 FIELD2 FIELD4
123456 01.07.2014 00:00:00 EMPLOYER GROUPS TMC SELECT CARE HMO
123789 01.07.2017 00:00:00 EMPLOYER GROUPS MQC SELECT CARE HMO
How to select only one row that have max(date)? i.e. 01.07.2017
select *
from theTable
where Date = (select max(date) from theTable)
Should do it. Add a top 1 if multiple rows have the same date.
In Oracle 12+, you can use:
select t.*
from t
order by date desc
fetch first 1 row only;
In earlier versions, you an use a subquery:
select t.*
from (select t.*
from t
order by date desc
) t
where rownum = 1;
If you need more than one record with the same maximum date:
select t.*
from t
where t.date = (select max(t2.date) from t);
For tsql / SQL Server you can use the below
DECLARE #max_date datetime;
SELECT #max_date = max(DATE) from table_name;
SELECT TOP 1 * FROM table_name WHERE DATE = #max_date;
The 'TOP 1' makes sure you only receive 1 row.
Which row this is will arbitrary though, unless you add an 'ORDER BY' statement in your query.
Alternatively
dense_rank() function may be used as :
select dt, ID
from
(
select dense_rank() over (order by "Date" desc) as dr,
"Date" as dt,
ID
from tab
)
where dr = 1;
or if you're sure about there's no tie(for the column "Date"), even row_number() function may be used :
select dt, ID
from
(
select row_number() over (order by "Date" desc) as rn,
"Date" as dt,
ID
from tab
)
where rn = 1;
SQL Fiddle Demo
I have a little sql query, like so
SELECT * FROM table
This returns a bunch of results, i output the following fields:
ID
UserID
Amount
Date
What i want to do is get the most recent entry from each UserID ( based on ID ), then if the amount is 0 do not return ANY results from that UserID.
select t1.*
from your_table t1
join
(
select userid, max(date) as mdate
from your_table
group by userid
having sum(case when amount = 0 then 1 else 0 end) = 0
) t2 on t1.userid = t2.userid and t1.date = t2.mdate
In the subquery you group by the user and select only those having no amount of zero. In that select you use max(date) as mdate to get the latest date for each user.
That subquery can be joined to the original table to get the complete record and not just the userid.
try this
WITH cte AS
(
SELECT
MAX(ID) OVER (PARTITION BY UserID) MaxIDForUserID,
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY ID DESC) rn,
UserID,
Amount,
Date
FROM TableName
)
SELECT * FROM cte WHERE rn = 1 AND Amount != 0
Consider the below results
Datetime1 DateTime2 Customer
2013-06-19 2011-03-30 IP003779
2014-04-24 2011-03-30 IP003779
2011-03-30 2009-03-18 IP003779
i need to select the minimum of the first column out of the maximums of the second column.
-> 2013-06-19
I'm having a hard time figuring out the query, combining min and max. any thoughts?
Something like this ought to do, I think, to find the min of the max for each customer:
select Customer = t.Customer ,
DateTime2_Max = t.dt2Max ,
DateTime1_Min = min( x.DateTime1 )
from ( select Customer = Customer ,
dt2Max = max( DateTime2 )
from some_table
group by Customer
) t
join some_table x on x.Customer = t.Customer
and x.DateTime2 = t.dt2Max
group by t.Customer ,
t.dt2Max
If you want to look at the table overall, then it gets simpler:
select DateTime2_Max = t.dt2Max ,
DateTime1_Min = min( x.DateTime1 )
from ( select dt2Max = max( DateTime2 )
from some_table
) t
join some_table x on x.DateTime2 = t.dt2Max
group by t.dt2Max
You could also use windowing functions. Broken out by customer, it looks something like:
select *
from ( select * ,
rank = row_number() over (
partition by Customer
order by DateTime2 desc ,
DateTime1 asc
)
) t
where t.rank = 1
order by 1,2,3
And again, simpler if you look at the table as a whole:
select top 1 *
from ( select * ,
rank = row_number() over (
order by DateTime2 desc ,
DateTime1 asc
)
) t
where t.rank = 1
I think this is what you want:
select top 1 *
from table t
order by DateTime2 desc, DateTime1 asc;
Edit:
If you need to do this for all customers, use row_number():
select t.*
from (select t.*,
row_number() over (partition by customer order by datetime2 desc, datetime1 asc) as seqnum
from table t
) t
where seqnum = 1;
So I have data like this:
UserID CreateDate
1 10/20/2013 4:05
1 10/20/2013 4:10
1 10/21/2013 5:10
2 10/20/2012 4:03
I need to group by each user get the average time between CreateDates. My desired results would be like this:
UserID AvgTime(minutes)
1 753.5
2 0
How can I find the difference between CreateDates for all records returned for a User grouping?
EDIT:
Using SQL Server 2012
Try this:
SELECT A.UserID,
AVG(CAST(DATEDIFF(MINUTE,B.CreateDate,A.CreateDate) AS FLOAT)) AvgTime
FROM #YourTable A
OUTER APPLY (SELECT TOP 1 *
FROM #YourTable
WHERE UserID = A.UserID
AND CreateDate < A.CreateDate
ORDER BY CreateDate DESC) B
GROUP BY A.UserID
This approach should aslo work.
Fiddle demo here:
;WITH CTE AS (
Select userId, createDate,
row_number() over (partition by userid order by createdate) rn
from Table1
)
select t1.userid,
isnull(avg(datediff(second, t1.createdate, t2.createdate)*1.0/60),0) AvgTime
from CTE t1 left join CTE t2 on t1.UserID = t2.UserID and t1.rn +1 = t2.rn
group by t1.UserID;
Updated: Thanks to #Lemark for pointing out number of diff = recordCount - 1
since you're using 2012 you can use lead() to do this
with cte as
(select
userid,
(datediff(second, createdate,
lead(CreateDate) over (Partition by userid order by createdate)
)/60) datdiff
From table1
)
select
userid,
avg(datdiff)
from cte
group by userid
Demo
Something like this:
;WITH CTE AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY CreateDate) RN,
UserID,
CreateDate
FROM Tbl
)
SELECT
T1.UserID,
AVG(DATEDIFF(mi, ISNULL(T2.CreateDate, T1.CreateDate), T1.CreateDate)) AvgTime
FROM CTE T1
LEFT JOIN CTE T2
ON T1.UserID = T2.UserID
AND T1.RN = T2.RN - 1
GROUP BY T1.UserID
With SQL 2012 you can use the ROW_NUMBER function and self-join to find the "previous" row in each group:
WITH Base AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY CreateDate) RowNum,
UserId,
CreateDate
FROM Users
)
SELECT
B1.UserID,
ISNULL(
AVG(
DATEDIFF(mi,B2.CreateDate,B1.CreateDate) * 1.0
)
,0) [Average]
FROM Base B1
LEFT JOIN Base B2
ON B1.UserID = B2.UserID
AND B1.RowNum = B2.RowNum + 1
GROUP BY B1.UserId
Although I get a different answer for UserID 1 - I get an average of (5 + 1500) / 2 = 752.
This only works in 2012. You can use the LEAD analytic function:
CREATE TABLE dates (
id integer,
created datetime not null
);
INSERT INTO dates (id, created)
SELECT 1 AS id, '10/20/2013 4:05' AS created
UNION ALL SELECT 1, '10/20/2013 4:10'
UNION ALL SELECT 1, '10/21/2013 5:10'
UNION ALL SELECT 2, '10/20/2012 4:03';
SELECT id, isnull(avg(diff), 0)
FROM (
SELECT id,
datediff(MINUTE,
created,
LEAD(created, 1, NULL) OVER(partition BY id ORDER BY created)
) AS diff
FROM dates
) as diffs
GROUP BY id;
http://sqlfiddle.com/#!6/4ce89/22
For example I have the following database entries:
timestamp | value1 | value 2
----------
1452|5|7
1452|1|6
1452|2|7
1623|1|2
1623|5|6
1623|4|5
1623|4|7
1855|1|2
Now I want to have a sql query which returns me value1 only for the timestamp which is availble the most. Therefore it should return only the timestamp 1623 and it's values.
I was first thinking of count, but that will return only the number of the availability and not the entries.
select *
from T
inner join (select timestamp
from T
group by timestamp
order by count(*) desc
limit 1) t2
on T.timestamp = t2.timestamp
see it's working live in a sqlfiddle
WITH CTE AS (
SELECT *, COUNT(timestamps) OVER (PARTITION BY value1, timestamps) AS cnt
FROM mytable
), cte2 as (select *, row_number() over (partition by value1 order by cnt DESC, timestamps) as Rn FROM cte)
SELECT value1, timestamps , cnt FROM CTE2 WHERE Rn = 1;