How to cast year, month, day to date? - sql

I have data something like this:
year (string)(Partitioned) | month (string)(Partitioned) | day (string)(Partitioned) | products
I want to cast year, month, day to only one date as date and the filter it with the following query:
SELECT year, month, day, products,
FROM X
WHERE date >= date('2020-05-01')
GROUP BY 1, 2, 3
How can I do this?

Do you talk about SQL Server? Then the best string format to cast to date is 'YYYYMMDD'. So you could cast your date numbers to strings, concatenate them and cast the result to a string again, like this:
SELECT [year], [month], [day], products
FROM X
WHERE CAST (
CAST([year] AS varchar) +
CAST([month] AS varchar) +
CAST([day] AS varchar)
AS date) >= '20200501'
GROUP BY 1, 2, 3;
You can also use the DATEFROMPARTS function which looks better:
https://learn.microsoft.com/de-de/sql/t-sql/functions/datefromparts-transact-sql?view=sql-server-ver15
SELECT [year], [month], [day], products
FROM X
WHERE DATEFROMPARTS ([year], [month], [day]) >= '20200501'
GROUP BY 1, 2, 3;
Otherwise you could convert it in a single number and compare it. This is my prefered solution:
SELECT [year], [month], [day], products
FROM X
WHERE [day] + 100*[month] + 10000*[year] >= 20200501
GROUP BY 1, 2, 3;

You can try:
cast(year || '-' || month || '-' || day as date)

Related

SQL sorting month end (mmm-yy) starting with April

In SQL I am trying to convert dates to mmm-yy and then sort starting with April as the first month. So far I have managed to converted the date to mmm-yy using
SELECT
LNAME as Location,
SUBSTRING( CONVERT( VARCHAR(11), MonthEnd, 113), 4, 8) AS [MonthEnd],
CAST (TYPEDESC as VARCHAR(20)) as 'Factory',
Sum(Tonnes) as Tonnes
FROM (
SELECT
EOMONTH(X_DELIVERY_DATE) as MonthEnd,
...
but I cannot complete the final step, all I get is the date sorted alphabetically. If someone could please help!!
Let's say that you use SQL Server:
DECLARE #Temp TABLE
(
MonthEnd DATETIME
)
INSERT #Temp VALUES
('2015/01/01'),
('2015/02/01'),
('2015/03/01'),
('2015/04/01'),
('2015/05/01'),
('2015/06/01'),
('2015/07/01'),
('2015/08/01'),
('2015/09/01'),
('2015/10/01'),
('2015/11/01'),
('2015/12/01'),
('2016/01/01'),
('2016/02/01'),
('2016/03/01'),
('2016/04/01'),
('2016/05/01'),
('2016/06/01'),
('2016/07/01'),
('2016/08/01'),
('2016/09/01'),
('2016/10/01'),
('2016/11/01'),
('2016/12/01')
SELECT MonthEnd, SUBSTRING(CONVERT(VARCHAR(11), MonthEnd, 113), 4, 8)
FROM #Temp
ORDER BY YEAR(MonthEnd), (MONTH(MonthEnd) + 8) % 12
You can play with the DATEPART function
DATEPART(month, '10/5/2015')
And try this calculation:
SELECT (DATEPART(year, '3/5/2015') * 100) +
(CASE WHEN DATEPART(month, '3/5/2015') > 3
THEN DATEPART(month, '3/5/2015') - 4
ELSE DATEPART(month, '3/5/2015') + 9
END);

T-SQL Sorting by year and month

I would like to sort by date according to date yyyy,mm.
I tried using VARCHARS first:
GROUP BY (YEAR(join_date) AS VARCHAR(4)) + '-' + CAST(MONTH(join_date) AS VARCHAR(2))
But converting them to varchar meant my sort went like
2014.1 - 2014.11 - 2014.12 - 2014.2
How could I sort them in properly by year then month order?
SELECT Year(join_date) + MONTH(join_date) AS Date, COUNT(*) AS Count
FROM X
WHERE mtype ='A' AND (join_date BETWEEN DATEADD(year, -5, GETDATE()) AND GETDATE())
GROUP BY YEAR(join_date) + MONTH(join_date)
ORDER BY Date ASC
You just don't need to convert them to varchar, YEAR and MONTH return ints:
SELECT Year(join_date) As JoinedYear, MONTH(join_date) AS JoinedMonth, COUNT(*) AS Count
FROM X
WHERE mtype ='A' AND (join_date BETWEEN DATEADD(year, -5, GETDATE()) AND GETDATE())
GROUP BY YEAR(join_date), MONTH(join_date)
ORDER BY YEAR(join_date) ASC, MONTH(join_date) ASC

How to properly convert a date into a ISO-8601 Week number format in SQL Server?

ISO-8601 states that week numbers are to be formated as YYYY-W## - observe that the week number should be two digits as 01, 02, ...
SELECT cast(DATEPART(YYYY, CreationDate) as varchar) + '-W' +
cast(DATEPART(ISO_WEEK, GETDATE())`
The problem is that this gives the week number as 1, 2, ...
What is the correct way of extracting 2020-W01, ...
The original question text relates to a simple formatting issue of the ISO_WEEK number, but I feel there is a bigger issue here with the concatenation of the year part. Simply concatenating DatePart YYYY and ISO_WEEK will yield incorrect (or at least unexpected) results for days in week 1 and/or 53. Notably dates like 2014-12-31 are part of week 1, but not 2014-W01. It is part of 2015-W01. Similarly, 2016-01-01 will be part of 2015-W53, not 2016-W53. In order to determine the Iso Year-Week the year must be corrected to take this into account:
With
Dates (Date) As (
Select Convert( date, N'2014-12-31' )
Union All Select Convert( date, N'2015-01-01' )
Union All Select Convert( date, N'2015-12-31' )
Union All Select Convert( date, N'2016-01-01' )
)
Select
src.Date
, Concat( Case
When DatePart( Month, src.Date ) = 12 And DatePart( ISO_WEEK, src.Date ) = 1 Then DatePart( Year, src.Date ) + 1
When DatePart( Month, src.Date ) = 1 And DatePart( ISO_WEEK, src.Date ) > 50 Then DatePart( Year, src.Date ) - 1
Else DatePart( Year, src.Date )
End, N'-W', Right( Concat( N'00', DatePart( ISO_WEEK, src.Date ) ), 2 ) ) As IsoYearWeek
From
Dates src
;
SELECT cast(DATEPART(YYYY, CreationDate) as varchar) + '-W' + Right('0'+cast(DATEPART(ISO_WEEK,CreationDate) as Varchar),2)

How do I group DATE field by YEAR-MM in SQL Server?

I have a date field in a query and I do want to get GROUP BY report like this:
DATE COUNT
2010-01 10
2010-02 2
...
2010-12 24
2012-13 34
What is the proper syntax to obtain this on SQL Server?
All these conversions to string work, but I find this method more efficient, albeit less readable:
SELECT m = DATEADD(MONTH, DATEDIFF(MONTH, 0, [DATE]), 0), COUNT(*)
FROM dbo.TheTable
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, [DATE]), 0);
If you don't want to repeat the expression, then:
;WITH x AS (SELECT m = DATEADD(MONTH, DATEDIFF(MONTH, 0, [DATE]), 0)
FROM dbo.TheTable)
SELECT m, COUNT(*)
FROM x GROUP BY m;
This way the output is still a date/time value and can be used that way for other things. And it doesn't involve any messy string conversions.
CONVERT(VARCHAR(7), CreationDate, 120) as Date
You can simply do:
select convert(char(7), #myDate, 20)
Example
declare #myDate as DateTime
set #myDate = '2012-06-23'
select convert(char(7), #myDate, 20)
Output
-------
2012-06
So the full statement would look like:
select convert(char(7), myDate, 20), count(*) as Count
from MyTable
group by convert(char(7), myDate, 20)
Update
The sample data includes the value 2012-13. I am going to assume this is a typo and that the number after the dash represents the month.
SELECT CAST(DATEPART(year, dateCol) as VARCHAR) + '-' + CAST(DATEPART(month, dateCol) as VARCHAR) as Date, count(*) As Count
FROM myTable
GROUP BY CAST(DATEPART(year, dateCol) as VARCHAR) + '-' + CAST(DATEPART(month, dateCol) as VARCHAR)

SQL Server 2008 select data only between month and year

I would like select data between two date, without day
An input example:
start month: 9 , start year: 2011
end month: 3, end year: 2012
I think that there are two way to do this.
The first is convert start month and start year to date like 2011-09-01 and convert last date to 2012-03-31, but this requires calculation of the last day of end month. Obtained these date we can use a BEETWEN function for the WHERE clause (but, is the CONVERT function reliable?)
The second solution is to use the DATEPART function like in the following code:
I try to explain: if end year is equal to the initial year, then month must be between the start and end months; else if the final months is greater than the initial years if different from the initial and final year, I take everything in between; else if the final year, the month must be less than or equal to the final month, if the initial year, month must be greater than or equal to the final month
Can you help me do this in the best way? Is correct, the solution I adopted?
declare #IndDebitoCredito bit,#ProgTributo int,#mi as integer,#ai as integer,#mf as integer,#af as integer,#IDAnagrafica varchar(5)
select #mi = 01,#ai = 2011,#mf = 12,#af = 2011,#IDAnagrafica = 'DELEL',#IndDebitoCredito = 1
select distinct rrd.IDTributo
from TBWH_Delega d
--inner join TBWH_SezioneDelega sd on d.IDDelega = sd.IDDelega
inner join TBWH_Rigo rd on rd.IDDelega = d.IDDelega
inner join TBWH_RataRigo rrd on rrd.IDRigo = rd.IDRigo
where
(
DATEPART(MM,d.DataDelega)<=#mf and
DATEPART(MM,d.DataDelega)>=#mi and
DATEPART(YYYY,d.DataDelega)=#ai and
#af = #ai
)
OR
(
--anno finale magg. anno iniziale
#af > #ai AND
(
( -- delega nell'intervallo
DATEPART(YYYY,d.DataDelega)<#af AND
DATEPART(YYYY,d.DataDelega)>#ai
-- DATEPART(MM,d.DataDelega)>=#mi
)
OR
( -- delega limite destro
DATEPART(YYYY,d.DataDelega)=#af AND
DATEPART(MM,d.DataDelega)<=#mf
)
OR
( -- delega limite sinistro
DATEPART(YYYY,d.DataDelega)=#ai AND
DATEPART(MM,d.DataDelega)>=#mi
)
)
)
GO
Your first solution is almost there, but is more complicated than it needs to be and won't work anyway. It will miss out any rows from the last day of the end month.
You can add one month to the end month and then use BETWEEN on the first of each month. eg.
start month: 9 , start year: 2011
end month: 3, end year: 2012
BETWEEN '2011-09-01' AND '2012-04-01'
or, as JNK points out, this will be better:
DataDelega >= '2011-09-01' AND DataDelega < '2012-04-01'
You'll need to add in some logic to deal with the end month being December, but this looks like the simplest way of doing it.
You are WAY overcomplicating this. You really only need two comparisons:
Is the month and year after or equal to the initial value?
Is the month and year before or equal to the final value?
Try:
SELECT *
FROM MyTable
WHERE Datefield BETWEEN
CAST(#mi as varchar) + '/1/' + CAST(#ai as varchar)
-- first of first month
AND
DATEADD(DAY, -1, (DATEADD(Month, + 1, (CAST(#mf as varchar) + '/1/' + CAST(#af as varchar)))))
-- Last day or final month
SELECT *
FROM Table
WHERE DateField
BETWEEN CONVERT(DATE, CONVERT(CHAR(4), #ai) + RIGHT('00' + CONVERT(VARCHAR(2), #mi), 2) + '01', 112)
AND DATEADD(DD, -1, DATEADD(MM, 1, CONVERT(DATE, CONVERT(CHAR(4), #af) + RIGHT('00' + CONVERT(VARCHAR(2), #mf), 2) + '01', 112)))
Avoid using expressions on the DateField columns, as it makes query not SARGable.
I would use:
WHERE DateToCheck >= --- first day of StartMonth
DATEADD( mm, #StartMonth-1,
DATEADD( yy, #StartYear-2000, '2000-01-01')
)
AND DateToCheck < --- first day of next month (after EndMonth)
DATEADD( mm, #EndMonth,
DATEADD( yy, #EndYear-2000, '2000-01-01')
)
DECLARE #mi INT
, #ai INT
, #mf INT
, #af INT
SELECT #mi = 01
, #ai = 2011
, #mf = 12
, #af = 2011
--local variables to hold dates
DECLARE #i DATETIME
, #f DATETIME
--build strings to represent dates in YYYYMMDD format
--add a month to the #f date
SELECT #i = CONVERT(VARCHAR(4), #ai) + RIGHT('0' + CONVERT(VARCHAR(2), #mi),
2) + '01'
, #f = DATEADD(month, 1,
CONVERT(VARCHAR(4), #af) + RIGHT('0'
+ CONVERT(VARCHAR(2), #mf),
2) + '01')
--select data where date >= #i, and < #f
SELECT *
FROM MyTable
WHERE DateField >= #i
AND DateField < #f