Row summation based on dates - tsql

I have a table as the following:
What I am trying to do is to get the sum of values in the createdYesterday column based on the month, for example for May or 05 month I need to get the some of all rows between 5-1-2018 to 5-30-2018 and write this value to a variable to a table.
The final result should be a table that has the 12 months and the sum of CreatedYesterday in each month.
month | Sum
Jan | 100
Feb | 500
Mar | 1000

CREATE TABLE #TEMP_A
(
CREATEDYESTERDAY INT,
CREATEDTHISWEEK INT,
CREATEDTHISYEAR INT,
CALCDATE DATE
)
INSERT INTO #TEMP_A
VALUES(0,0,26226,'05/18/2018')
INSERT INTO #TEMP_A
VALUES(71,1647,7402,'05/18/2018')
INSERT INTO #TEMP_A
VALUES(21,60,1931,'05/18/2018')
INSERT INTO #TEMP_A
VALUES(21,60,1931,'04/18/2018')
SELECT SUM(CREATEDYESTERDAY) AS [SUM],DATENAME(MONTH,CALCDATE) AS [MONTH] FROM #TEMP_A GROUP BY DATENAME(MONTH,CALCDATE)

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.

convert values from two columns into one new column

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;

confusion in using select command in postgresql with timestamp column

I have table which has structure like this.
CREATE TABLE users (
id serial NOT NULL,
created_at timestamp NOT NULL
)
I have more than 1000 records in this table.
This is my first query.
1 query
select id,created_at from users where id in (1051,1052)
This returns two rows which is correct as as expected. However when I use
2nd Query
select id,created_at from users where created_at = '2020-06-28'
or
select id,created_at from users where created_at = date '2020-06-28'
It returns nothing, this is not expected result as it should return two rows against it.
Similarly if I use this
3rd Query
select id, created_at from users where created_at between date '2020-06-28' and date '2020-06-28'
It returns nothing however I think this should also return two rows.
While this
4th Query
select id, created_at from users where created_at between date '2020-06-28' and date '2020-06-29'
returns two rows.
Show timezone returns correct timezong in which currently i am
I did`t understand this, why the results are different in 2nd, 3rd and 4th query. How can i get same result as of query 1 using 3rd query.
One single reason for all your queries is that you are comparing timestamp with date
in Query 2
You are comparing 2020-06-28 13:02:53 = 2020-06-28 00:00:00 which will not match so returning no records.
in Query 3
You are using between i.e. 2020-06-28 13:02:53 between 2020-06-28 00:00:00 and 2020-06-28 00:00:00 which will not match so returning no records.
in Query 4
You are using between i.e. 2020-06-28 13:02:53 between 2020-06-28 00:00:00 and 2020-06-29 00:00:00. Here both records are falling in those timestamps and you are getting the records
So you have to compare date values. As right operand is a date type value, you have to convert the left operand to date. try this
for 2nd Query
select id,created_at from users where date(created_at) = '2020-06-28'
for 3rd Query
select id, created_at from users where date(created_at) between date '2020-06-28' and date '2020-06-28'
You should opt 3rd method if you want to compare a date range. For one day only you should use 2nd query.
Because what you are doing is:
test(5432)=# select '2020-06-28'::timestamp;
timestamp
---------------------
06/28/2020 00:00:00
You are selecting for created_at that is exactly at midnight and there is none. The same thing when you do:
select id, created_at from users where created_at between date '2020-06-28' and date '2020-06-28'
You already corrected the mistake in your 3rd query in the 4th query:
select id, created_at from users where created_at between date '2020-06-28' and date '2020-06-29'
where you span the time from midnight of 06/28/2020 to midnight 06/29/2020
An alternate solution is:
create table dt_test(id integer, ts_fld timestamp);
insert into dt_test values (1, '07/04/2020 8:00'), (2, '07/05/2020 1:00'), (3, '07/05/2020 8:15');
select * from dt_test ;
id | ts_fld
----+---------------------
1 | 07/04/2020 08:00:00
2 | 07/05/2020 01:00:00
3 | 07/05/2020 08:15:00
select * from dt_test where date_trunc('days', ts_fld) = '07/05/2020'::date;
id | ts_fld
----+---------------------
2 | 07/05/2020 01:00:00
3 | 07/05/2020 08:15:00
In your case:
select id, created_at from users where date_trunc('days', created_at) = '2020-06-28'::date;

How to see value of a char column sorted as I need

The table name is Date , and one of it's column's name is MonthOfYear (for jan the column value is 1 , for feb it is 2 , for march : 3 ... )and the other column is MonthName : this column contains the name of month ( jan , feb , march ..)
I need to see the result sorted ( jan be the first col , then feb , then march , then ...)
I updated the MonthName like this but it didn't work :
update Date
set [monthname] = cast(monthofyear as nvarchar(3)) + ' ' + rtrim(ltrim([monthname]))
You cannot get what you want sort on a char column
As a char 12 will sort before 2
You need to search on an integer column for 12 to sort before 2
So sort on MonthOfYear
Order By MonthOfYear
I don't know all your column names, but you don't want to change the data in the MonthName column.
Have you tried:
SELECT Year, MonthName
FROM Date
ORDER BY Year, MonthOfYear
To order by a varchar column you can put a 0 at the front of the single digit numbers.
Here's an example that shows how you can do it:
declare #t table (StringNumber varchar(3))
insert into #t (StringNumber)
select '9' union
select '10' union
select '11'
select StringNumber
from #t
order by StringNumber
select StringNumber, right('0' + StringNumber, 2) as OrderByStringNumber
from #t
order by right('0' + StringNumber, 2)

SQL query to convert date ranges to per day records

Requirements
I have data table that saves data in date ranges.
Each record is allowed to overlap previous record(s) (record has a CreatedOn datetime column).
New record can define it's own date range if it needs to hence can overlap several older records.
Each new overlapping record overrides settings of older records that it overlaps.
Result set
What I need to get is get per day data for any date range that uses record overlapping. It should return a record per day with corresponding data for that particular day.
To convert ranges to days I was thinking of numbers/dates table and user defined function (UDF) to get data for each day in the range but I wonder whether there's any other (as in better* or even faster) way of doing this since I'm using the latest SQL Server 2008 R2.
Stored data
Imagine my stored data looks like this
ID | RangeFrom | RangeTo | Starts | Ends | CreatedOn (not providing data)
---|-----------|----------|--------|-------|-----------
1 | 20110101 | 20110331 | 07:00 | 15:00
2 | 20110401 | 20110531 | 08:00 | 16:00
3 | 20110301 | 20110430 | 06:00 | 14:00 <- overrides both partially
Results
If I wanted to get data from 1st January 2011 to 31st May 2001 resulting table should look like the following (omitted obvious rows):
DayDate | Starts | Ends
--------|--------|------
20110101| 07:00 | 15:00 <- defined by record ID = 1
20110102| 07:00 | 15:00 <- defined by record ID = 1
... many rows omitted for obvious reasons
20110301| 06:00 | 14:00 <- defined by record ID = 3
20110302| 06:00 | 14:00 <- defined by record ID = 3
... many rows omitted for obvious reasons
20110501| 08:00 | 16:00 <- defined by record ID = 2
20110502| 08:00 | 16:00 <- defined by record ID = 2
... many rows omitted for obvious reasons
20110531| 08:00 | 16:00 <- defined by record ID = 2
Actually, since you are working with dates, a Calendar table would be more helpful.
Declare #StartDate date
Declare #EndDate date
;With Calendar As
(
Select #StartDate As [Date]
Union All
Select DateAdd(d,1,[Date])
From Calendar
Where [Date] < #EndDate
)
Select ...
From Calendar
Left Join MyTable
On Calendar.[Date] Between MyTable.Start And MyTable.End
Option ( Maxrecursion 0 );
Addition
Missed the part about the trumping rule in your original post:
Set DateFormat MDY;
Declare #StartDate date = '20110101';
Declare #EndDate date = '20110501';
-- This first CTE is obviously to represent
-- the source table
With SampleData As
(
Select 1 As Id
, Cast('20110101' As date) As RangeFrom
, Cast('20110331' As date) As RangeTo
, Cast('07:00' As time) As Starts
, Cast('15:00' As time) As Ends
, CURRENT_TIMESTAMP As CreatedOn
Union All Select 2, '20110401', '20110531', '08:00', '16:00', DateAdd(s,1,CURRENT_TIMESTAMP )
Union All Select 3, '20110301', '20110430', '06:00', '14:00', DateAdd(s,2,CURRENT_TIMESTAMP )
)
, Calendar As
(
Select #StartDate As [Date]
Union All
Select DateAdd(d,1,[Date])
From Calendar
Where [Date] < #EndDate
)
, RankedData As
(
Select C.[Date]
, S.Id
, S.RangeFrom, S.RangeTo, S.Starts, S.Ends
, Row_Number() Over( Partition By C.[Date] Order By S.CreatedOn Desc ) As Num
From Calendar As C
Join SampleData As S
On C.[Date] Between S.RangeFrom And S.RangeTo
)
Select [Date], Id, RangeFrom, RangeTo, Starts, Ends
From RankedData
Where Num = 1
Option ( Maxrecursion 0 );
In short, I rank all the sample data preferring the newer rows that overlap the same date.
Why do it all in DB when you can do it better in memory
This is the solution (I eventually used) that seemed most reasonable in terms of data transferred, speed and resources.
get actual range definitions from DB to mid tier (smaller amount of data)
generate in memory calendar of a certain date range (faster than in DB)
put those DB definitions in (much easier and faster than DB)
And that's it. I realised that complicating certain things in DB is not not worth it when you have executable in memory code that can do the same manipulation faster and more efficient.