convert values from two columns into one new column - postgresql

I have two columns: year and month:
Year Month
2017 01
2017 02
2018 12
2019 06
2020 07
With
select to_date(concat(Year, Month), 'YYYYMM') csv_date FROM my_table;
I can get just one column with date datatype.
How can I add this column in my table, to get this:
Year Month csv_date
2017 01 2017-01-00
2017 02 2017-02-00
2018 12 2018-12-00
2019 06 2019-06-00
2020 07 2020-07-00

You can not have a column defined as date that contains 00 for the day. That would be an invalid date, and Postgres will not allow it. The suggested method of concatenating the 2 works if the year and month are defined as a string type column, but the result will have '01' for the day. If those columns are defined as numeric then you can use the make date function.
with my_table(tyr, tmo, nyr,nmo) as
( values ('2020', '04', 2020, 04 ) )
select to_date(concat(tyr, tmo), 'YYYYMM') txt_date
, make_date(nyr,nmo,01) num_date
from my_table;
With that said then use the to_char function for a date column you can to get just year and month (and if you must) add the '-00'. so
with my_table (adate) as
( values ( date '2020-04-01') )
select adate, to_char(adate,'yyyy-mm') || '-00' as yyyymm
from mytable;
If you are on v12 and want to add the column you can add it as a generated column. This will have the advantage that it cannot be updated independently but will automatically update when the source columns(s) get updated. See fiddle complete example;
alter table my_table add column cvs_date date generated always as (make_date(yr, mo,01)) stored;

Using PostgreSQL Query
If you want to add new column then
alter table my_table add column csv_date date;
update my_table set csv_date=to_date(concat(Year, Month), 'YYYYMM');
If you want only select output then:
select year, month, to_date(concat(Year, Month), 'YYYYMM') csv_date FROM my_table;

Related

Postgres Crosstab on double columns with unknown value

So i have a table like this in my Postgres v.10 DB
CREATE TABLE t1(id integer primary key, ref integer,v_id integer,total numeric, year varchar, total_lastyear numeric,lastyear varchar ) ;
INSERT INTO t1 VALUES
(1, 2077,15,10000,2020,9000,2019),
(2, 2000,13,190000,2020,189000,2019),
(3, 2065,11,10000,2020,10000,2019),
(4, 1999,14,2300,2020,9000,2019);
select * from t1 =
id ref v_id total year total_lastyear lastyear
1 2077 15 10000 2020 9000 2019
2 2000 13 190000 2020 189000 2019
3 2065 11 10000 2020 10000 2019
4 1999 14 2300 2020 9000 2019
Now i want to Pivot this table so that i have 2020 and 2019 as columns with the total amounts as values.
My Problems:
I don't know how two pivot two columns in the same query, is that even possibly or do you have to make two steps?
The years 2020 and 2019 are dynamic and can change from one day to another. The year inside the column is the same on every row.
So basicly i need to save the years inside lastyear and year in some variable and pass it to the Crosstab query.
This far i made it myself but i only managed to pivot one year and the 2019 and 2020 years is hardcoded.
Demo
You can pivot one at a time with WITH.
WITH xd1 AS (
SELECT * FROM crosstab('SELECT ref,v_id,year,total FROM t1 ORDER BY 1,3',
'SELECT DISTINCT year FROM t1 ORDER BY 1') AS ct1(ref int,v_id int,"2020" int)
), xd2 AS (
SELECT * FROM crosstab('SELECT ref,v_id,lastyear,total_lastyear FROM t1 ORDER BY 1,3',
'SELECT DISTINCT lastyear FROM t1 ORDER BY 1') AS ct2(ref int,v_id int,"2019" int)
)
SELECT xd1.ref,xd1.v_id,xd1."2020",xxx."2019"
FROM xd1
LEFT JOIN xd2 AS xxx ON xxx.ref = xd1.ref AND xxx.v_id = xd1.v_id;
This doesn't prevent from last_year and year colliding.
You still have to know the years query will return as you have to define record as it is returned by crosstab.
You could wrap it in an EXECUTE format() to make it more dynamic and deal with some stringology.
This issue was mentioned here.

how to get hour, month from timestamp in postgresql

timestamp with timezone is this - 2020-05-31T10:05:07Z
this is not working, despite referencing official documentation. I need to extract may 2020 or separate month and year to compare against May 2020
SELECT date_trunc('hour', TIMESTAMP '2020-05-31T10:05:07Z')
SELECT date_part('day', TIMESTAMP '2020-05-31T10:05:07Z');
If you want to check if a timestamp value is "may 2020", you have different options.
to_char(the_value, 'yyyy-mm') = '2020-05'
or
extract(month from the_value) = 5
and extract(year from the_value) = 2020
or
(extract(month from the_value), extract(year from the_value)) = (5, 2020)
extract() and date_part() are the same thing - but I prefer the standard compliant extract() version.
demo:db<>fiddle
You need to_char() to format a date or timestamp. Mon gives you the first three letters of a month name:
SELECT
to_char(
TIMESTAMP '2020-05-31T10:05:07Z',
'Mon YYYY'
)
Returning the entire month name you can use Month instead of Mon. But, for some reasons, the length of the Month value is fixed to the longest month name available. That means May is returned with right padded spaces. To avoid this, you need to add the modifier FM:
SELECT
to_char(
TIMESTAMP '2020-05-31T10:05:07Z',
'FMMonth YYYY'
)

How to sort attendance date along with the month?

Attendance is sorting according to date, that is fine, but I want to sort date along with the month name January should come at the bottom, and December at the top.
Table
Attendance Date
---------------
26 Feb 2018
19 Dec 2018
18 Dec 2018
14 Dec 2018
12 June 2018
7 Dec 2018
5 Feb 2018
Query
select distinct
(select ARRAY_TO_STRING(ARRAY_AGG(ARRAY[to_char(t1.l_time,'HH12:mi AM')]::text), ',')
from
(select (al1.create_time AT TIME ZONE 'UTC+5:30')::time as l_time
from users.access_log as al1
where al1.user_id = al.user_id
and al1.login_status = 1
and al1.create_time::date = al.create_time::date
order by al1.create_time::time ASC
) as t1
) as login_time,
(select ARRAY_TO_STRING(ARRAY_AGG(ARRAY[to_char(t2.o_time,'HH:mi AM')]::text), ',')
from
(select (al2.create_time AT TIME ZONE 'UTC+5:30')::time as o_time
from users.access_log as al2
where al2.user_id = al.user_id
and al2.login_status = 0
and al2.create_time::date = al.create_time::date
order by al2.create_time::time ASC
) as t2
) as logout_time,
al.create_time::date
from users.access_log as al
where al.user_id = ?;
Attendance is sorting according to date, that is fine, but I want to sort date along with the month name January should come at the bottom, and December at the top.

convert int to datetime in sybase

This works perfectly on the server (sql server 2012) for a julian date of 5 digits
select cast (column1 as DATETIME) FROM mytable
How to cast an int to datetime in sybase?
And which would be the best way, since I have a large table and I have to minimize the time i'm going to be using the server under the query.
I saw here: http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc32300.1570/html/sqlug/sqlug645.htm
that it is allowed to convert from int to varchar and from varchar to smalldate.
So maybe something like this, but i don't know the syntax for sybase:
declare #convDate varchar (200)
set #convDate = 'SELECT top 100 CONVERT( varchar (200), column1, 104 )as someCol FROM dbo.mytable'
select cast (#convDate as DateTime) as newDate into #myTemp from ?
Assuming date is in YYYYMMDD format. Use below:
SELECT CONVERT(DATETIME, CONVERT(VARCHAR, col1)) AS someCol FROM dbo.mytable
Internal numbers representing dates in Excel are a continuous sequence of integers from Jan 1 1900, which is number one. Hence, a solution is to use the function DATEADD to sum your integer (minus one) to Jan 1 1900. In this query, " " is the same as "Jan 1 1900" as this is the Sybase ASE default.
select dateadd(day, column1 - 1, " ") from mytable /* Probably wrong */
But I tested and got a one day difference. The result of this is Jul 13 2015, but Excel shows Jul 12 2015 instead.
select dateadd(day, 42197 - 1, " ")
IMHO, Excel is wrong, as it shows Feb 29 1900 for the number 60, but 1900 (contrary to 2000) is not a leap year. Sybase ASE is correct; this gives Feb 28 1900 and Mar 1 1900:
select dateadd(day, 59 - 1, " "), dateadd(day, 60 - 1, " ")
Assuming you had to take Excel convention, then just subtract two instead of one:
select dateadd(day, column1 - 2, " ") from mytable /* Bizarre but maybe OK */

What is the best way to get this TSQL Pivot to work [duplicate]

I need to do the following transpose in MS SQL
from:
Day A B
---------
Mon 1 2
Tue 3 4
Wed 5 6
Thu 7 8
Fri 9 0
To the following:
Value Mon Tue Wed Thu Fri
--------------------------
A 1 3 5 7 9
B 2 4 6 8 0
I understand how to do it with PIVOT when there is only one column (A) but I can not figure out how to do it when there are multiple columns to transpose (A,B,...)
Example code to be transposed:
select LEFT(datename(dw,datetime),3) as DateWeek,
sum(ACalls) as A,
Sum(BCalls) as B
from DataTable
group by LEFT(datename(dw,datetime),3)
Table Structure:
Column DataType
DateTime Datetime
ACalls int
BCalls int
Any help will be much appreciated.
In order to transpose the data into the result that you want, you will need to use both the UNPIVOT and the PIVOT functions.
The UNPIVOT function takes the A and B columns and converts the results into rows. Then you will use the PIVOT function to transform the day values into columns:
select *
from
(
select day, col, value
from yourtable
unpivot
(
value
for col in (A, B)
) unpiv
) src
pivot
(
max(value)
for day in (Mon, Tue, Wed, Thu, Fri)
) piv
See SQL Fiddle with Demo.
If you are using SQL Server 2008+, then you can use CROSS APPLY with VALUES to unpivot the data. You code would be changed to the following:
select *
from
(
select day, col, value
from yourtable
cross apply
(
values ('A', A),('B', B)
) c (col, value)
) src
pivot
(
max(value)
for day in (Mon, Tue, Wed, Thu, Fri)
) piv
See SQL Fiddle with Demo.
Edit #1, applying your current query into the above solution you will use something similar to this:
select *
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
col,
value
from DataTable
cross apply
(
values ('A', ACalls), ('B', BCalls)
) c (col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv