Select Max(Date) and next highest Max(date) from table - sql

I have a table with approx 100,000 rows per day, each containing the date they were added. However, rows are only added on business days, so no weekends or bank holidays.
I'm trying to find an efficient way to find the Max(BusinessDate) and also the next highest max(BusinessDate). However, when I try the following it takes 20 mins to run (+50Mn rows total).
SELECT
MAX(t1.BusinessDataDate) AS BusinessDataDate
, MAX(t2.BusinessDataDate) AS PreviousDataDate
FROM
cb_account t1
, cb_account t2
WHERE
t2.BusinessDataDate < t1.BusinessDataDate
Just selecting the Max(BusinessDataDate) is instant.
SELECT TOP 2
'cb_account' AS TableName
, BusinessDataDate
FROM cb_account
GROUP BY BusinessDataDate
ORDER BY BusinessDataDate DESC
will give me both the dates, but I really need them in a single row.

SELECT MAX(dates.BusinessDataDate) AS BusinessDataDate,
MIN(dates.BusinessDataDate) AS PreviousDataDate
FROM
(SELECT TOP 2
'cb_account' AS TableName
, BusinessDataDate
FROM cb_account
GROUP BY BusinessDataDate
ORDER BY BusinessDataDate DESC) dates

If I've got it right try this:
WITH T as
(
SELECT MAX(BusinessDataDate) as MaxD
FROM cb_account
)
SELECT MaxD, (SELECT MAX(BusinessDataDate)
FROM cb_account
WHERE BusinessDataDate<T.MaxD)
FROM T

What is the SQL version you are using ? if you are are on SQL 2012 you can use LEAD() or LAG() functions
http://technet.microsoft.com/en-us/library/hh213125.aspx

Related

How to filter records by them amount per date?

i have a tablet 'A' that have a column of date. and the same date can be in a few records. I'm trying to filter the records where the amount of the records by day is less than 5. And still keep all the fields of the tablet.
I mean that if i have only 4 records on 11/10/2017 I need to filter all of this 4 records.
So You can SELECT them basing at sub-query . In SUB-Query group them by this date column and then use HAVING with aggregated count to know how many in every date-group we have and then select all which have this count lesser than 5 ;
SELECT *
FROM A
WHERE A.date in (SELECT subA.date
FROM A
GROUP BY A.date
HAVING COUNT(*) < 5 );
Take Care's answer is good. Alternatively, you can use an analytic/windowing function. I'd benchmark both and see which one works better.
with cte as (
select *, count(1) over (partition by date) as cnt
from table_a
)
select *
from cte
where cnt < 5

ensure TOP (10) percent includes records from each date in range

I am attempting to perfect an Audit methodology to gather 10 percent of records from the last week to they can be audited. I currently use a CROSS APPLY to get 10 percent for each office during the period, but most of those records are from the first 2 days. In order to improve audit I want to make sure that records for each day in the range are included in the 10 percent.
SELECT t1.PIC, t1.TransID, t1.ID, t1.TranCode, t1.Doc, t1.TranDate, t1.Operator, t1.Office
FROM [dbo].[Office]
CROSS APPLY
(
SELECT TOP (10) PERCENT d2.*
FROM ##AUDIT AS d2
WHERE d2.Office = [dbo].[Office].CodeValue
ORDER BY d2.TransID
) AS t1
ORDER BY [dbo].[Office].CodeValue
This works great to get me 10 percent from each office, but I need to improve it.
Don't order by TransId, which is presumably incrementally assigned. Instead, use a where to get the date period you want and then order randomly:
SELECT . . .
FROM [dbo].[Office] CROSS APPLY
(SELECT TOP (10) PERCENT d2.*
FROM ##AUDIT AS d2
WHERE d2.Office = [dbo].[Office].CodeValue AND
d2.tranDate >= dateadd(day, -7, cast(getdate() as date))
ORDER BY newid()
) t1
Here's an alternative method, not using top...percent. Assign row numbers pseudorandomly (using newID), starting over on each new transdate. Use ceiling(tot/10.0) to get the number of records on each date that comprises at least 10% of the sample (e.g. 1 record if there were 10 or fewer records on that day, 2 records if there were between 11 and 20 records, etc), then select that many records from your initial table.
;with CTE as (
select tranDate, transID
, count(transID) over (partition by tranDate) tot
, row_number() over (partition by tranDate order by newid()) rn
from ##Audit)
select *
from CTE
where ceiling(tot/10.0) >= rn
You can modify the partition by portion of the query if you need to select 10% from each office, on each date, or other factors.

Duration between 2 dates based on another column

I currently have a table of data that shows different steps in a process, with a date/time each step was carried out.
enter image description here
What I'm looking to do is add a column that calculates the time in minutes between each step, however it has to relate to the claimID, so in the image shown I would be looking for difference between each step for the top 4 results (as they share the same claimID), then the following 6 results, etc.
Can anyone help? I'm using SQL Server
Depending on what version of SQL Server you are using you can either use a self join or the lag window function (this should work in SQL Server 2012+):
select
claimid
, statusid
, statussetdate
, coalesce(datediff(minute,
lag(statussetdate) over (partition by claimid order by statussetdate),
statussetdate
),0) as diff_in_minutes
from
your_table
order by
ClaimID
, StatusSetDate;
You can self join the table to itself using the Row_number to get the previous date and do a DateDiff on the 2 values..
;WITH cte AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY ClaimID ORDER BY StatusSetDate) Rn
FROM ClaimStatus
)
SELECT curr.*,
ISNULL(DATEDIFF(minute, prev.StatusSetDate, curr.StatusSetDate),0)
FROM cte curr
LEFT JOIN cte prev ON curr.ClaimID = prev.ClaimID AND curr.Rn = prev.Rn + 1
ORDER BY curr.ClaimID, curr.StatusSetDate

SELECT field value minus previous field value

I have a select query that gets a CarID, month, mileage and CO2 emission.
Now it gives for each month per car the mileage like this:
month 1: 5000
month 2: 5200
...
What I really need is that it takes the current value minus the previous one. I get data between a certain time frame and I already included a mileage point before that time frame. So it would be possible to get the total miles per month, I just don't know how. What I want is this.
pre timeframe: 5000
month 1: 200
month 2: 150
...
How would I do this?
edit: code, I have not yet tried anything as I have no clue how to start to do this.
resultlist as (
SELECT
CarID
, '01/01/2000' as beginmonth
, MAX(kilometerstand) as Kilometers
, MAX(Co2Emission) as CO2
FROM
totalmileagelist
GROUP BY CarID
UNION
SELECT
CarID
, beginmonth
, MAX(kilometerstand) as Kilometers
, MAX(Co2Emission) as CO2
FROM
resultunionlist
GROUP BY CarID, beginmonth
)
select * from resultlist
order by CarID, beginmonth
Edit2: explanation to the code
In the first part of the result list I grab the latest mileage per car. In the second part, after the union, I grab per month per car the latest mileage.
If you just want to subtract the previous milage, use the lag() function:
select ml.*,
(kilometerstand - lag(kilometerstand) over (partition by carid order by month)
) as diff
from totalmileagelist ml;
lag() is available in SQL Server 2012+. In earlier versions you can use a correlated subquery or outer apply.
(I missed the version because it is in the title and not on a tag.) In SQL Server 2008:
select ml.*,
(ml.mileage - mlprev.mileage) as diff
from totalmileagelist ml outer apply
(select top 1 ml2.*
from totalmileagelist ml2
where ml2.CarId = ml.CarId and
ml2.month < ml.month
order by ml2.month desc
) mlprev;
Try like this:
SELECT id, yourColumnValue,
COALESCE(
(
SELECT TOP 1 yourColumnValue
FROM table_name t
WHERE t.id> tbl.id
ORDER BY
rowInt
), 0) - yourColumnValue AS diff
FROM table_name tbl
ORDER BY
id
or like this using rank()
select rank() OVER (ORDER BY id) as 'RowId', mileage into temptable
from totalmileagelist
select t1.mileage - t2.mileage from temptable t1, temptable t2
where t1.RowId = t2.RowId - 1
drop table temptable

Select 2nd and 3rd newest row in an SQL table

i'm currently developing a news-site.
So here is the problem. I want to select the 2nd and 3rd row in a TOP 3.
SELECT TOP 3 * FROM News ORDER BY Date DESC;
I want to remove the 1st row and only return the 2nd and 3rd row.
Can anyone help?
Try this:
SELECT TOP 2 FROM
( SELECT TOP 3 * FROM News ORDER BY Date DESC ) xx
ORDER BY Date
SQLFiddle: http://www.sqlfiddle.com/#!3/dbb7e/5
You can also do this generically using window functions:
select n.*
from (SELECT n.*, row_number() over (order by date desc) as seqnum
FROM News n
) n
where n.seqnum >= 2 and n.seqnum <= 3;
I just offer this as a general solution. You can also ensure that you get everything from the second date (in case there are more than two items on that date) by using dense_rank() rather than row_number().
If you know that no two dates will be the same, you could add
where date not in (select max(date) from News)
Or you could look at rowid if you know that the first item will have rowid=0, for example if you created a temp table with the results of your initial query.
I assume, you somehow know what you Top 3 news are by ordering by date descending.
Therfore you should use the LIMIT clause with an OFFSET [For sqlite]
SELECT * FROM News ORDER BY Date DESC LIMIT 2 OFFSET 1;
Select top 2 * from News cross apply (select top 3 from news order by date desc)x