SQL Server : select the minimum value from table - sql

I know it's simple question, but I still can't figure it out.
I want to find the date which is the closest date from now.
Here is my product table:
P_INDATE
----------
2013-11-03
2013-12-13
2013-11-13
Basically, it should show 2013-12-13.
I type this SELECT Max( P_INDATE) FROM product and it work.
Then, I try to use MIN((GETDATE()- P_INDATE)) in the where condition, but I fail.

Use MAX and WHERE clause along with function GETDATE():
SELECT MAX(P_INDATE)
FROM product
WHERE P_INDATE < GETDATE()
The above query gives you maximum date, which is less than current date, which you get using function GETDATE()

One way to go about this is to order the query by the difference between the stored date and the current date and take the first rows only. Using abs will allow you to find the closest date regardless of whether its before or after the current date.
SELECT TOP 1 p_indate
FROM mytable
ORDER BY ABS(GETDATE() - p_indate) ASC

Assuming you have a column which stores data and you want to show only recent one every time,why cant you use
select max(date) from yourtable which will always give you recent date

If you have an index on the column, the most efficient method is probably a bit more complicated:
SELECT TOP 1 P_INDATE
FROM ((SELECT TOP 1 P_INDATE
FROM product
WHERE P_INDATE < GETDATE()
ORDER BY P_INDATE DESC
) UNION ALL
(SELECT TOP 1 P_INDATE
FROM product
WHERE P.INDATE >= GETDATE()
ORDER BY P.INDATE
)
)
ORDER BY ABS(DATEDIFF(second, P_INDATE, GETDATE()))
The subqueries will use the index to get (at most) one row earlier and later than the current date. The outer ORDER BY then just needs to sort two rows.

Well you can try this:
SELECT TOP(1) P_INDATE
FROM [product table]
ORDER BY CASE
WHEN DATEDIFF(day,P_INDATE,GETDATE()) < 0
THEN DATEDIFF(day,GETDATE(),P_INDATE)
ELSE DATEDIFF(day,P_INDATE,GETDATE())
END ASC

Related

Between numerical values with no lower limit in Oracle SQL

My data looks something similar to:
days
weight
start date
end date
180
1
01/01/2020
null
365
0.75
01/01/2020
null
And I want to be able to select this to assign the correct value where say if the days were 0-180, they would be row 1 and 181-365 it would be row 2. If it was 365+ it would be row 2. I have already found out I can use between sql syntax for the date.
My initial code tries to do this:
select weight from (select * from table where days >= #DAYS order by days ASC) where rownum =1
But then if you do more than the last value it doesn't show anything so i've then tried to introduce a maximum element trying to find the maximum value and saying
>= #DAYS
or
>= MAX(#DAYS)
Is there a simpler way to do this?
Thanks.
select weight
from (select t.*, max(days) over () as max_day from table t) v
where days >= least(#DAY,max_day)
order by days asc
fetch first 1 row only
I'd suggest this option. When #DAY becomes larger than the largest days entry, we use max_days instead.
Select max(weight) from table
Where days=(Select max(days) from table
Where days >= #DAYS)
The first max() function is defensive in case your table has 2 entries with the same days number.

SQL server: Get record with date closest to given date

I have a table dbo.studies with datetime column studydate
I want to query the database using the datetime variable givendate to find the record closest to the datetime in column studydate
Using:
SELECT TOP 1 *
FROM studies
WHERE studies.studydate < givendate
ORDER BY studies.studydate DESC
Will result in the record that is less and closest to givendate, but I need the record closest to givendate, regardless of whether it's less or more then studydate
Any thoughts on how to find it?
One method is:
SELECT TOP 1 s.*
FROM studies s
ORDER BY ABS(DATEDIFF(day, s.studydate, #givendate));
This uses DATEDIFF() to get the closest date. Note that this is using day for the difference. If your "dates" have a time component, you might want a different date part.
Note that this will not take advantage of indexes. A faster method (if you have the indexes) is a bit more complicated:
SELECT TOP (1) s.*
FROM ((SELECT TOP 1 s.*
FROM studies s
WHERE s.studydate <= #givendate
ORDER BY s.studydate DESC
) UNION ALL
(SELECT TOP 1 s.*
FROM studies s
WHERE s.studydate > #givendate
ORDER BY s.studydate ASC
)
) s
ORDER BY DATEDIFF(day, s.studydate, #givendate));
Although this is more complicated, each subquery can use an index on studydate. The final sort would have only two rows, so it should be really fast.
SELECT TOP 1 *
FROM studies
ORDER BY ABS(DATEDIFF(second, #givendate, studies.studydate))
use datediff function in order by it will always return the nearest 1
SELECT TOP 1 *
FROM studies
ORDER BY DATEDIFF(dd,studies.studydate, givendate) ASC
Order By ABS(DATEDIFF(day, YourDate, GetDate()))
Is The Best Method For Get Distinct and unique Recod When Your Table is Many Row and one column different
SELECT * FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ColumnName ORDER BY ABS(DATEDIFF(day, YourDate, GetDate()))) RowNumber
FROM [tableName]
)A
WHERE RowNumber = 1

Need to count unique transactions by month but ignore records that occur 3 days after 1st entry for that ID

I have a table with just two columns: User_ID and fail_date. Each time somebody's card is rejected they are logged in the table, their card is automatically tried again 3 days later, and if they fail again, another entry is added to the table. I am trying to write a query that counts unique failures by month so I only want to count the first entry, not the 3 day retries, if they exist. My data set looks like this
user_id fail_date
222 01/01
222 01/04
555 02/15
777 03/31
777 04/02
222 10/11
so my desired output would be something like this:
month unique_fails
jan 1
feb 1
march 1
april 0
oct 1
I'll be running this in Vertica, but I'm not so much looking for perfect syntax in replies. Just help around how to approach this problem as I can't really think of a way to make it work. Thanks!
You could use lag() to get the previous timestamp per user. If the current and the previous timestamp are less than or exactly three days apart, it's a follow up. Mark the row as such. Then you can filter to exclude the follow ups.
It might look something like:
SELECT month,
count(*) unique_fails
FROM (SELECT month(fail_date) month,
CASE
WHEN datediff(day,
lag(fail_date) OVER (PARTITION BY user_id,
ORDER BY fail_date),
fail_date) <= 3 THEN
1
ELSE
0
END follow_up
FROM elbat) x
WHERE follow_up = 0
GROUP BY month;
I'm not so sure about the exact syntax in Vertica, so it might need some adaptions. I also don't know, if fail_date actually is some date/time type variant or just a string. If it's just a string the date/time specific functions may not work on it and have to be replaced or the string has to be converted prior passing it to the functions.
If the data spans several years you might also want to include the year additionally to the month to keep months from different years apart. In the inner SELECT add a column year(fail_date) year and add year to the list of columns and the GROUP BY of the outer SELECT.
You can add a flag about whether this is a "unique_fail" by doing:
select t.*,
(case when lag(fail_date) over (partition by user_id order by fail_date) > fail_date - 3
then 0 else 1
end) as first_failure_flag
from t;
Then, you want to count this flag by month:
select to_char(fail_date, 'Mon'), -- should aways include the year
sum(first_failure_flag)
from (select t.*,
(case when lag(fail_date) over (partition by user_id order by fail_date) > fail_date - 3
then 0 else 1
end) as first_failure_flag
from t
) t
group by to_char(fail_date, 'Mon')
order by min(fail_date)
In a Derived Table, determine the previous fail_date (prev_fail_date), for a specific user_id and fail_date, using a Correlated subquery.
Using the derived table dt, Count the failure, if the difference of number of days between current fail_date and prev_fail_date is greater than 3.
DateDiff() function alongside with If() function is used to determine the cases, which are not repeated tries.
To Group By this result on Month, you can use MONTH function.
But then, the data can be from multiple years, so you need to separate them out yearwise as well, so you can do a multi-level group by, using YEAR function as well.
Try the following (in MySQL) - you can get idea for other RDBMS as well:
SELECT YEAR(dt.fail_date) AS year_fail_date,
MONTH(dt.fail_date) AS month_fail_date,
COUNT( IF(DATEDIFF(dt.fail_date, dt.prev_fail_date) > 3, user_id, NULL) ) AS unique_fails
FROM (
SELECT
t1.user_id,
t1.fail_date,
(
SELECT t2.fail_date
FROM your_table AS t2
WHERE t2.user_id = t1.user_id
AND t2.fail_date < t1.fail_date
ORDER BY t2.fail_date DESC
LIMIT 1
) AS prev_fail_date
FROM your_table AS t1
) AS dt
GROUP BY
year_fail_date,
month_fail_date
ORDER BY
year_fail_date ASC,
month_fail_date ASC

Comparing dates using SQL

I have a date that looks like this: 2014-10-01 12:35:29.440
the table looks like this:
ORDER 1 | 2014-07-31 00:00:00.000
ORDER 2 | 2015-07-31 00:00:00.000
sorry i wanted ORDER 2 to show up.. As my get date returns todays date and that is GREATER than 2014-07-31 00:00:00.000
Here is what i have tried:
SELECT TOP 1 NAME
FROM ORDER_DATES
WHERE GETDATE() > ORDER_DATE
ORDER BY NAME DESC
Your question still isn't quite worded in a way that is conducive to what you need... but I think I understand what you want now based on the comments.
Based on the comment:
IF it doesnt match the date then it needs to return the next row.
Which is ORDER 2
Something like this should work:
SELECT TOP 1 name
FROM ORDER_DATES o
INNER JOIN (
-- This subquery finds the first date that occurs *after* the current date
SELECT MIN(ORDER_DATE) AS ORDER_DATE
FROM ORDER_DATES
WHERE ORDER_DATE > GETDATE()
) minDateAfterToday ON o.ORDER_DATE = minDateAfterToday.ORDER_DATE
ORDER BY name
This would work a lot better if you had an ID field in the table, but this should work with the given data, you'll potentially run into issues if you have two orders on the exact same date.
EDIT:
here's a fiddle showing the query in action:
http://sqlfiddle.com/#!6/f3057/1
DATEDIFF will come handy, also you have to order by ORDER_DATE:
SELECT TOP 1 NAME
FROM ORDER_DATES
WHERE DATEDIFF(DAY,ORDER_DATE,GETDATE())>0
ORDER BY ORDER_DATE DESC
You can write as:
SELECT NAME
FROM ORDER_DATES
WHERE cast(GETDATE()as date) > cast (ORDER_DATE as date)
ORDER BY NAME DESC
Demo
Check if you are querying against right table
declare #dt datetime = cast('2014-10-01 12:35:29.440' as datetime), #dt2 datetime= cast('2014-07-31 00:00:00.000' as datetime);
print(case when #dt > #dt2 then 1 else 0 end);
This piece of script shows output 1 i.e. condition should match for ORDER 1.
Verify if you are missing some thing.
Edit as per change to original question:
here the condition needed be reverted as date value is in future which is greater than current date
new query will be as
SELECT TOP 1 NAME
FROM ORDER_DATES
WHERE ORDER_DATE > GETDATE()
ORDER BY NAME DESC

Select X Most Recent Non-Consecutive Days Worth of Data

Anyone got any insight as to select x number of non-consecutive days worth of data? Dates are standard sql datetime. So for example I'd like to select 5 most recent days worth of data, but there could be many days gap between records, so just selecting records from 5 days ago and more recent will not do.
Following the approach Tony Andrews suggested, here is a way of doing it in T-SQL:
SELECT
Value,
ValueDate
FROM
Data
WHERE
ValueDate >=
(
SELECT
CONVERT(DATETIME, MIN(TruncatedDate))
FROM
(
SELECT DISTINCT TOP 5
CONVERT(VARCHAR, ValueDate, 102) TruncatedDate
FROM
Event
ORDER BY
TruncatedDate DESC
) d
)
ORDER BY
ValueDate DESC
I don't know the SQL Server syntax, but you need to:
1) Select the dates (with time component truncated) in descending order
2) Pick off top 5
3) Obtain 5th value
4) Select data where the datetime >= 5th value
Something like this "pseudo-SQL":
select *
from data
where datetime >=
( select top 1 date
from
( select top 5 date from
( select truncated(datetime) as date
from data
order by truncated(datetime) desc
)
order by date
)
)
This should do it and be reasonably good from a performance standpoint. You didn't mention how to handle ties, so you can add the WITH TIES clause if you need to do that.
SELECT TOP (#number_to_return)
* -- Write out your columns here
FROM
dbo.MyTable
ORDER BY
MyDateColumn DESC