SQL Select MAX and 2nd MAX - sql

I am running a query against MS SQL Server 2008 and am selecting an accountnumber and the max of the column mydate grouped by accountnumber:
select AccountNumber,
max(mydate),
from #SampleData
group by AccountNumber
I want to add a column to the result that contains the second highest mydate that is associated with the AccountNumber group. I know it would have to be something like:
select max(mydate)
from #SampleData
where mydate < (select max(mydate) from #SampleData)
But how do I get both the max and 2nd max in one select query?

You didn't specify your DBMS so this is ANSI SQL:
select accountnumber,
rn,
mydate
from (
select accountnumber,
mydate,
row_number() over (partition by accountnumber order by mydate desc) as rn
from #SampleData
) t
where rn <= 2;

Try this
Select AccountNumber,
MAX(Case when Rnum = 1 Then mydate END) mydate_1,
MAX(Case when Rnum = 2 Then mydate END) mydate_2
From
(
select
AccountNumber, mydate,
ROW_NUMBER() OVER (PARTITION By AccountNumber ORDER BY mydate DESC) as Rnum
from #SampleData
) V
Group By AccountNumber

Something like this should select the second highest:
select AccountNumber,
max(mydate),
(select max(SD2.mydate) from #SampleData SD2 where SD2.AccountNumber=#SampleData.AccountNumber AND SD2.mydate<max(#SampleData.mydate))
from #SampleData
group by AccountNumber

You could also use a TOP N clause combined with an order by:
select
TOP 2
accountnumber,
mydate,
row_number() over (partition by accountnumber order by mydate desc) as rn
from #SampleData
ORDER BY
row_number() over (partition by accountnumber order by mydate desc)

Related

How to select last record from table consider to Year and WorkingPeriod(Month)

I have a table like this:
I want last [Status] for each [Guid], consider to latest [Year] and [WorkingPeriodTitle].
By the way I know that [WorkingPeriodTitle] should be replace by [WorkingPeriodId].
With ROW_NUMBER() window function:
select
t.[PaymentAllocationGuid], t.[Status]
from (
select *,
row_number() over (partition by [PaymentAllocationGuid] order by [Year] desc, [WorkingPeriodTitle] desc) rn
from tablename
) t
where t.rn = 1
SELECT *,
LAST_VALUE(Status) OVER (PARTITION BY PaymentAllocationGuid ORDER BY Year,
WorkingPeriodTitle RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
AS LastStatus
FROM tablexyz

Return first order by each month

I am trying to modify my current query to return the first order for each month in each year.
Here is my full table and my current query.
select orderdate, sum(UnitPrice*Qty)
AS month_firstOrder_total
from OrderByDate
group by OrderDate
Try something like this:
WITH DataSource AS
(
select *
,ROW_NUMBER() OVER (PARTITION BY YEAR(orderdate), MONTH(orderdate) ORDER BY orderdate ASC) rn
from OrderByDate
)
SELECT *
FROM DataSource
WHERE rn = 1;
You can get the total sum per order date if you want, too:
WITH DataSource AS
(
select *
,ROW_NUMBER() OVER (PARTITION BY YEAR(orderdate), MONTH(orderdate) ORDER BY orderdate ASC) rn
,sum(UnitPrice*Qty) OVER (PARTITION BY orderdate) as sum_qty
from OrderByDate
)
SELECT *
FROM DataSource
WHERE rn = 1
Could you create a MONTH column?
Then you select Min(id) and order by MONTH.
I just guess
select *
from
(select min(id), month, sum(UnitPrice * Qty)
from your_table_name
group by month)

Group data by latest date per month

I store data on a daily basis in the following table
CREATE TABLE dbo.DemoTable
(
ReportDate DATE NOT NULL,
IdOne INT NOT NULL,
IdTwo INT NOT NULL,
NumberOfThings INT NOT NULL DEFAULT 0
CONSTRAINT PK_DemoTable PRIMARY KEY NONCLUSTERED (ReportDate, IdOne, IdTwo)
)
I'd like to report on this but only pull out data (sum of NumberOfThings) for the latest date we have for each month.
Example data
INSERT INTO DemoTable
(ReportDate, IdOne, IdTwo, NumberOfThings)
VALUES
('2016-11-02',1,2,2), ('2016-11-02',1,3,2), ('2016-11-01',1,2,20), ('2016-11-01',1,3,20),
('2016-10-31',1,2,2), ('2016-10-31',1,3,2), ('2016-10-30',1,2,20), ('2016-10-30',1,3,20), ('2016-10-29',1,2,200), ('2016-10-29',1,3,200),
('2016-09-30',1,2,5), ('2016-09-30',1,3,5), ('2016-09-29',1,2,55), ('2016-09-29',1,3,55)
So for this data I want to see:
2016-11-02 | 4
2016-10-31 | 4
2016-09-30 | 10
Thanks
You can use RANK() to spot the latest date rows on each month, and them sum them .
SELECT s.ReportDate,SUM(s.NumberOfThings)
FROM (
SELECT t.*,
RANK() OVER(PARTITION BY YEAR(t.ReportDate), MONTH(t.ReportDate) ORDER BY t.ReportDate DESC) as rnk
FROM DemoTable t) s
WHERE s.rnk = 1
GROUP BY s.ReportDate
You can use query like this
select ReportDate, sum(NumberofThings) as SumNumberofThings from DemoTable where ReportDate in
(
select max(ReportDate) MaxReportDate from DemoTable
group by datepart(yy,reportdate), datepart(m,reportdate)
)
group by ReportDate
A typical method involves row_number(). The only trick is using date functions to get the year and the month:
select dt.*
from (select dt.*,
row_number() over (partition by year(ReportDate), month(ReportDate)
order by ReportDate desc
) as seqnum
from DemoTable dt
) dt
where seqnum = 1;
If there are duplicates per date, you would just do the same thing with aggregation:
select dt.ReportDate, dt.NumberOfThings
from (select dt.ReportDate, sum(NumberOfThings) as NumberOfThings,
row_number() over (partition by year(ReportDate), month(ReportDate)
order by ReportDate desc
) as seqnum
from DemoTable dt
group by NumberOfThings
) dt
where seqnum = 1;
Aggregate your data so as to get the sum per date. Then rank your records by date within month. Then pick the best ranked records.
SELECT
ReportDate,
SumNumberOfThings
FROM
(
SELECT
ReportDate,
ROW_NUMBER() OVER (PARTITION BY YEAR(ReportDate), MONTH(ReportDate)
ORDER BY ReportDate DESC) AS rn
SUM(NumberOfThings) AS SumNumberOfThings
FROM DemoTable
GROUP BY ReportDate
) ranked
WHERE rn = 1
ORDER BY ReportDate;

How to add numbers to grouped columns that might repeat, in chronological order

Query:
DECLARE #EmploymentLength TABLE
(
EmployeeID INT,
Date DATE,
DateFlag CHAR(1),
RowNumber INT
);
INSERT INTO #EmploymentLength
(
EmployeeID,
Date,
DateFlag
)
SELECT z.EmployeeID,
z.Date,
z.DateFlag
FROM (SELECT EmployeeId,
HireDate AS Date,
'H' AS DateFlag
FROM dbo.Employment
WHERE EmployeeId = 328195
AND HireDate IS NOT NULL
UNION
SELECT EmployeeId,
TerminationDate AS Date,
'T' AS DateFlag
FROM dbo.Employment
WHERE EmployeeId = 328195
AND TerminationDate IS NOT NULL) z;
SELECT *
FROM #EmploymentLength
ORDER BY Date;
Result:
I need this to end up like this:
After this is done, I can group by the RowNumber to get the MAX() and MIN() of each row number group (1, 2, 3...).
If the last 2 records were "T", then I'd have 2 4's and so on.
EDIT
To clarify, I need to group each DateFlag and add a number to each group but it has to be in order ... (by date).
So in this example, you have 2 records that fall into the first group (group 1).
Then one record for group 2 (T)
Then one record for group 3 (H)
Then one record for group 4 (T)
You can do this with a difference of row_number() values to describe the group and then an additional dense_rank() to enumerate them. I think the following works:
select el.*, dense_rank() over (partition by EmployeeId order by grp)
from (select el.*,
(row_number() over (partition by EmployeeId order by date) -
row_number() over (partition by EmployeeId, DateFlag order by date)
) as grp
from #EmploymentLength el
) el;
There are situations where the grp value might actually repeat for different groups within an employee. In that case, it is better to use the minimum date for each group for the enumeration:
select el.*, dense_rank() over (partition by EmployeeId, order by grpdate)
from (select el.*, min(date) over (partition by EmployeeId, DateFlag, grp) as grpdate
from (select el.*,
(row_number() over (partition by EmployeeId order by date) -
row_number() over (partition by EmployeeId, DateFlag order by date)
) as grp
from #EmploymentLength el
) el
) el

How to select the user with max count by day

I have a table with three columns
UserID, Count, Date
I'd like to be able to select the userid with the highest count for each date.
I've tried a few different variations of queries with inline select statements but none have worked 100%, and I'm not too fond of having a select with three inline selects.
Is doing inline selects the only way to go without using temp tables? Whats the best way to tackle this?
This solution will give you multiple records if there is a tie in Count but should work.
SELECT a.Date, a.UserId, a.[Count]
FROM yourTable a INNER JOIN (
SELECT MAX([Count]) as [Count], Date
FROM yourTable
GROUP BY Date
) b ON a.[Count] = b.[Count] AND a.Date = b.Date
ORDER BY a.Date
If [Date] is in fact a [Date] column with no time component:
;WITH x AS
(
SELECT [Date], [Count], UserID, rn = ROW_NUMBER() OVER
(PARTITION BY [Date] ORDER BY [Count] DESC)
FROM dbo.table
)
SELECT [Date], [Count], UserID
FROM x
WHERE rn = 1
ORDER BY [Date];
If [Date] is a DATETIME column with a time component, then:
;WITH x AS
(
SELECT [Date] = DATEADD(DAY, DATEDIFF(DAY, '19000101', [Date]), '19000101'),
[Count], UserID, rn = ROW_NUMBER() OVER
(PARTITION BY DATEADD(DAY, DATEDIFF(DAY, '19000101', [Date]), '19000101')
ORDER BY [Count] DESC)
FROM dbo.table
)
SELECT [Date], [Count], UserID
FROM x
WHERE rn = 1
ORDER BY [Date];
If you want to pick a specific row in the event of a tie, you can add a tie-breaker to the ORDER BY within the over. If you want to include multiple rows in the case of ties, you can try changing ROW_NUMBER() to DENSE_RANK().
SELECT x.*
FROM (
SELECT Date
FROM atable
GROUP BY Date
) t
CROSS APPLY (
SELECT TOP 1 WITH TIES
UserID, Count, Date
FROM atable
WHERE Date = t.Date
ORDER BY Count DESC
) x
If Date is datetime type and can have a non-zero time component, change the t table like this:
…
FROM (
SELECT Date = DATEADD(DAY, DATEDIFF(DAY, 0, Date), 0)
FROM atable
GROUP BY DATEADD(DAY, DATEDIFF(DAY, 0, Date), 0)
) t
…
References:
TOP (Transact-SQL)
Using APPLY
for SQL 2k5
select UserID, Count, Date
from tb
where Rank() over (partition by Date order by Count DESC, UserID DESC) = 1