How do I write a WHERE statement to match dates on the last day of the month? - sql

I am querying a time series database table and I want to return data where the created_date is on the last day of the month (End of Month). The schema looks like this:
| int | varchar(12) | smalldatetime |
| primary key | data | created_date |
and the query I want to write is something like
SELECT * FROM my_table where create_date IS end_of_month
Is there a built in function for this in Sybase? If not, what would the logic be to create a query like this? Can it be done without writing a function?
I've been able to write a function to get the last day of the month for any given month. What I'm looking for now is a way to match on all given End of Months.

you can use dateadd and removeing one day DATEADD(day,-1,1stDayOfMonth)
-- Date format mm/dd/yyyy and calculate the last day of month m-1
-- example for 03/23/2015 dateadd calulate => 28/02/2015
DATEADD(day,-1, RTRIM (cast(datepart(mm,getdate()) as varchar(2)))+'/01/'+cast(datepart(yyyy,getdate()) as varchar(4)) )

Related

Compare date filed with month and year in Postgres

I have a date field in one of my tables and the column name is from_dt. Now I have to compare a month and year combination against this from_dt field and check whether the month has already passed. The current database function uses separate conditions for the month and the year, but this is wrong as it will compare month and year separately. The current code is like this
SELECT bill_rate, currency FROM table_name WHERE
emp_id = employee_id_param
AND EXTRACT(MONTH FROM from_dt) <= month_param
AND EXTRACT(YEAR FROM from_dt) <= year_param
Now the fromt_dt field has value 2021-10-11. If I give month_param as 01 and year_param as 2022, this condition will not work as the month 10 is greater than 1, which I have given. Basically, I need to check whether 01-2022 (Jan 2022) is greater than r equal to 2021-10-01(October 1st, 2021). It would be very much helpful if someone can shed some light here.
If you just want to check whether one date is >= then another:
# select '2022-01-01'::date >= '2021-10-11'::date;
?column?
----------
t
If you want to restrict to year/month then:
select date_trunc('month','2022-01-01'::date) >= date_trunc('month', '2021-10-11'::date);
?column?
----------
t
Where the date_trunc components are:
select date_trunc('month','2022-01-01'::date) ;
date_trunc
------------------------
2022-01-01 00:00:00-08
select date_trunc('month','2021-10-11'::date) ;
date_trunc
------------------------
2021-10-01 00:00:00-07
See Postgres date_trunc for more information.
Assuming the given year_param and month_param are integers you can use the make_date function to create the first of the year_month and date_trunc to get the first on the month from the table. Just compare those values. (See date functions) So:
select bill_rate, currency
from table_name
where emp_id = employee_id_param
and date_trunc('month',from_dt) =
make_date( year_param, month_param, 01);

Convert Year+WeekOfYear+DayOfWeek to a date

I have date values identified by a year, the week number within that year and the weekday and want to convert those into simple dates.
I couldn't find a function or another simple way to combine those, so I came up with a workaround using generate_series to get all dates in a range and JOIN the extracted values of those with my data:
SELECT data.*, days.d result
FROM ( VALUES (2017, 33, 3) ) data(d_year, d_week, d_weekday)
JOIN (
SELECT
-- the potential castdate
d::date d
-- year-week-dayofweek combination for JOINing
, EXTRACT('year' FROM d) d_year, EXTRACT('week' FROM d) d_week, EXTRACT('dow' FROM d) d_weekday
FROM generate_series('2015-01-01', '2019-12-31', INTERVAL '1day') AS days(d)
) days
USING(d_year, d_week, d_weekday)
Result is:
+--------+--------+-----------+------------+
| d_year | d_week | d_weekday | result |
+--------+--------+-----------+------------+
| 2017 | 33 | 3 | 16.08.2017 |
+--------+--------+-----------+------------+
While this works, this seems like overkill for such a simple task. Moreover, if one doesn't have a fixed range, this might not even work.
Is there an easier way to this?
demo:db<>fiddle
you can use the to_date() function, which takes an date string as argument, as well as a format pattern. So if the date string may be '2017-33-3', you could take this pattern to clarify each date part:
'IYYY-IW-ID'
'ID': The tricky part is: Does your week start with Sunday oder with Monday? This question influences the solution because it would shift the week numbers in an unexpected ways if you don't think about it. Thanks to your expected output, I saw you need 'ID' (ISO week day, week starts mondays) instead of 'D' (week day, week start sundays.)
'IW': Because we are taking the ISO week day, we need the ISO week of year as well (instead of 'WW': week of year)
'IYYY': Similar to (2)
More information about date patterns (especially the ISO thing): Postgres documentation
SELECT to_date(d_year || '-' || d_week || '-' || d_weekday, 'IYYY-IW-ID')
If you used the standard week pattern: 'YYYY-WW-D', your result would be 2017-08-13 (see fiddle)
Of course, this works also without the - characters, but it might be less readable:
SELECT to_date(d_year || d_week || d_weekday, 'IYYYIWID')

How can I calculate the number of minutes per day between a daterange

First off I apologize I do not even know where to start and haven't been able to find anything specific to this particular question.
I have a table with datetimes (start and end) and i need to find a way to get minutes/hours between those days. It could either be a sum of the time on weekdays or a some kind of pivot on each day and grouping by the ID number. I had thought to assign a value to the number of days however the times are random and do not start/end at midnight so I am at a loss as how to approach this.
Here are some examples of the date/time format if that helps.
startdate 2018-12-14 10:53:01
enddate 2018-12-27 11:50:00
Any helps or hints would be greatly appreciated!
Edit
forgot to include I am working in SQL Server (SSMS)
Editing For Additional Clarification
Here is a sample date range with an ID number, I wanted to keep it simple.
|ID number| start time |end time
|1 |12/14/2018 10:53|12/17/2018 12:00
here is what I'm trying to achieve (the separation of each date range/ID #)
ID number| start time |end time |mins|
1 | 12/14/2018 10:53|12/14/2018 23:59|786 |
1 | 12/15/2018 0:00 |12/15/2018 23:59|1439|
1 | 12/16/2018 0:00 |12/16/2018 23:59|1439|
1 | 12/17/2018 0:00 |12/17/2018 12:00|960 |
The MINUTE parameter of the DATEDIFF function can be used to determine the difference in minutes between two datetime columns. As below, the second parameter is the start date and the third parameter is the end date, with the result being the amount of time in the specified interval (days, minutes, etc.) from the start to the end date. If you need to find the number of hours between these two columns the HOUR parameter can be used for this. Grouping can be performed as well, as in the second example.
DATEDIFF:
SELECT DATEDIFF(MINUTE, StartDateColumn, EndDateColumn)
DATEDIFF with Grouping and Aggregation:
SELECT ColumnA, SUM(DATEDIFF(MINUTE, StartDateColumn, EndDateColumn)) as DifferenceInMinutes
FROM YourSchema.YourTable
GROUP BY ColumnA

Conversion failed when converting date and/or time from character string in SQL

I have the following columns in my table:
year decimal(4,0)
month decimal(2,0)
day decimal(2,0)
and I am trying to convert them as below:
SELECT (CAST (CAST(year AS varchar(4))
+CAST(month AS varchar(2))
+CAST(day AS varchar(2)
) AS date ) ) AS xDate
FROM table
ORDER BY xDate DESC
But I am getting this error:
Conversion failed when converting date and/or time from character string.
Your approach does not take into account that month or day can be a single-digit value. If you had a row like this:
year month day
---- ----- ---
2014 7 18
your method would concatenate the parts as 2014718 and the subsequent attempt to convert that into a date would result in the error in question.
You probably meant to combine the parts like this: 20140718, which would convert to date without issues. To me, the easiest way to get to that format from numerical parts is to go like this:
calculate year * 10000 + month * 100 + day;
convert the previous result to char(8);
convert the string to date.
So, in Transact-SQL it would be like this:
CAST(CAST(year * 10000 + month * 100 + day AS char(8)) AS date)
On a different note, I cannot possibly know whether you really need to store your dates split like that, of course, but if you do insist on doing so, at least consider using integer type(s) for the parts. I realise that decimal(2,0) may serve as some kind of constraint for you that makes sure that you cannot have month or day values with more than 2 digits, but it still does not protect you from having invalid months or days. And another major point is decimal(2,0) requires more storage space than even int, let alone smallint or tinyint.
So, this would seem fine to me
year int,
month int,
day int
but if you are into saving the storage space as much as possible, you could also try this:
year smallint,
month tinyint,
day tinyint
Finally, to make sure you cannot have invalid values in those columns, you could add a check constraint like below:
ALTER TABLE tablename
ADD CONSTRAINT CK_tablename_yearmonthday CHECK
ISDATE(CAST(year * 10000 + month * 100 + day AS char(8))) = 1
;
This issue is because you have redundant data in your table(Nothing wrong with your query). check following in your table.
1). values for days columns must not be greater than max number of days in associated month (e.g month 2 must not have 30 or 31 days in it).
2). value for month column must not be greater than 12 or equal to 0.

Returning all rows falling within a time period

I have a doubt in writing sql.
I had a farmerfields table with
YEAR,SEASON,Number of Fields.
and season look like this
Kharif---- 15june-15Oct
Rabi---15 oct to 15 Feb
Summer----15Feb to 15 June
now i want to write sql which returns all the rows excluding the current season in the current year. ie we should get the current season based on system date.
I am cracking my brain to get this, but could not.
Please help me.
I would suggest that you define a seasons table with three rows as above, e.g.
create table season (
season_id int,
description varchar(32),
start_day_of_month int,
start_month int
end_day_of_month int,
end_month int
)
the year is not included here just the day of month and month indices.
Your farmerfields table should then have a seaon_id column referring to this and most likely have a year column too.
Depending on your SQL vendor different date functions will be available but should should be able to compose a start and end date using the year from farmerfields and the month and day-of-month from season. Given this you can then determine if the current date falls within a given farmerfield entry's start and end dates.
Your table structure is wrong and not fit for what you need.
Instead of single field called SEASON, have two fields: SEASON_START and SEASON_END both of type Date then the query is as simple as:
Select * From [farmerfields] Where GetDate() Between SEASON_START And SEASON_END
If the names are part of your current SEASON field, add third field SEASON_NAME as well and the new structure will be:
SEASON_NAME | SEASON_START | SEASON_END
---------------------------------------
Kharif | 15june | 15Oct
Rabi | 15 oct | 15 Feb
...
Edit: in my above sample code I assumed you have SQL Server database - in case of different database you'll have different function to get current system date.
For difference of dates you can use the sql function DATEDIFF
SELECT * FROM table WHERE `date` BETWEEN '2011-10-20' AND '2011-1-1' AND DATEDIFF(`date`, '2011-10-20') % 10 = 0
I would suggest that you define a seasons table with three rows as above, e.g.
create table season (
season_id int,
description varchar(32),
start_day_of_month int,
start_month int
end_day_of_month int,
end_month int
)
The year is not included here just the day of month and month indices.
Your farmerfields table should then have a season_id column referring to this and most likely have a year column too.
Depending on your SQL vendor different date functions will be available but should should be able to compose a start and end date using the year from farmerfields and the month and day-of-month from season. Given this you can then determine if the current date falls within a given farmerfield entry's start and end dates.