Fiscal Period Name from varchar column - sql

I fairly new to SQL and learning so much from this forum. Thank All!
Have another one for which I seek expert advice.
I have a column which basically is fiscal period. However, its not a date column, but instead of type varchar.
Intention is to manipulate the column value to get the name of the fiscal month out of it.
So in the example below, I have column Fiscal_Period available and I need column FiscalMonth.
Fiscal_Period FiscalMonth
----------------------------
2018001 Jan
2018002 Feb
2018003 Mar
2018004 Apr
2018005 May
2018006 Jun
2018007 Jul
2018008 Aug
2018009 Sep
2018010 Oct
2018011 Nov
2018012 Dec
Is there a straightforward way of achieving this then the way I am then trying?
My current hack approach is:
Convert Fiscal_Period column filed into YYYY-DD-MM format by using substring and concat function
Use Datetime to convert into date and then extract month out of it
Use Month and Datename function
My query:
SELECT
DATENAME(MONTH, (CONVERT(DATETIME, (CONCAT(SUBSTRING(FISCAL_PERIOD, 1, 4), '-', SUBSTRING(FISCAL_PERIOD, 6, LEN(FISCAL_PERIOD)), '-', '11'))))) AS PeriodName
FROM
table 1
Cheers!

this is slightly shorter
datename(month, stuff(FISCAL_PERIOD, 5, 1, '-') + '-01')
if you only wanted the abbreviated month name then apply LEFT ( ) on the above expression

As you only need a month name neither the year or day need to vary, hence you can use constants for those. In T-SQL a literal that equates to YYYYMMDD is safe to use as a date, so:
select datename(month,'2018' + right(fiscal_period,2) + '01')

This is basically the answers already given, just figured id finish typing it. It relies on filling in a problem with the date format as given, but otherwise it works the same.
;with src (dt) as
(
select convert(date, '20180101', 120) union all
select '20180201' union all
select '20180301' union all
select '20180401' union all
select '20180501' union all
select '20180601' union all
select '20180701' union all
select '20180801' union all
select '20180901' union all
select '20181001' union all
select '20181101' union all
select '20181201'
)
select
Dt,
left(datename(month, dt), 3)
from src

Related

SQL: Outputting month names in correct order

I am using the query composer on Google BigQuery.
I want to output the months in the correct order, e.g. starting with January, ending with December.
Here is my query:
SELECT month, gender, SUM(cost) AS Cost
FROM [medicare.medicareTable]
GROUP BY month, gender
ORDER BY month, gender
Without the ORDER BY above, the months were in a completely random order. Now they are alphabetised, which is a little better but still not what I want.
Using the above query, the output looks like this: https://docs.google.com/spreadsheets/d/18r_HhY1jG3Edkj5Nk8gDM_eSQ_1fI6ePHSZuJuoAppE/edit?usp=sharing
Thanks to anyone who can help.
for BigQuery Standard SQL you can use PARSE_DATE(). You can see Supported Format Elements for DATE
WITH m AS (
SELECT 'January 01 2016' AS d UNION ALL
SELECT 'February 01 2016' AS d UNION ALL
SELECT 'March 01 2016' AS d
)
SELECT d, EXTRACT(month FROM PARSE_DATE('%B %d %Y', d)) AS month_number
FROM m
ORDER BY month_number
You can try to get month number from month name and sort is ascending order.
Syntax for SQL Server
select DATEPART(MM,'january 01 2016') -- returns 1
So you can try something like this
SELECT month, gender, SUM(cost) AS Cost
FROM [medicare.medicareTable]
GROUP BY month, gender
ORDER BY datepart(MM,month + ' 01 2016'),gender
Hope this helps

Get SQL to process each row 1 by 1

I have been going around for while trying to get an anwswer to my issue, I think it revolves around cursors in SQL but I am not sure. I think I know how to write the loop for a single row of data but I don't know how to run it for all the records:
Hopefully there is an easy answer:
I have a table, let's call it A, that has Product_Code, Start_Date, End_Date and Value
I would need an output table B that has column: Product_Code, Month, Year, Value when Month * Year is in between Start_Date and End_date
Each record of A should then create several record into B. Hope that's fairly clear, I'm happy to elaborate if not! :)
CREATE TABLE YearMonth(
Year int not null,
Month int not null,
FirstDay date not null,
LastDay date not null
);
Fill this table with as many years and months that your range of data is covered (no problem if you have too much).
You could do this with a statement like this:
WITH y(year) AS (
SELECT 2007
union all
SELECT 2008
union all
SELECT 2009
union all
SELECT 2010
union all
SELECT 2011
union all
SELECT 2012
union all
SELECT 2013
union all
SELECT 2014
union all
SELECT 2015
union all
SELECT 2016
),
m(month) AS (
SELECT 1
union all
SELECT 2
union all
SELECT 3
union all
SELECT 4
union all
SELECT 5
union all
SELECT 6
union all
SELECT 7
union all
SELECT 8
union all
SELECT 9
union all
SELECT 10
union all
SELECT 11
union all
SELECT 12
)
INSERT INTO YearMonth(Year, Month, FirstDay, LastDay)
SELECT y.year
,m.month
,convert(date, convert(nvarchar(4), y.year) + '.' + convert(nvarchar(2), m.month) + '.01', 102)
,DateAdd(day, - 1,
CASE WHEN m.month = 12 THEN
convert(date, convert(nvarchar(4), y.year + 1) + '.01.01', 102)
ELSE
convert(date, convert(nvarchar(4), y.year) + '.' + convert(nvarchar(2), m.month + 1) + '.01', 102)
END)
FROM y CROSS JOIN m
The tricky part to calculate the LastDay works like this: create a date that is the first of the following month, then subtract one day from it. This handles the problem that the last day of the month can be 28, 29, 30, or 31.
Then just use a join:
INSERT INTO B(Product_Code, Month, Year, Value)
SELECT A.Product_Code
,YearMonth.Month
,YearMonth.Year
,A.Value
FROM A
JOIN YearMonth ON YearMonth.LastDay <= A.StartDate
AND YearMonth.FirstDay <= A.EndDate
Depending on the exact interpretation of "Month*Year is in between Start_Date and End_date", you might have to switch one or both of the <=s to <.

Return just the last day of each month with SQL

I have a table that contains multiple records for each day of the month, over a number of years. Can someone help me out in writing a query that will only return the last day of each month.
SQL Server (other DBMS will work the same or very similarly):
SELECT
*
FROM
YourTable
WHERE
DateField IN (
SELECT MAX(DateField)
FROM YourTable
GROUP BY MONTH(DateField), YEAR(DateField)
)
An index on DateField is helpful here.
PS: If your DateField contains time values, the above will give you the very last record of every month, not the last day's worth of records. In this case use a method to reduce a datetime to its date value before doing the comparison, for example this one.
The easiest way I could find to identify if a date field in the table is the end of the month, is simply adding one day and checking if that day is 1.
where DAY(DATEADD(day, 1, AsOfDate)) = 1
If you use that as your condition (assuming AsOfDate is the date field you are looking for), then it will only returns records where AsOfDate is the last day of the month.
Use the EOMONTH() function if it's available to you (E.g. SQL Server). It returns the last date in a month given a date.
select distinct
Date
from DateTable
Where Date = EOMONTH(Date)
Or, you can use some date math.
select distinct
Date
from DateTable
where Date = DATEADD(MONTH, DATEDIFF(MONTH, -1, Date)-1, -1)
In SQL Server, this is how I usually get to the last day of the month relative to an arbitrary point in time:
select dateadd(day,-day(dateadd(month,1,current_timestamp)) , dateadd(month,1,current_timestamp) )
In a nutshell:
From your reference point-in-time,
Add 1 month,
Then, from the resulting value, subtract its day-of-the-month in days.
Voila! You've the the last day of the month containing your reference point in time.
Getting the 1st day of the month is simpler:
select dateadd(day,-(day(current_timestamp)-1),current_timestamp)
From your reference point-in-time,
subtract (in days), 1 less than the current day-of-the-month component.
Stripping off/normalizing the extraneous time component is left as an exercise for the reader.
A simple way to get the last day of month is to get the first day of the next month and subtract 1.
This should work on Oracle DB
select distinct last_day(trunc(sysdate - rownum)) dt
from dual
connect by rownum < 430
order by 1
I did the following and it worked out great. I also wanted the Maximum Date for the Current Month. Here is what I my output is. Notice the last date for July which is 24th. I pulled it on 7/24/2017, hence the result
Year Month KPI_Date
2017 4 2017-04-28
2017 5 2017-05-31
2017 6 2017-06-30
2017 7 2017-07-24
SELECT B.Year ,
B.Month ,
MAX(DateField) KPI_Date
FROM Table A
INNER JOIN ( SELECT DISTINCT
YEAR(EOMONTH(DateField)) year ,
MONTH(EOMONTH(DateField)) month
FROM Table
) B ON YEAR(A.DateField) = B.year
AND MONTH(A.DateField) = B.Month
GROUP BY B.Year ,
B.Month
SELECT * FROM YourTableName WHERE anyfilter
AND "DATE" IN (SELECT MAX(NameofDATE_Column) FROM YourTableName WHERE
anyfilter GROUP BY
TO_CHAR(NameofDATE_Column,'MONTH'),TO_CHAR(NameofDATE_Column,'YYYY'));
Note: this answer does apply for Oracle DB
Here's how I just solved this. day_date is the date field, calendar is the table that holds the dates.
SELECT cast(datepart(year, day_date) AS VARCHAR)
+ '-'
+ cast(datepart(month, day_date) AS VARCHAR)
+ '-'
+ cast(max(DATEPART(day, day_date)) AS VARCHAR) 'DATE'
FROM calendar
GROUP BY datepart(year, day_date)
,datepart(month, day_date)
ORDER BY 1

strip 0 from all rows in single column MySQL

I want to TRIM(LEADING 0 FROM month) FROM table1
where table1 has column month with data formatted like so: 01, 02, 03
I would like to update the data so it is formatted as: 1, 2, 3, ...
Thanks!
Simplest way might be to just cast it to an int:
SELECT CAST(month as int) from table
From the MySQL docs:
mysql> SELECT MONTH('2008-02-03');
-> 2
So, if you only have the month (and not the rest of the date), you could use:
SELECT MONTH(CONCAT('2010-', month, '-', '01')) FROM table1;
Source: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_month

How do I include empty rows in a single GROUP BY DAY(date_field) SQL query?

I'm using MS SQL Server but welcome comparitive solutions from other databases.
This is the basic form of my query. It returns the number of calls per day from the 'incidentsm1' table:
SELECT
COUNT(*) AS "Calls",
MAX(open_time),
open_day
FROM
(
SELECT
incident_id,
opened_by,
open_time - (9.0/24) AS open_time,
DATEPART(dd, (open_time-(9.0/24))) AS open_day
FROM incidentsm1
WHERE
DATEDIFF(DAY, open_time-(9.0/24), GETDATE())< 7
) inc1
GROUP BY open_day
This data is used to draw a bar graph, but if there were no calls on a given day of the week, there is no result row and thus no bar, and the user is like, "why does the graph only have six days and skip from Saturday to Monday?"
Somehow I need to UNION ALL with a blank row from each day or something like that, but I can't figure it out.
I am constrained to what I can do with one SQL statement and I have readonly access so I can't create a temporary table or anything.
How about something like this?
SELECT
COUNT(incident_id) AS "Calls",
MAX(open_time),
days.open_day
FROM
(
select datepart(dd,dateadd(day,-6,getdate())) as open_day union
select datepart(dd,dateadd(day,-5,getdate())) as open_day union
select datepart(dd,dateadd(day,-4,getdate())) as open_day union
select datepart(dd,dateadd(day,-3,getdate())) as open_day union
select datepart(dd,dateadd(day,-2,getdate())) as open_day union
select datepart(dd,dateadd(day,-1,getdate())) as open_day union
select datepart(dd,dateadd(day, 0,getdate())) as open_day
) days
left join
(
SELECT
incident_id,
opened_by,
open_time - (9.0/24) AS open_time,
DATEPART(dd, (open_time-(9.0/24))) AS open_day
FROM incidentsm1
WHERE DATEDIFF(DAY, open_time-(9.0/24), GETDATE()) < 7
) inc1 ON days.open_day = incidents.open_day
GROUP BY days.open_day
I've only tested it on a simplified table schema, but I think it should work. You might need to tinker with the dateadd stuff..
I would suggest the usage of a date table. With an existing date table in place, you can perform a RIGHT OUTER JOIN to the date table to bring in your missing days.
Can you create a table variable with the dates that you need and then RIGHT JOIN onto it? For example,
DECLARE #dateTable TABLE ([date] SMALLDATETIME)
INSERT INTO #dateTable
VALUES('26 FEB 2009')
INSERT INTO #dateTable
VALUES('27 FEB 2009')
-- etc
SELECT
COUNT(*) AS "Calls",
MAX(open_time),
open_day
FROM
(
SELECT
incident_id,
opened_by,
open_time - (9.0/24) AS open_time,
DATEPART(dd, (open_time-(9.0/24))) AS open_day
FROM incidentsm1
RIGHT JOIN #dateTable dates
ON incidentsm1.open_day = dates.date
WHERE
DATEDIFF(DAY, open_time-(9.0/24), GETDATE())< 7
) inc1
GROUP BY open_day
The more ideal situation however, would be to have a table object with the dates in
Can you create the set of dates as part of your query? Something along the lines of:
SELECT COUNT(*) AS Calls, ...
FROM incidentsm1 RIGHT OUTER JOIN
(SELECT date_values
FROM TABLE(('27 Feb 2009'), ('28 Feb 2009'), ('1 Mar 2009'),
('2 Mar 2009'), ('3 Mar 2009'), ('4 Mar 2009'),
('5 Mar 2009')) AS date_list
)
ON ...
This is inspired by a sort of hybrid of Informix and DB2 notations and is pretty much guaranteed to be syntactically incorrect in both. Basically, is there a way in your DBMS of creating a literal table on the fly. One possibility - ugly but barely doable - would be to do a 7-way UNION of date literals selected from 'dual' or some table expression that guarantees one row (in Informix terms, SELECT MDY(2,28,2009) FROM "informix".systables WHERE tabid = 1 UNION ...).