Presto SQL get yyyymm minus 2 months - sql

I am using Presto. I have an integer column (let's call the column 'mnth_nbr') showing year and month as: yyyymm. For instance, 201901. I want to have records showing all dates AFTER 201901 as well as 2 months before the given date. In this example, it would return 201811, 201812, 201901, 201902, 201903, etc. Keep in mind that my data type here is integer.
This is what I have so far (I do a self join):
select ...
from table 1 as first_table
left join table 1 as second_table
on first_table.mnth_nbr = second_table.mnth_nbr
where first_table.mnth_nbr <= second_table.mnth_nbr
I know this gives me all dates AFTER 201901, including 201901. But, I don't know how to add the 2 previous months (201811 and 201812)as explained above.

As far as the documentation, Presto DB date_parse function expects a MySQL-like date format specifier.
So the proper condition for your use case should be :
SELECT ...
FROM mytable t
WHERE
date_parse(cast(t.mnth_nbr as varchar), '%Y%m') >= date '2019-01-01' - interval '2' month
Edit
As commented by Piotr, a more optimized expression (index-friendly) would be :
WHERE
mnth_nbr >= date_format(date '2019-01-01' - interval '2', '%Y%m')

Something like this would help. first parse your int to date
date_parse(cast(first_table.mnth_nbr as varchar), 'yyyymm') > date '2019-01-01' - interval '2' month
please keep in mind that you may encounter with indexing issues with this approach.

Related

DB2 How to select dates for the previous 2 months

I am using Oracle SQL developer on DB2 and have a date field stored as an integer e.g. 20210401
I want to bring back results for the last 2 months and have tried this:
select * from table where date > add_months(sysdate, -2)
This is producing error 206 saying it is not valid in the context used.
Does anyone know how to convert the data column or have an easier way to filter for the last 2 months
Use this:
select *
from table
where date > INT (TO_CHAR (CURRENT TIMESTAMP - 2 MONTH, 'YYYYMMDD'));

How do I select only the month end date from a table

I am new to SQL, here is my problem.
I have a table with daily dates:
Date:
20190101
20190102
20190103
.
**20190131**
20190201
20190202
20190203
.
**20190228**
20190301
20190302
20190303
.
**20190331**
I want to select only the month-end dates, what would be the code to do that?
thanks
I am using MS SQL Studio.
One method in standard SQL would be:
select t.*
from t
where extract(month from date + interval '1' day) <> extract(month from date);
Date/time functions vary significantly by database, so the exact functions might not match your database. However, the idea is simple: add one day and see if the month changes.
In standard SQL, you could do:
select date
from mytable
where date = date_trunc('month', date) + interval '1' month - interval '1' day
Edit
In SQL Server, you can just use eomonth(). Given a date, this functions returns the corresponding end of month, which you can compare against the date. So:
select date
from mytable
where date = eomonth(date)

How to write a SQL query to retrieve all those customers whose age in months is more than 200 months?

How to write a SQL/Oracle query to retrieve all those customers whose age in months is more than 200 months?
I have a exam on Monday but I am having some confusion with months and dates calculation.
You can use a Query like this for MySQL:
SELECT *
FROM yourTable
WHERE bithdayField <= NOW() - INTERVAL 200 MONTH;
The logic is the same (the date is older than today minus 200 months), but the actual SQL is usually different, because DBMSes have a large variation of syntax in the date/time area.
Standard SQL & MySQL:
WHERE datecol < current_date - interval '200' month
Oracle:
WHERE datecol < add_months(current_date, -200)
In fact Oracle also supports the Standard SQL version, but it's not recommended, because you might get an invalid date error when you do something like '2018-03-31' - interval '1' month. This is based on a (dumb) Standard SQL rule which MySQL doesn't follow: one month before March 31 was February 31, oops, that date doesn't exists.
In Oracle DB, there are two nice functions : months_between and add_months
been used for these type date calculations. For your case, you may use one of the following :
select id, name, surname
from customers
where months_between(trunc(sysdate),DOB)>200;
or
select id, name, surname
from customers
where add_months(trunc(sysdate),-200)>DOB;
demo

DB2 Between Statement for Last Sunday to This Coming Saturday

I am curious why this does not work:
SELECT *
FROM TABLE
WHERE DATE_TIME_COLUMN BETWEEN
current date - int((dayofweek(current date))-1)
AND
current date + int(7-(dayofweek(current date)))
When this gives me exactly what I want:
select current date - int((dayofweek(current date))-1) days from sysibm.sysdummy1)
select current date + int(7-(dayofweek(current date))) days from sysibm.sysdummy1)
The above two will yield the correct dates that I want my specific date time column to be in between. What am I missing here?
SELECT *
FROM TABLE
WHERE DATE_TIME_COLUMN BETWEEN
current date - ((dayofweek(current date))-1) DAYS
AND
current date + (7-(dayofweek(current date))) DAYS
You have to signify that you are reducing DAYS from current date as shown above.
"Does not work" is a little vague :). Your first query is missing the DAY part to tell DB2 what part you're doing the date math on.
However, it kind of sounds like you might want to use the WEEK scalar function:
SELECT *
FROM TABLE
WHERE WEEK(DATE_TIME_COLUMN) = WEEK(CURRENT_DATE)
AND YEAR(DATE_TIME_COLUMN) = YEAR(CURRENT_DATE)

Choose active employes per month with dates formatted dd/mm/yyyy

I'm having a hard time explaining this through writing, so please be patient.
I'm making this project in which I have to choose a month and a year to know all the active employees during that month of the year.. but in my database I'm storing the dates when they started and when they finished in dd/mm/yyyy format.
So if I have an employee who worked for 4 months eg. from 01/01/2013 to 01/05/2013 I'll have him in four months. I'd need to make him appear 4 tables(one for every active month) with the other employees that are active during those months. In this case those will be: January, February, March and April of 2013.
The problem is I have no idea how to make a query here or php processing to achieve this.
All I can think is something like (I'd run this query for every month, passing the year and month as argument)
pg_query= "SELECT employee_name FROM employees
WHERE month_and_year between start_date AND finish_date"
But that can't be done, mainly because month_and_year must be a column not a variable.
Ideas anyone?
UPDATE
Yes, I'm very sorry that I forgot to say I was using DATE as data type.
The easiest solution I found was to use EXTRACT
select * from employees where extract (year FROM start_date)>='2013'
AND extract (month FROM start_date)='06' AND extract (month FROM finish_date)<='07'
This gives me all records from june of 2013 you sure can substite the literal variables for any variable of your preference
There is no need to create a range to make an overlap:
select to_char(d, 'YYYY-MM') as "Month", e.name
from
(
select generate_series(
'2013-01-01'::date, '2013-05-01', '1 month'
)::date
) s(d)
inner join
employee e on
date_trunc('month', e.start_date)::date <= s.d
and coalesce(e.finish_date, 'infinity') > s.d
order by 1, 2
SQL Fiddle
If you want the months with no active employees to show then change the inner for a left join
Erwin, about your comment:
the second expression would have to be coalesce(e.finish_date, 'infinity') >= s.d
Notice the requirement:
So if I have an employee who worked for 4 months eg. from 01/01/2013 to 01/05/2013 I'll have him in four months
From that I understand that the last active day is indeed the previous day from finish.
If I use your "fix" I will include employee f in month 05 from my example. He finished in 2013-05-01:
('f', '2013-04-17', '2013-05-01'),
SQL Fiddle with your fix
Assuming that you really are not storing dates as character strings, but are only outputting them that way, then you can do:
SELECT employee_name
FROM employees
WHERE start_date <= <last date of month> and
(finish_date >= <first date of month> or finish_date is null)
If you are storing them in this format, then you can do some fiddling with years and months.
This version turns the "dates" into strings of the form "YYYYMM". Just express the month you want like this and you can do the comparison:
select employee_name
from employees e
where right(start_date, 4)||substr(start_date, 4, 2) <= 'YYYYMM' and
(right(finish_date, 4)||substr(finish_date, 4, 2) >= 'YYYYMM' or finish_date is null)
NOTE: the expression 'YYYYMM' is meant to be the month/year you are looking for.
First, you can generate multiple date intervals easily with generate_series(). To get lower and upper bound add an interval of 1 month to the start:
SELECT g::date AS d_lower
, (g + interval '1 month')::date AS d_upper
FROM generate_series('2013-01-01'::date, '2013-04-01', '1 month') g;
Produces:
d_lower | d_upper
------------+------------
2013-01-01 | 2013-02-01
2013-02-01 | 2013-03-01
2013-03-01 | 2013-04-01
2013-04-01 | 2013-05-01
The upper border of the time range is the first of the next month. This is on purpose, since we are going to use the standard SQL OVERLAPS operator further down. Quoting the manual at said location:
Each time period is considered to represent the half-open interval
start <= time < end [...]
Next, you use a LEFT [OUTER] JOIN to connect employees to these date ranges:
SELECT to_char(m.d_lower, 'YYYY-MM') AS month_and_year, e.*
FROM (
SELECT g::date AS d_lower
, (g + interval '1 month')::date AS d_upper
FROM generate_series('2013-01-01'::date, '2013-04-01', '1 month') g
) m
LEFT JOIN employees e ON (m.d_lower, m.d_upper)
OVERLAPS (e.start_date, COALESCE(e.finish_date, 'infinity'))
ORDER BY 1;
The LEFT JOIN includes date ranges even if no matching employees are found.
Use COALESCE(e.finish_date, 'infinity')) for employees without a finish_date. They are considered to be still employed. Or maybe use current_date in place of infinity.
Use to_char() to get a nicely formatted month_and_year value.
You can easily select any columns you need from employees. In my example I take all columns with e.*.
The 1 in ORDER BY 1 is a positional parameter to simplify the code. Orders by the first column month_and_year.
To make this fast, create an multi-column index on these expressions. Like
CREATE INDEX employees_start_finish_idx
ON employees (start_date, COALESCE(finish_date, 'infinity') DESC);
Note the descending order on the second index-column.
If you should have committed the folly of storing temporal data as string types (text or varchar) with the pattern 'DD/MM/YYYY' instead of date or timestamp or timestamptz, convert the string to date with to_date(). Example:
SELECT to_date('01/03/2013'::text, 'DD/MM/YYYY')
Change the last line of the query to:
...
OVERLAPS (to_date(e.start_date, 'DD/MM/YYYY')
,COALESCE(to_date(e.finish_date, 'DD/MM/YYYY'), 'infinity'))
You can even have a functional index like that. But really, you should use a date or timestamp column.