Select Max() is too slow - sql

I am trying to get the latest date from a column in SQL Server 2016 but it takes near 10 seconds to retrieve this data. The table has over 146 million records.
Combined with other queries that I have to run the code takes over 1 minute to complete.
How can I make this query faster?
DECLARE #MaxDate DATE
SELECT #MaxDate = MAX(MyDate)
SELECT COUNT(*) FROM MyTable
I have even tried
SELECT c.*
FROM (SELECT MyDate, COUNT(*) as TheDate,
RANK() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM MyTable
GROUP BY MyDate
) c
WHERE seqnum = 1;
and it is the same speed

If you want the max date quickly, then create an index on that column, (mydate).
You should be able to do:
select max(mydate)
from t;
Or if you want all the rows:
select top (1) with ties t.*
from t
order by mydate desc;

Related

Get Top 3 Records By Date By Day SQL Server 2012

I have table with rows that look like this:
DateTime, Field1, Field2, Field3
I want to get the TOP 3 records by date, by day. For one record I would execute
SELECT TOP(3) *
FROM tum
I need that for each day. I am assuming I would use partition or cross apply, but the actual syntax for this is not clear to me.
You would use row_number():
select t.*
from (select t.*,
row_number() over (partition convert(date, datetime) order by ?) as seqnum
from t
) t
where seqnum <= 3;

SQL Query Pattern Selecting

Table part
I need to select all from the rows where the first 7 characters of the Assoc.Ref column are the same on a specific day.
Result example
You need aggregation :
SELECT t.col
FROM table t
GROUP BY t.col
HAVING COUNT(*) > 1;
If you want exactly two rows for each then use COUNT(*) = 2 instead .
If you want all rows then you can use windows function :
SELECT t.*
FROM (SELECT t.*,
COUNT(*) OVER(PARTITION BY col) AS cnt
FROM table t
) t
WHERE t.cnt > 1;
EDIT : After made update on question you might need LEFT() :
SELECT t.*
FROM (SELECT t.*,
COUNT(*) OVER(PARTITION BY CAST(Date_created AS date), LEFT(associated_ref, 7)) AS cnt
FROM table t
) t
WHERE t.cnt > 1 AND CAST(t.Date_created AS date) = '2019-02-08';
If the Date_created has no time then no conversation is needed. Just use Date_created instead.

SQL Earliest hour for every day

I have table like this
And I want to have only earliest time from column time for each day from column date. Rest of table has to be unaffected.
So result would be that for example that I have only time 9:25 for 2018-07-13 and rest of rows with later times for 2018-07-13 are deleted
To delete you can use a CTE with ROW_NUMBER window function
;WITH cteDups
AS(
SELECT *, RN=ROW_NUMBER()OVER (PARTITION BY M.Date ORDER BY M.Time ASC)
FROM dbo.yourtable M
)
--SELECT *
DELETE
FROM cteDups D WHERE D.RN > 1
You can use a window function to return all rows
select
*,
min([time]) over (partition by [date] order by [time])
from YourTable
Or just the aggregate to remove them
select *
from YourTable
inner join
(select whatever, min(FullDate) dt
from yourtable
group by whatever) x on x.whatever = YourTable.whatever and x.dt = YourTable.FullDate
If the whatever column doesn't matter, and you only want the date and time:
Select
[date],
min([time])
from YourTable
group by [Date]
The simplest way to keep certain records and remove the rest would be by using a CTE with a windowing function to rank (or add rownumbers). Check this out:
;WITH EarliestHourEveryDay AS (
SELECT
whatever
,FullDate
,[date]
,[time]
,rn = ROW_NUMBER() OVER (PARTITION BY [date] ORDER BY [time])
FROM TableName
)
SELECT *
FROM EarliestHourEveryDay
WHERE rn = 1
/*
DELETE FROM EarliestHourEveryDay
WHERE rn > 1
*/
I have commented out the delete statement so that you can test this first. Run the CTE as-is, and if the result set contains the exact rows which you want, remove the SELECT statement from the CTE and uncomment the DELETE statement and you'll be good to go.
Group by day and select the MIN time.
Use the MIN function and a GROUP BY clause.
Something like:
SELECT date, MIN(time) AS EarliestTime
FROM MyTable
GROUP BY date
ORDER BY date ASC
Here is an example of this working: SQL Fiddle

Retrieve next to last date SQL Server

I think this is a simple problem but I cannot find an easy answer. I need to retrieve the last 2 entries by date. I have used max() to get the latest date; but do not know how to retrieve the next most recent.
The stored procedure code for latest date is:
SELECT *
FROM Table
WHERE Date=(SELECT MAX(Date) FROM Table);
So using a separate procedure how do I get the next most recent?
You can use order by and top:
select top 2 t.*
from t
order by date desc;
Or just to get the next most recent only as you stated...thus returning only one row...
select top 1 t.*
from t
where t.date != (select max(date) from table)
order by date desc;
or...
with cte as(
select
t.*
,row_number() over (order by t.date desc) as RN
from table t)
select *
from cte
where RN = 2

Pagination of large dataset

I have a query that returns a large (10000+ rows) dataset. I want to order by date desc, and display the first 40 results. Is there a way to run a query like this that only retrieves those 40 results without retrieving all 10000 first?
I have something like this:
select rownum, date, * from table
order by date desc
This selects all the data and orders it by date, but the rownum is not in order so it is useless for selecting only the first 40.
ROW_NUMBER() over (ORDER BY date desc) AS rowNumber
^ Will display a rownumber in order, but I can't use it in a where clause because it is a window function. I could run this:
select * from (select ROW_NUMBER() over (ORDER BY date desc) AS rowNumber,
rownum, * from table
order by date desc) where rowNumber between pageStart and pageEnd
but this is selecting all 10000 rows. How can I do this efficiently?
SELECT *
FROM (SELECT *
FROM table
ORDER BY date DESC)
WHERE rownum <= 40
will return the first 40 rows ordered by date. If there is an index on date that can be used to find these rows, and assuming statistics are up to date, Oracle should choose to use that index to identify the 40 rows that you want and then do 40 single-row lookups against the table to retrieve the rest of the data. You could throw a /*+ first_rows(40) */ hint into the inner query if you want though that shouldn't have any effect.
For a more general discussion on pagination queries and Top N queries, here's a nice discussion from Tom Kyte and a much longer AskTom discussion.
Oracle 12c has introduced a row limiting clause:
SELECT *
FROM table
ORDER BY "date" DESC
FETCH FIRST 40 ROWS ONLY;
In earlier versions you can do:
SELECT *
FROM ( SELECT *
FROM table
ORDER BY "date" DESC )
WHERE ROWNUM <= 40;
or
SELECT *
FROM ( SELECT *,
ROW_NUMBER() OVER ( ORDER BY "date" DESC ) AS RN
FROM table )
WHERE RN <= 40;
or
SELECT *
FROM TEST
WHERE ROWID IN ( SELECT ROWID
FROM ( SELECT "Date" FROM TEST ORDER BY "Date" DESC )
WHERE ROWNUM <= 40 );
Whatever you do, the database will need to look through all the values in the date column to find the 40 first items.
You don't need a window function. See
http://www.techonthenet.com/oracle/questions/top_records.php
for an answer to your problem.