Minimum of the maximum values - sql

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;

Related

penultimate date for each record

I'm struggling with creation of select which shows me penultimate date for each record in my DB.
For example:
id date
1 01.01.2018
1 05.01.2018
1 06.02.2018
2 01.06.2018
2 03.06.2018
3 12.12.2017
Out of this record I need to write select, which shows me following:
ID max_date penultimate
1 06.02.2018 05.01.2018
2 03.06.2018 01.06.2018
3 12.12.2017 NULL
Any idea how to do it? many thanks in advance
Use conditional aggregation and the ANSI-standard row_number() or dense_rank() functions:
select id,
max(date) as max_date,
max(case when seqnum = 2 then date end) as penultimate_date
from (select t.*,
dense_rank() over (partition by id order by date desc) as seqnum
from t
) t
where seqnum in (1, 2)
group by id;
Use row_number() if the dates can be the same in the event of ties.
Use GROUP BY to get the MAX and a correlated subquery with another MAX but this time lower than the former.
SELECT
T.id,
MAX(T.date) max_date,
(
SELECT
MAX(N.date)
FROM
YourTable N
WHERE
N.id = T.id AND
N.date < MAX(T.date)
) penultimate
FROM
YourTable T
GROUP BY
T.id
Just an opitimized query:
;WITH cte AS
(
SELECT id AS ID
,[date] AS max_date
,LEAD ([date], 1, 0) OVER (PARTITION BY id ORDER BY [date] DESC) AS penultimate
,ROW_NUMBER() OVER(PARTITION BY id ORDER BY [date] DESC) AS RN
FROM Table3
)
SELECT ID,max_date,penultimate
FROM cte
WHERE RN=1
SQL Fiddle
I wrote in this way,
SELECT ID
,max(StartDate) MaxDate
,(
SELECT StartDate
FROM YourTable t2
WHERE t2.id = t1.id
ORDER BY StartDate DESC OFFSET 1 ROWS FETCH NEXT 1 ROW ONLY
) penultimate
FROM YourTable t1
GROUP BY id

Returning the results of multiple 'WITH CTE' queries as one result

I am trying to select the highest price from the same product over n periods of time i.e. last 5, 50, 100, 500.
At the moment I'm running the query four times for above periods like this:
;WITH CTE AS
(
SELECT TOP (500) * FROM Ticker WHERE ProductId='BTC-USD'
ORDER BY ID DESC
) SELECT TOP (1) * FROM CTE
ORDER BY PRICE desc
Is there a way I can get all the results at once in 4 rows?
Hmmmm . . . My first thought is a union all:
with cte as (
select top (500) t.*, row_number() over (order by id desc) as seqnum
from Ticker t
where ProductId = 'BTC-USD'
order by id desc
)
select 500 as which, max(cte.price) as max_price from cte where seqnum <= 500 union all
select 100 as which, max(cte.price) from cte where seqnum <= 100 union all
select 50 as which, max(cte.price) from cte where seqnum <= 50 union all
select 5 as which, max(cte.price) from cte where seqnum <= 5;
But, I have another idea:
with cte as (
select top (500) t.*, row_number() over (order by id desc) as seqnum
from Ticker t
where ProductId = 'BTC-USD'
order by id desc
)
select v.which, x.max_price
from (values (5), (50), (100), (500)) v(which) cross apply
(select max(price) as max_price from cte where seqnum <= which) x;
Of course, the "500" in the CTE needs to match the maximum value in v. You can actually get rid of the TOP in the CTE.

Need to join 3 select queries that refers to same table

I am using SQL Server Express 2014 and I need to pull out the last record for few (3 for now) tags with different IDs from one table.
So far I made it but not at all. I am using
SELECT TOP 1 [TagItemId], [TagValue]
FROM [DB].[dbo].[Table]
where [TagItemId] like 'Random.Int1'
order by [TagTimestamp] desc
SELECT TOP 1 [TagItemId], [TagValue]
FROM [DB].[dbo].[Table]
where [TagItemId] like 'Random.Int2'
order by [TagTimestamp] desc
SELECT TOP 1 [TagItemId], [TagValue]
FROM [DB].[dbo].[Table]
where [TagItemId] like 'Random.Int3'
order by [TagTimestamp] desc
and the result is what I need, but not exactly. I need to get the three results in single table like:
TagItemId TagValue
Random.Int1 55
Random.Int2 75
Random.Int3 23`
and not like:
TagItemId TagValue
Random.Int1 55
TagItemId TagValue
Random.Int2 75
TagItemId TagValue
Random.Int3 23`
The reason is that I need to use the data for a chart.
Best regards and thanks!
You could do this using Row_Number
SELECT [TagItemId],
[TagValue]
FROM
(
SELECT [TagItemId],
[TagValue],
ROW_NUMBER() OVER (PARTITION BY [TagItemId] ORDER BY [TagTimestamp] DESC) Rn
FROM [DB].[dbo].[Table]
WHERE [TagItemId] IN ('Random.Int1','Random.Int2','Random.Int3')
) t
WHERE Rn = 1
There are several ways to accomplish this:
SELECT
MT.TagItemID,
MT.TagValue
FROM
My_Table MT
INNER JOIN
(
SELECT TagItemID, MAX(TagTimestamp)
FROM My_Table
WHERE
MT.TagItemID IN ('Random.Int1', 'Random.Int2', 'Random.Int3')
GROUP BY TagItemID) SQ ON SQ.TagItemID = MT.TagItemID
WHERE
MT.TagItemID IN ('Random.Int1', 'Random.Int2', 'Random.Int3')
Or:
SELECT
MT.TagItemID,
MT.TagValue
FROM
My_Table MT
WHERE
MT.TagItemID IN ('Random.Int1', 'Random.Int2', 'Random.Int3') AND
NOT EXISTS (SELECT * FROM My_Table MT2 WHERE MT2.TagItemID = MT.TagItemID AND MT2.Timestamp > MT.Timestamp)
Or:
;WITH CTE_WithRowNums AS
(
SELECT
MT.TagItemID,
MT.TagValue,
ROW_NUMBER() OVER(PARTITION BY TagItemID ORDER BY Timestamp DESC) AS row_num
FROM
My_Table MT
)
SELECT
TagItemID,
TagValue
FROM
CTE_WithRowNums
WHERE
row_num = 1
Could you not just do a simple UNION ALL
select * from
(SELECT TOP 1 [TagItemId], [TagValue]
FROM [DB].[dbo].[Table]
where [TagItemId] like 'Random.Int1'
order by [TagTimestamp] desc )
UNION ALL
select * from
(SELECT TOP 1 [TagItemId], [TagValue]
FROM [DB].[dbo].[Table]
where [TagItemId] like 'Random.Int2'
order by [TagTimestamp] desc )
UNION ALL
select * from
(SELECT TOP 1 [TagItemId], [TagValue]
FROM [DB].[dbo].[Table]
where [TagItemId] like 'Random.Int3'
order by [TagTimestamp] desc )
Probably crashed because each select TOP had an order by, so I "wrapped" the queries into select * so each subquery could retain its own ORDER BY clause

Select row with the earliest date

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.

Sql query for getting the price for the second latest date

I have this query:
select #cost = #cost +
(select top 1 price from
(select top 2 price, date from myTable order by date DESC)
order by date ASC
)
And I get error: Incorrect syntax near the keyword 'order'.
How to fix this?
I guess this subquery
(select top 2 price, date from myTable order by date DESC)
needs an alias, like this
(select top 2 price, date from myTable order by date DESC) sq
But I'd write it like this:
SELECT #cost = #cost + (
SELECT price FROM (
SELECT price, date, ROW_NUMBER() OVER (ORDER BY date DESC) AS rownum
) sq WHERE rownum = 2
)