How to extract the year without it being repeated - postgresql

Hello colleagues I have a question about a query if it could be done I have a table called sale and a field called sales_date, so the field is full more or less like this
--------------------------------------------------
sales_date
--------------------------------------------------
2013-02-03
2013-02-05
2014-06-07
2015-03-04
2015-01-04
2016-04-07
2016-09-03
2016-04-09
And I would like to know how to do a select and show me only the years without repeating
--------------------------------------------------
sales_date
--------------------------------------------------
2013
2014
2015
2016
Thanks any help, my database is in Postgresql version 9.5.

You can use extract() to get the year and distinct to remove the duplicates:
select distinct extract(year from sales_date) as sales_date
from sale;

Related

I want to fetch the data where I will put the date and I will get data from last three years to the entered date

imagine if I put the date 27th Jan 2022 then I should get all the records from 26th Jan 2019 till 27th Jan 2022.
There are a couple of ways to do this. One caveat is how you are defining three years:
select '2022-01-27'::date - interval '3 years';
?column?
---------------------
2019-01-27 00:00:00
or as you have it:
2019-01-26 to 2022-01-27.
Using your definition:
SELECT * FROM some_table WHERE date_fld BETWEEN '2019-01-26' and '2022-01-27';
--Using a date range
select * from some_table where date_fld <# daterange('2019-01-26', '2022-01-27', '[]');
t
--or
select * from some_table where date_fld <# daterange('2019-01-26', '2022-01-28');
t
By default date ranges are exclusive on the upper bound, that is why you need to include either the '[]' or bump the upper bound up one date(which is actually what the '[]' does).
For more information on ranges Ranges and Range operators

How to get max of column based on grouping by date column in postgreSQL

First time poster here, not sure if my title really outlines what I am looking for here...
I am trying to get the following:
"Which month of the year does each property type earn the most money on average?"
I have two tables with the following fields I am working with:
calendar_metric
period (this is a date, formatted 'yyyy-mm-dd'
revenue
airbnb_property_id
property
property_type
airbnb_property_id
I have figured out how to get the month, property type, and average revenue to display, but am having trouble with grouping it correctly I think.
select
extract(month from calendar_metric.period) as month,
property.property_type,
avg(calendar_metric.revenue) as average_revenue
from
calendar_metric
inner join property on
calendar_metric.airbnb_property_id = property.airbnb_property_id
group by
month,
property_type
What I want it to output would look like this:
month | property_type | max_average_revenue
---------------------------------------------
1 | place | 123
2 | floor apt | 535
3 | hostel | 666
4 | b&b | 363
5 | boat | 777
etc| etc | etc
but currently I am getting this:
month-property_type | max_average_revenue
---------------------------------------------
1 | place | 123
2 | floor apt | 535
1 | place | 444
4 | b&b | 363
4 | b&b | 777
etc| etc | etc
So essentially, months are coming back duplicated as I extracted the month from a date stamp, the data set goes across 5 years or so, and am probably not grouping right? I know I am missing something simple probably, I just cannot seem to figure out how to do this correctly.
Help!
you should be grouping by year-month, since you are trying to view 5 year period.
select
extract(month from calendar_metric.period) as month,
property.property_type,
avg(calendar_metric.revenue) as average_revenue
from
calendar_metric
inner join property on
calendar_metric.airbnb_property_id = property.airbnb_property_id
group by
extract(year from period),
month,
property_type
I think your query is basically there, it's just returning all months rather than just filtering out the rows you don't want. I'd tend to use the DISTINCT ON clause for this sort of thing, something like:
SELECT DISTINCT ON (property_type)
p.property_type, extract(month from cm.period) AS month,
avg(cm.revenue) AS revenue
FROM calendar_metric AS cm
JOIN property AS p USING (airbnb_property_id)
GROUP BY property_type, month
ORDER BY property_type, revenue DESC;
I've shortened your query down a bit, hope it still makes sense to you.
using CTEs you can express this in two steps which might be easier to follow what's going on:
WITH results AS (
SELECT p.property_type, extract(month from cm.period) AS month,
avg(cm.revenue) AS revenue
FROM calendar_metric AS cm
JOIN property AS p USING (airbnb_property_id)
GROUP BY property_type, month
)
SELECT DISTINCT ON (property_type)
property_type, month, revenue
FROM results
ORDER BY property_type, revenue DESC;

Is there a reliable way to extract years from a postgres interval?

In Postgres, if I do the following:
select (now() - created_at) from my_table
I get results like this:
854 days 12:04:50.29658
Whereas, if I do:
select age(now(), created_at) fro my_table
I get results like this:
2 years 4 mons 3 days 12:04:50.29658
According to pg_typeof(...) they are both of type interval
But if I try to extract the years:
select extract(years from age(now(), created_at)) from my_table
I get:
2
Whereas, with:
select extract(years from (now() - created_at)) from my_table
I get:
0
Is there a consistent way to extract the number of years from an interval value (no matter how it was generated)?
Note: I don't have write access to the db, so can't define stored procedures, etc. Needs to be a select statement.
------ UPDATE ------
justify_interval(...) was suggested below, but unfortunately it seems to be inaccurate in its calculations.
E.g:
select age('2018-01-03'::timestamp, '2016-01-05'::timestamp);
gives the correct answer:
1 year 11 mons 29 days
Whereas:
select justify_interval('2018-01-03'::timestamp - '2016-01-05'::timestamp);
gives:
2 years 9 days
I believe this is because it (incorrectly) assumes that all months have 30 days in them
(see justify_days
here: https://www.postgresql.org/docs/current/static/functions-datetime.html)
The function justify_interval does what you want. Use it in combination with EXTRACT to get the years:
SELECT EXTRACT(years FROM
justify_interval(INTERVAL '1 year 900 days 700 hours'));
date_part
-----------
3
(1 row)
If 30 days = 1 month isn't accurate enough for you, you'll have to use EXTRACT to get the number of days and divide by 365.25.
There is a theoretical limit how exact you can be, because the number of years in an interval somewhat depends on between which dates that interval is.
The two-element age function gives a precise result for the number of years between two dates.

Add new row to SELECT results

Say I have a table with the column 'CreatedDate'. I am interested in creating an SQL statement that can get me a list of all years except add one more year at the end of the result.
TSQL:
SELECT DISTINCT YEAR(CreatedDate) as FY from MyTable ORDER BY FY ASC
Current results returned by above query:
2016
2017
I would like the final results to be:
2016
2017
2018
I do not want to accomplish this by using a TemporaryTable or a View. I do not want to create new tables and truncate them. Is there anything I can do to the select statement to get me what I need?
select *
from (
SELECT DISTINCT YEAR(CreatedDate) as FY
from MyTable
union
SELECT max(YEAR(CreatedDate))+1 as FY
from MyTable
)x
ORDER BY FY ASC

Month & Company view

I have a table that has a bunch of companies listed in it. I need to make a dictionary view that combines some of the company data, and in addition, every month of the past 3 years up to the current month.
So for example I would pull the cmp_num, then have additional columns for year and month. It's important that the actual years not be hard coded, since I would want this to continue to work in the following years.
cmp1, 2014, 11
cmp1, 2014, 10
cmp1, 2014, 09
...etc for all months of 2014
cmp1, 2013, 01
...etc for all months of 2013
cmp1, 2014
...etc for all months of 2012
*and so on for all companies
Do I need a seed table with the years and months listed? Or is there a way to create this on the fly with a calculating view? Which approach would be better in tsql?
Here's a List of the stuff I need to join with the year\month list.
[cmp_code]
[Name]
[City]
[State]
[GroupNo]
[lastname]
As I said, you can do both. Here is a CTE you can use to generate the list for 3 years back. If the performance is not good enough, you should consider creating dedicated table.
WITH cte AS
(
SELECT 1 AS id, DATEPART(YEAR,GETDATE()) AS [year], DATEPART(MONTH,GETDATE()) AS [month]
UNION ALL
SELECT
id + 1 AS id,
CASE WHEN [month] = 1 THEN [year]-1 ELSE [year] END AS [year],
CASE WHEN [month] = 1 THEN 12 ELSE [month] - 1 END AS [month]
FROM cte WHERE id < 36
)
SELECT [Name], [year], [month]
FROM cte CROSS JOIN dbo.viewFranchiseList