Returning all rows falling within a time period - sql

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.

Related

How to get all months between two dates in sql with data

Please suggest a method where in we could retrieve name of all months in between two dates.
The months may or may not contain data, but as the need is to display monthly trend, we are required to fetch all months in between two date ranges with or without data.
The Output will be like:
Jan | Feb | Mar
----------------------
Data | Data| Data
If you use SQL Server, you can use the MONTH() or DATEPART() function to extract the month from a date. For example, the following statement returns the current month in SQL Server: SELECT MONTH(CURRENT_TIMESTAMP); SELECT DATEPART(month, CURRENT_TIMESTAMP);
You could then do a GROUP BY to determine how many events occur per month
In general "dynamic columns" are much harder to achieve than "dynamic rows" so output that looks like the first of the tables below will be easier to achieve than output that looks like the second.
Easier
Month | Data
-------------
Jan | Data
Feb | Data
Mar | Data
Harder
Jan | Feb | Mar
----------------------
Data | Data | Data
In general, the best approach is to have your SQL queries return data in the first type of structure and then if required transform this to the second type in your "presentation layer", which might be in Excel, PowerBI, SSRS, or on a website, for example.
If you have months where there might be no data to be returned, you need some means of generating the months and then outer joining this to the data. Obviously in general, at least behind the scenes you'll want to be including years in your data in addition months as otherwise all the data from Jan 2020 will be grouped together with that from Jan 2021, which is probably not what you want.
Here is a SQL-Server-friendly snippet which will output all the months (and years) between two dates. At the time of writing, you haven't specified a DBMS, so if you aren't on SQL Server, this may not work for you.
DECLARE #datefrom DATE = '2020-06-01'
DECLARE #dateto DATE = '2021-03-01';
WITH cte AS
(
SELECT #datefrom as MyDate
UNION ALL
SELECT DATEADD(month,1,MyDate)
FROM cte
WHERE DATEADD(month,1,MyDate)<=#dateto
)
SELECT
YEAR(cte.MyDate) CalendarYear,
MONTH(cte.MyDate) CalendarMonth,
DATENAME(month, cte.MyDate) as MonthNameFull,
CONVERT(char(3),cte.MyDate,0) as MonthName3Char
FROM
cte
ORDER BY
Year(cte.MyDate),
Month(cte.MyDate)
The final SELECT query could be OUTER JOINed to your actual data to give you your desired results. Pivoting should then be done in the presentation layer.
An alternative to the cte would be to use a "numbers table" and use that to add a number of months to the start date and limit the output to where the result of adding that number of months is between the two provided dates.

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

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)) )

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.

SQL query to search by day/month/year/day&month/day&year etc

I have a PostgreSQL database with events. Each event has a datetime or an interval. Common data are stored in the events table and dates are stored in either events_dates (datetime field) or events_intervals (starts_date, ends_date both are date fields).
Sample datetime events
I was born on 1930-06-09
I got my driver's license on 1950-07-12
Christmas is on 1900-12-24 (1900 is reserved for yearly reoccuring events)
Sample interval events
I'll be on vacation from 2011-06-09 till 2011-07-23
Now I have a user that will want to look up these events. They will be able to fill out a form with from and to fields and in those fields they can enter full date, day, month, year, day and month, day and year, month and year in one or both fields.
Sample queries
From May 3 to 2012 December 21 will look for events between May 3 and December 21 whose max year is 2012
From day 3 to day 15 will look for events between the 3rd and 15th day of every month and year
From day 3 will look for events on the 3rd day of every month and year (same if from is empty and to is not)
From May 3 to June will look for events between May 3 and last day of June of every year
etc.
Any tips on how to write a maintanable query (it doesn't necessarily have to be fast)?
Some things that we thought of
write all possible from, to and day/month/year combinations - not maintable
compare dates as strings e.g. input: ____-06-__ where _ is a wildcard - I wouldn't have to generate all possible combinations but this doesn't work for intervals
You can write maintainable queries that additionally are fast by using the pg/temporal extension:
https://github.com/jeff-davis/PostgreSQL-Temporal
create index on events using gist(period(start_date, end_date));
select *
from events
where period(start_date, end_date) #> :date;
select *
from events
where period(start_date, end_date) && period(:start, :end);
You can even use it to disallow overlaps as a table constraint:
alter table events
add constraint overlap_excl
exclude using gist(period(start_date, end_date) WITH &&);
write all possible from, to and day/month/year combinations - not maintable
It's actually more maintainable than you might think, e.g.:
select *
from events
join generate_series(:start_date, :end_date, :interval) as datetime
on start_date <= datetime and datetime < end_date;
But it's much better to use the above-mentioned period type.

Determine monthly values of timestamped records

I have a SQL table with the following schema:
fruit_id INT
price FLOAT
date DATETIME
This table contains many records where the price of a given fruit is recorded at a given time. There may be multiple records in a single day, there may be
I would like to be able to fetch a list of prices for a single fruit over the last 12 months inclusive of the current month. So given a fruit_id of 2 and datetime of now(), what would the price values be for December, January, February, ... October, November?
Given the above requirements, what strategy would you use to get this data? Pure sql, fetch all prices and process in code?
Thanks for you time.
Are you talking about min price, max price, average price, or something else?
Here's a quick query to get you started, which includes min, max, and average price for each month for fruit_id 2:
select left(date,7) as the_month, min(price),max(price),avg(price)
from fruit_price
where fruit_id = 2
and date >= concat(left(date_sub(curdate(), interval 11 month),7),'-01')
group by the_month;
If I understand it correctly from -
I would like to be able to fetch a list of prices for a single fruit over the last 12 months inclusive of the current month. So given a fruit_id of 2 and datetime of now(), what would the price values be for December, January, February, ... October, November?
You want the total price for every month for a single year based on the date and fruit_if you pass in.
So,this won't give all months of an year but all months which had a price for year..in case you want all months..you would need to create a dimdate table which will have all the dates...and then join with it..
declare #passeddate=Now() --date to be calculated
declare #fruit_id=2 --fruit id to be calculated
Select
fruit_id as FruitId,
Sum(price) as MonthPrice,
Month(date) as FruitMonth
from SQL_Table
group by FruitMonth,FruitId
where fruit_id=#fruit_id and
Year(date)=Year(#passeddate)
select month(date) as "Month", distinct(price) as "Unique Price" where fruit_id = 2 group by month(date);
I'd try to state as much as possible in SQL that does not require unindexed access to data because it's usually fast(er) than processing it with the application.