Retrieving max date from multiple columns, for dates before today - sql

I've written some code to extract the latest date from multiple columns.
select (select max(LatestDate)
from (values (col1),(col2),(col3)) as updatedate(LatestDate)
) as LatestDate
from table1
However, I only want to take the date if it's before today. When I run the code for the sample dates below, it gives me the latest date as 10/04/2019 which is after today.
The date that i'd want it to extract is 14/03/2019 (col2) as it's before today, and is the latest date of all the columns whose date is before today.
Today = 27/03/2019
col1 = 02/02/2019
col2 = 14/03/2019
col3 = 10/04/2019
Can anyone advise on this? Hope it makes sense.
Many thanks
afk

You can use APPLY with WHERE clause :
select t.*, tt.LatestDate
from table1 t outer apply
( select max(LatestDate) as LatestDate
from ( values (col1),(col2),(col3) ) as updatedate(LatestDate)
where LatestDate < convert(date, GETDATE())
) tt;

You can use the below code for achieving the same.
select (select max(LatestDate)
from (values (col1),(col2),(col3)) as updatedate(LatestDate)
where updatedate < CAST(GETDATE() AS DATE)
) as LatestDate
from table1

Try this - you can take the MAX date that is less than or equal to today:
with cte as
(
select *
from (values (col1),(col2),(col3)) as updatedate
)
select (
select max(updatedate)
from cte
where updatedate <= GETDATE()
) as LatestDate
from table1

Add a where clause. I would phrase the query using cross apply:
select max(LatestDate)
from table1 t1 cross apply
(values (t1.col1), (t1.col2), (t1.col3)
) updatedate(LatestDate)
where updateddate.LatestDate < getdate();

Related

SQL Server extend table and add date in a date range for every row

I have a table like this:
Id user
-------
1 A
2 B
I want to extend it and add date in a range for every row like below:
Id user date
------------------
1 A 20190101
1 A 20190102
2 B 20190101
2 B 20190102
A simple cross join with a calendar table should work here:
WITH dates AS (
SELECT '20190101' AS dt UNION ALL
SELECT '20190102'
)
SELECT
t.Id,
t.user,
d.dt AS date
FROM yourTable t
CROSS JOIN dates d;
You can use a lateral join. In SQL Server, this uses the cross apply syntax:
select t.*, v.dte
from t cross apply
(values (convert(date, '2019-01-01')) (convert(date, '2019-01-02'))
) v(dte);
Please try this:
DECLARE #FromDate DATE = '2019-01-01', #ToDate DATE = '2019-01-02';
;WITH rs AS (SELECT #FromDate dt UNION ALL SELECT DATEADD(DAY,1,dt) FROM rs WHERE dt<#ToDate)
SELECT t.Id,t.[user],rs.dt AS [date]
FROM [YourTableName] t
CROSS APPLY rs
ORDER BY t.[user],rs.dt
OPTION (MaxRecursion 0)
;
Thanks for Tim’s response and I have an idea:
WITH thedates AS
(
SELECT CAST(#startdate AS DATETIME) AS thedates
UNION ALL
SELECT DATEADD(DAY,1,thedates)
FROM thedates
WHERE DATEADD(DAY,1,thedates)<= CAST(#enddate AS DATETIME)
)
SELECT t.user_id,t.user_name,CONVERT(NVARCHAR(10),d.thedates,112)
FROM [dbo].[test] t
CROSS JOIN thedates d

Ignoring Duplicate Records SQL

In need of some help :)
So I have a table of records with the following columns:
Key (PK, FK, int) DT (smalldatetime) Value (real)
The DT is a datetime for every half hour of the day with an associated value
E.g.
Key DT VALUE
1000 2010-01-01 08:00:00 80
1000 2010-01-01 08:30:00 75
1000 2010-01-01 09:00:00 100
I have a Query that finds the max value every 24 hour period and its associated time however, on one day the max value occurs twice and hence duplicates the date which is causing processing issues. I have tried using rownumber() which works but I can't use a calculated column in my where clause?
Currently I have:
SELECT cast(T1.DT as date) as 'Date',Cast(T1.DT as time(0)) as 'HH', ROW_NUMBER() over (PARTITION BY cast(DT as date) ORDER BY DT) AS 'RowNumber'
FROM TABLE_1 AS T1
INNER JOIN (
SELECT CAST([DT] as date) as 'DATE'
, MAX([VALUE]) as 'MAX_HH'
FROM TABLE_1
WHERE DT > '6-nov-2016' and [KEY] = '1000'
GROUP BY CAST([DT] as date)
) AS MAX_DT
ON MAX_DT.[DATE] = CAST(T1.[DT] as date)
AND T1.VALUE = MAX_DT.MAX_HH
WHERE DT > '6-nov-2016' and [KEY] = '1000'
ORDER BY DT
This results in
Key DT VALUE HH
1000 2010-01-01 80 07:00:00
1000 2010-02-01 100 17:30:00
1000 2010-02-01 100 18:00:00
I need to remove the duplicate date (I Have no preference which HH it takes)
I think I've explained that terribly, let me know if it makes no sense and i'll try and re write
Any ideas?
Can you try this the new code is in ** **:
SELECT cast(T1.DT as date) as 'Date', ** MIN(Cast(T1.DT as time(0))) as 'HH' **
FROM TABLE_1 AS T1
INNER JOIN (
SELECT CAST([DT] as date) as 'DATE'
, MAX([VALUE]) as 'MAX_HH'
FROM TABLE_1
WHERE DT > '6-nov-2016' and [KEY] = '1000'
GROUP BY CAST([DT] as date)
) AS MAX_DT
ON MAX_DT.[DATE] = CAST(T1.[DT] as date)
AND T1.VALUE = MAX_DT.MAX_HH
WHERE DT > '6-nov-2016' and [KEY] = '1000'
here put the group by
GROUP BY cast(T1.DT as date)
ORDER BY DT
i would do something like this
i didnt try it but i think it s correct.
SELECT cast(T1.DT as date) as 'Date',Cast(T1.DT as time(0)) as 'HH', VALUE
FROM TABLE_1 T1
WHERE [DT] IN (
--select the max date from Table_1 for each day
SELECT MAX([DT]) max_date FROM TABLE_1
WHERE (CAST([DT] as date) ,value) IN
(
SELECT CAST([DT] as date) as 'CAST_DATE'
,MAX([VALUE]) as 'MAX_HH'
FROM TABLE_1
WHERE DT > '6-nov-2016' and [KEY] = '1000'
GROUP BY CAST([DT] as date
)group by [DT]
)
WHERE DT > '6-nov-2016' and [KEY] = '1000'
Change the JOIN to an APPLY.
The APPLY operation will allow you to limit the connected relation to just one result for each source relation.
SELECT v.[Key], cast(v.DT As Date) as "Date", v.[Value], cast(v.DT as Time(0)) as "HH"
FROM
( -- First a projection to get just the exact dates you want
SELECT DISTINCT [Key], CAST(DT as DATE) as DT
FROM Table_1
WHERE [Key] = '1000' AMD DT > '20161106'
) dates
CROSS APPLY (
-- Then use APPLY rather than JOIN to find just the exact one record you need for each date
SELECT TOP 1 *
FROM Table_1
WHERE [Key] = dates.[Key] AND cast(DT as DATE) = dates.DT ORDER BY [Value] DESC
) v
A final note: Both this query and your sample query in the question will include values from Nov 6, 2016. The query says > 2016-11-05 with an exlusive inequality, but the original was still comparing using full DateTime values, meaning there is a implied 0 as a time component. So 12:01 AM on Nov 6 is still greater than 12:00:00.001 AM on Nov 6. If you want to exclude all Nov 6 dates from the query, you either need to change this to use a time value at the end of the date, or cast to date before making that > comparison.
With SQL you can use SELECT DISTINCT,
The SELECT DISTINCT statement is used to return only distinct (different) values.
Inside a table, a column often contains many duplicate values; and sometimes you only want to list the different (distinct) values.
The SELECT DISTINCT statement is used to return only distinct (different) values.

Find missing date as compare to calendar

I am explain problem in short.
select distinct DATE from #Table where DATE >='2016-01-01'
Output :
Date
2016-11-23
2016-11-22
2016-11-21
2016-11-19
2016-11-18
Now i need to find out missing date a compare to our calender dates from year '2016'
i.e. Here date '2016-11-20' is missing.
I want list of missing dates.
Thanks for reading this. Have nice day.
You need to generate dates and you have to find missing ones. Below with recursive cte i have done it
;WITH CTE AS
(
SELECT CONVERT(DATE,'2016-01-01') AS DATE1
UNION ALL
SELECT DATEADD(DD,1,DATE1) FROM CTE WHERE DATE1<'2016-12-31'
)
SELECT DATE1 MISSING_ONE FROM CTE
EXCEPT
SELECT * FROM #TABLE1
option(maxrecursion 0)
Using CTE and get all dates in CTE table then compare with your table.
CREATE TABLE #yourTable(_Values DATE)
INSERT INTO #yourTable(_Values)
SELECT '2016-11-23' UNION ALL
SELECT '2016-11-22' UNION ALL
SELECT '2016-11-21' UNION ALL
SELECT '2016-11-19' UNION ALL
SELECT '2016-11-18'
DECLARE #DATE DATE = '2016-11-01'
;WITH CTEYear (_Date) AS
(
SELECT #DATE
UNION ALL
SELECT DATEADD(DAY,1,_Date)
FROM CTEYear
WHERE _Date < EOMONTH(#DATE,0)
)
SELECT * FROM CTEYear
WHERE NOT EXISTS(SELECT 1 FROM #yourTable WHERE _Date = _Values)
OPTION(maxrecursion 0)
You need to generate the dates and then find the missing ones. A recursive CTE is one way to generate a handful of dates. Another way is to use master..spt_values as a list of numbers:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
),
d as (
select dateadd(day, n.n, cast('2016-01-01' as date)) as dte
from n
where n <= 365
)
select d.date
from d left join
#table t
on d.dte = t.date
where t.date is null;
If you are happy enough with ranges of missing dates, you don't need a list of dates at all:
select date, (datediff(day, date, next_date) - 1) as num_missing
from (select t.*, lead(t.date) over (order by t.date) as next_date
from #table t
where t.date >= '2016-01-01'
) t
where next_date <> dateadd(day, 1, date);

get date range between dates

I have following table tbl in database and I have dynamic joining date 1-1-2012 and I want this date is between (Fall and spring) or (spring and summer) or (summer and fall).I want query in which i passed only joining date which return semestertime and joining date in Oracle.
Semestertime joiningDate
Fall 10-13-2011
Spring 2-1-2012
Summer 6-11-2012
Fall 10-1-2015
If I understand your question correctly:
SELECT *
FROM your_table
WHERE joiningDate between to_date (your_lower_limit_date_here, 'mm-dd-yyyy')
AND to_date (your_upper_limit_date_here, 'mm-dd-yyyy`);
What about something like that:
select 'BEFORE' term,
t."Semestertime", to_char(t."joiningDate", 'MM-DD-YYYY')
from (
select tbl.*, rownum rn from tbl where tbl."joiningDate" < to_date('1-1-2012','MM-DD-YYYY')
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- your reference date
order by tbl."joiningDate" desc) t
where rn = 1
union all
select 'AFTER' term,
t."Semestertime", to_char(t."joiningDate", 'MM-DD-YYYY')
from (
select tbl.*, rownum rn from tbl where tbl."joiningDate" > to_date('1-1-2012','MM-DD-YYYY')
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- your reference date
order by tbl."joiningDate" asc) t
where rn = 1
This will return the "term" before and after a given date. You will probably have to adapt such query to your specific needs. But that might be a good starting point.
For example, given your business rules, you might consider using <= instead of <. You you might require to have the result displayer a column instead of rows. Bu all of this shouldn't be too had to change.
As an alternate solution using CTE and sub-queries:
with testdata as (select to_date('1-1-2012','MM-DD-YYYY') refdate from dual)
select v.what, tbl.* from tbl join
(
select 'BEFORE' what, max(t1."joiningDate") d
from tbl t1
where t1."joiningDate" < to_date('1-1-2012','MM-DD-YYYY')
union all
select 'AFTER' what, min(t1."joiningDate") d
from tbl t1
where t1."joiningDate" > to_date('1-1-2012','MM-DD-YYYY')
) v
on tbl."joiningDate" = v.d
See http://sqlfiddle.com/#!4/c7fa5/15 for a live demo comparing those solutions.

Valid price at given date

got a table with dates and prices.
Date Price
2012-01-01 25
2012-01-05 12
2012-01-10 10
Is there some kind of function that lets me find what the current price where at '2012-01-07'? Without me knowing of the other dates.
Pseudoquery: select price where currentprice('2012-01-07')
Thanks!
MySQL:
select price from your_table
where date <= '2012-01-07'
order by date desc
limit 1
SQL Server:
select top 1 price from your_table
where date <= '2012-01-07'
order by date desc
If you don't have use of ROW_NUMBER(), and want a generic solution, you need to join on a sub-query.
Get the date you want, then get the data for that date.
SELECT
*
FROM
yourTable
INNER JOIN
(
SELECT MAX(yourDate) AS maxDate FROM yourTable WHERE yourDate <= #dateParameter
)
AS lookup
ON yourTable.yourDate = lookup.maxDate
select price
from table1 t
where t.date = ( select max(t2.date)
from table1 t2
where t2.date <= '2012-01-07' )
Note this is not the copy&paste answer, as we're not not knowing what is the datatype for your date column.