Is there a function that takes a year, month and day to create a date in PostgreSQL?
Needing to do this in SQL routinely usually says you have problems with your data model where you're storing dates split up into fields in the DB rather than as true date or timestamp fields, or you have serious escaping and SQL injection problems. See explanation below.
Either of the following will solve your immediate problem:
CREATE OR REPLACE FUNCTION make_date(year integer, month integer, day integer) AS $$
SELECT year * INTERVAL '1' YEAR + month * INTERVAL '1' MONTH + day * INTERVAL '1' DAY;
$$ LANGUAGE sql STRICT IMMUTABLE;
or
CREATE OR REPLACE FUNCTION make_date(year integer, month integer, day integer) AS $$
SELECT format('%s-%s-%s', year, month, day)::date;
$$ LANGUAGE sql STRICT IMMUTABLE;
but please, read on.
The fact that you're asking this makes me think you're probably trying to build SQL in your application like this:
$sql = "SELECT date'" + year + '-' + month + '-' + day + "';";
which is generally dangerous and wrong (though probably not directly unsafe with if year
, month
and day
are integer data types). You should be using parameterized queries instead if this is what you're doing to avoid SQL injection and save yourself lots of hassle with escaping and literal formatting. See http://bobby-tables.com/ .
Here's how you'd query a date using a parameterized statement in Python with psycopg2 (since you didn't specify your language or tools):
import datetime
import psycopg2
conn = psycopg2.connect('')
curs = conn.cursor()
curs.execute('SELECT %s;', ( datetime.date(2000,10,05), ))
print repr(curs.fetchall());
This will print:
[(datetime.date(2000, 10, 5),)]
ie an array with a single Python date in it. You can see that it's been on a round trip through the database and you've never had to worry about PostgreSQL's date format or representation, since psycopg2 and PostgreSQL take care of that for you when you use parameterized statements. See this earlier related answer.
Producing date from year and month values in PostgreSQL
There's a '/'
missing:
SELECT
expiry_month,
expiry_year,
to_date(CONCAT(expiry_year, '/', expiry_month), 'YYYY/MM') AS the_start_of_year_month
FROM thisTable ;
will produce:
expiry_month | expiry_year | the_start_of_year_month
-----------: | ----------: | :----------------------
9 | 2018 | 2018-09-01
1 | 2019 | 2019-01-01
5 | 2016 | 2016-05-01
3 | 2019 | 2019-03-01
10 | 2017 | 2017-10-01
2 | 2020 | 2020-02-01
The date format is specifying '/' and it wasn't there, so, the whole text was taken as the year, and the month and day were taken as 1/1. CONCAT('2018','9')
was just returning '20189'
(which is a valid year).
dbfiddle here
How to extract year and month from date in PostgreSQL without using to_char() function?
date_part(text, timestamp)
e.g.
date_part('month', timestamp '2001-02-16 20:38:40'),
date_part('year', timestamp '2001-02-16 20:38:40')
http://www.postgresql.org/docs/8.0/interactive/functions-datetime.html
Month and year instead of the date variable
Use a character type parameter (text
or varchar
, not character
!) and the function to_date()
to convert the input to a date.
Your function, simplified and fixed:
CREATE OR REPLACE FUNCTION public.fn_reporte_venta(p_fech text
, p_client integer
, p_comprobante text)
RETURNS TABLE(nro integer, fecha date, tipo varchar, cliente text
, porc_igv numeric, st numeric, igv numeric, total numeric) AS
$func$
DECLARE
v_fech date := to_date(p_fech, 'YYYY-MM-DD'); -- transform to date
BEGIN
RETURN QUERY
SELECT v.numero_venta,
v.fecha_venta,
t.descripcion,
concat_ws(' ', c.apellido_paterno, c.apellido_materno, c.nombres) AS client,
v.porcentaje_igv,
v.sub_total,
v.igv,
v.total
FROM venta v
JOIN cliente c USING (codigo_cliente)
JOIN tipo_comprobante t USING (codigo_tipo_comprobante)
WHERE v.estado = 'E'
AND (v_fech = '1111-11-11' OR v.fecha_venta = v_fech) -- var goes here
AND (p_client = 0 OR c.codigo_cliente = p_client)
AND (p_comprobante = '00' OR t.codigo_tipo_comprobante = p_comprobante)
ORDER BY 2;
END
$func$ LANGUAGE plpgsql STABLE ROWS 1000;
Always use the ISO-8601 format for date literals (YYYY-MM-DD), which is unambiguous with any locale setting. Don't fall for local syntax dialects, they break if a session should run with a different locale.
I am using a date
variable in the plpgsql function, which can be assigned at declaration right away.
This expression is very versatile:
to_date(p_fech, 'YYYY-MM-DD');
to_date()
allows to omit elements to the right to 1 for missing data. All of these are valid and result in '2016-01-01':
SELECT to_date('2016-01-01', 'YYYY-MM-DD')
, to_date('2016-1-1' , 'YYYY-MM-DD')
, to_date('2016-01' , 'YYYY-MM-DD')
, to_date('2016-' , 'YYYY-MM-DD')
, to_date('2016' , 'YYYY-MM-DD');
So you can pass in full dates or truncated to month or even year. You always specify a single day this way.
Or to always cover a whole month:
...
v_fech date := to_date(p_fech, 'YYYY-MM'); -- no "-DD" truncates to month
...
AND (v_fech = '1111-11-01' -- also truncated to 1st of month!
OR v.fecha_venta >= v_fech
AND v.fecha_venta < v_fech + interval '1 month')
...
Why not data type character
? Because that's an outdated, largely useless type, typically a misunderstanding:
Best way to check for "empty or null value"
Any downsides of using data type "text" for storing strings?
Also note how I replaced your verbose CASE
statements with simple OR expressions.
And some other minor improvements ...
How to get year, month and day from seconds in PostgreSql?
I would like to get year, month and day separately and put these formated values to columns
Don't do that.
Use a single column of type date
or timestamp
, depending on your application. Not every combination of your three columns will be a valid date. But every value in a single column of type date
will be a valid date.
If you need the parts of a date separately, use PostgreSQL's date/time functions.
PostgreSQL Converting Two String Date Columns (Year,Month) to One Column (YYYY/MM/DD)
demo:db<>fiddle
First step: Generating a date
with to_date()
: Identifier Month
detects the long month name
SELECT to_date(year || '-' || month || '-01', 'yyyy-Month-dd')
Second step: Generating your expected date format with to_char()
SELECT
to_char(
to_date(year || '-' || month || '-01', 'yyyy-Month-dd'),
'yyyy/MM/dd'
)
FROM mydates
Postgres date functions
How to write function in postgresql to get months starting date and end date on the basis of name of the month passed to it
demo:db<>fiddle
CREATE OR REPLACE FUNCTION get_month_start_and_end(_month text)
RETURNS TABLE (start_date date, end_date date)
AS $$
BEGIN
RETURN QUERY
SELECT
_start_date,
(_start_date + interval '1 month -1 day')::date
FROM (
SELECT
to_date(_month || date_part('year', CURRENT_DATE), 'MonthYYYY') AS _start_date
) s;
END;
$$ LANGUAGE plpgsql;
- Get current year:
date_part('year', CURRENT_DATE)
- Get date from month and year:
to_date(<month text> || <year from 1.>, 'MonthYYYY')
. This gives the first of month because no day value is given - Calculate the end day of month as you already described
- Give out the two columns
Related Topics
Removing Milliseconds from a Oracle Tmstmp Field
How to Query Named Range on Sheet with Spaces in Name in Excel
How to Convert SQL Server to Oracle
Combine Rows When the End Time of One Is the Start Time of Another (Oracle)
Split Ipv4 Address into 4 Numbers in Oracle SQL
T-SQL How to Convert Comma Separated String of Numbers to Integer
Sql: Filter Rows with Max Value
Get Count of Items and Their Values in One Column
Insert Identity Column Value into Table from Another Table
Deleting Value Using SQLite While Doing an Inner Join
Using Timestampdiff in a Derby Where Clause
SQL 2 Counts with Different Filter
Nhibernate Count Distinct (Based on Multiple Columns)
Run Multiple Commands in SQLite Manager