I'm trying to produce a unique value for a date so I can bucket table rows based on their date values. I tried doing this:
SELECT CONCAT(TO_VARCHAR(YEAR($1)), TO_VARCHAR(DAYOFYEAR($1)))
FROM VALUES ('2022-01-01'), ('1972-10-01')
I would expect this to produce the following results:
Column1
1
20221
2
19721
However, I'm actually receiving an error:
Function EXTRACT does not support VARCHAR(10) argument type
I have no idea what the term, EXTRACT, is, and this appears like it should work. What am I doing wrong here?
It requires a slight adjustment. As long as the date strings are YYYY-MM-DD there's no conversion required from the date strings:
SELECT CONCAT(year($1::date), DAYOFYEAR($1::date))
FROM VALUES ('2022-01-01'), ('1972-10-01')
However, the output is different. Since you're using DAYOFYEAR, the second row will have 275 for the day of year.
CONCAT(YEAR($1::DATE), DAYOFYEAR($1::DATE))
20221
1972275
Related
I have a query that was working fine before a server migration and now is not working. I'm trying to convert all dates to a specific month and year, but I keep getting this error:
Conversion failed when converting date and/or time from character string.
Looking into the data, there are no null values in InputDate, which is a date data type column. When I run the Concat() function everything is formatted as 'YYYYMMdd', yet both CAST and CONVERT fail with the same error.
Is there an issue with my query that I'm not seeing?
SELECT RandoSTUFF,
DATEADD(day,2,CAST(CONCAT('2023','02',FORMAT(InputDate,'dd')) AS date)) AS MovedDate
FROM a_table_
I expect the issue is you have date values near the end of their months, and you're trying to compose the equivalent values for February, which is shorter.
So if you have an InputDate value of, say, 2022-12-31 and run the code in the question, it will extract the 31 and concat it with the other values, and you'll end up trying to do this:
CAST('20230231' as Date)
Of course, there is no such date.
As it is, it's not clear whether you want such an input to map to February 28 or March 3. To fix this, you'll need to rethink the problem so you only try to map to valid dates, and ensure the final result is more clearly defined. This is one of the many reasons it's almost always better to use Date/time functions instead of composing dates from strings.
I have a db where I have converted 2 date columns to varchar for the purpose of getting 1 column substringed into another. However, now I don't seem to be able to convert the datatype when I try to use:
ALTER TABLE datacomplete
ALTER COLUMN yearmonth TYPE DATE; /*Can't find a way to specify a format*/
It throws this error:
ERROR: column "yearmonth" cannot be cast automatically to type date
Hint: You might need to specify "USING yearmonth::date".
I'm not sure how to use that command at all, could anyone potentially assist?
My first column is in the format of yyyy-mm-dd, however I'd like it to be yyyymm only, but I'm guessing this is easier once I convert the datatype to date and I can somehow switch formats.
The second column only shows the year so I need to convert it to date as format 'yyyy'.
UPDATE: The first one was solved, now I need to convert the second to 'yyyy'
ALTER TABLE pscomplete_1 ALTER COLUMN "year" TYPE DATE USING "year"::date;
It throws this error
15:12:51 [ALTER - 0 rows, 1.062 secs] [Code: 0, SQL State: 22007] ERROR: >invalid input syntax for type date: "2016"
... 1 statement(s) executed, 0 rows affected, exec/fetch time: 1.062/0.000 sec >[0 successful, 1 errors]
The USING keyword allows you to give the translation function to PostgreSQL.
For your first column it is easy for you already have a correct DATE format:
ALTER TABLE datacomplete ALTER COLUMN yearmonth TYPE DATE USING yearmonth::DATE;
For your second column it is unclear for PostgreSQL which exact date you want. Let's say we want the first of January of the given year:
ALTER TABLE datacomplete ALTER COLUMN year TYPE DATE USING (year || '-01-01')::DATE;
I understood you were starting from a VARCHAR column in the format 'YYYY-MM-DD'.
So I'll do the same.
And you want a column in the date format, and you want a yearmonth in all-digits format.
If I'm not forced to use leading zeroes in an all-digits column, I prefer INT to string columns.
And I prefer to make the same derivation only once if I can do that.
This is why I use a WITH clause (global table expression) to cast the varchar to date, and then I use the resulting date for the DATE_PART() function I use to create the yearmonth column. I have seen very often that date arithmetics are safer and often faster than subtring-ing the date literal (remember, the Americans format dates differently from the Europeans, there are different formats within Europe, and also in Asia, and not all like the ISO date format). So I derive yearmonth as the year multiplied by 100, plus the month, and as DATE_PART() returns a float, I cast the whole expression to INT.
So here goes:
WITH foo(varchar_dt) AS (
SELECT '2017-01-11'
UNION ALL SELECT '2016-12-11'
UNION ALL SELECT '2016-11-11'
UNION ALL SELECT '2016-10-11'
)
, foo_with_date AS (
SELECT
varchar_dt
, CAST(varchar_dt AS DATE) AS the_date
FROM foo
)
SELECT
varchar_dt
, the_date
, CAST(DATE_PART('year',the_date)*100 + DATE_PART('month',the_date) AS INT) AS yearmonth
FROM foo_with_date
;
varchar_dt|the_date |yearmonth
2017-01-11|2017-01-11| 201,701
2016-12-11|2016-12-11| 201,612
2016-11-11|2016-11-11| 201,611
2016-10-11|2016-10-11| 201,610
I can't help myself - I find this much cleaner, and filtering by yearmonth would become filtering by an integer, which is always at least a little bit faster than strings.
Happy playing
Marco the Sane
I have date field in the database in the format 2012-03-17 19:50:08.023.
I want to create a select query which gives me the data collected in the March month.
But I am not able to achieve this.
I am trying following query.
select * from OrderHeader where
Convert(varchar,UploadDt,103) between '01/03/2013' and '31/03/2013'
and DistUserUniqueID like '6361%'
This query gives me data for all the dates.
select * from OrderHeader where
UploadDt between '01/03/2013' and '31/03/2013' and DistUserUniqueID like '6361%'
This query gives me the error as Msg 242, Level 16, State 3, Line 1
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
Please help me resolve this.
Thanks in advance
The first query returns all dates because you are converting your column to a string. Not sure why you are doing this. So when you say BETWEEN '01/anything' AND '31/anything', when you consider it is now just a string, that is going to match all "dates" in the column, regardless of month and year, since your WHERE clause will cover every single day possible (well, with the exception of the 31st of months other than March, and the first day in January and February - so not all the data but a very large percentage). '15/11/2026', for example, is BETWEEN '01\03\2013' AND '31/03/2013'.
Think about that for a second. You have datetime data in your database, and you want to convert it to a string before you query it. Would you also convert salary to a string before comparing it? If so, the person making $70,000 will look like they are making more than the person making $690,000, since character-based sorting starts at the first character and doesn't consider length.
The second query fails because you are using a regional, ambiguous format for your dates. You may like dd/mm/yyyy but clearly your server is based on US English formatting where mm/dd/yyyy is expected.
The solution is to use a proper, unambiguous format, such as YYYYMMDD.
BETWEEN '20130301' AND '20130313'
However you shouldn't use BETWEEN - since this is a DATETIME column you should be using:
WHERE UploadDt >= '20130301'
AND UploadDt < '20130401'
(Otherwise you will miss any data from 2013-03-31 00:00:00.001 through 2013-03-31 23:59:59.997.)
If you really like BETWEEN then on 2008+ (you didn't tell us your version) you can use:
WHERE CONVERT(DATE, UploadDt) BETWEEN '20130301' AND '20130331'
More date-related tips here:
Dating Responsibly
Finally, when converting to VARCHAR (or any variable-length data types), always specify a length.
Rewrite the query as
select * from OrderHeader where
UploadDt between '01/03/2013' and '01/04/2013'
and DistUserUniqueID like '6361%'
or
select * from OrderHeader where
UploadDt between Convert(datetime,'01/03/2013', 103) and Convert(datetime,'01/04/2013',103)
and DistUserUniqueID like '6361%'
I have a table named book_data with batch_dt as column name of type varchar in sql server.
when I pass the query
SELECT DISTINCT batch FROM book_data
it gives me the following results
batch_dt
-------------
2012-10-31
-------------
2012-11-01
-------------
2012-11-02
-------------
2012-11-03
-------------
.
.
.
Now what I am doing is getting the total count of records between two dates. Fairly a simple query.
SELECT COUNT(*) FROM book_data WHERE CONVERT(varchar(12),CONVERT(datetime,batch_dt),101) BETWEEN '11/02/2012' and '10/31/2012'
the result is
112
and just by changing the month from 02 to 2 the query gives me 218 results
SELECT COUNT(*) FROM book_data WHERE CONVERT(varchar(12),CONVERT(datetime,batch_dt),101) BETWEEN '11/2/2012' and '10/31/2012'
why this different behaviour?
Use CAST(batch_dt AS DATE) instead, and pass the date in a language neutral manner, in the format YYYYMMDD. This way it will be comared as a date not as a varchar:
SELECT COUNT(*)
FROM book_data
WHERE CAST(batch_dt AS DATE)
BETWEEN '20121102' and '20121130'
But, this is not safe, if there was any value in barch_dt in a wrong format, you will get a casting error. In this case you can add ISDATE(batch_dt) = 1 to ensure that it is a valid data time. But it is better to make this column of datatype DateTime.
Another thing to note: is that BETWEEN is asymmetric in SQL Server, meaning that BETWEEN '11/02/2012' and '10/31/2012' is evaluated as:
DATE >= '11/02/2012'
AND
DATE <= '10/31/2012'
which will never be true, the reason it works for you is that the dates were be compared as strings not as a dates. But you have to keep it like BETWEEN the small value and the biggest value.
You compare string with BETWEEN. If you do so you need to make sure that you compare in the correct order => YYYYMMDD MM:SS would be a correct order.
If you can, add columns with type datetime and store real date time values in your database. If you can not do that you can split up the values and build a date value yourself. This is much slower then just use a CONVERT() or CAST() but you can make sure that it works even with wrong date-strings.
You can use PATINDEX(),LEFT(),RIGHT() keywords to get the values you need or you use a split() function (you can find many version google it, e.g. https://codereview.stackexchange.com/questions/15125/sql-server-split-function-optimized). If you use the split function, then split by / and then get year, month, day from the positions.
But best would be still to have correct datetime values stored in your db.
You get this different behavior because you don't compare the dates but the strings/varchars.
For a Date (or DateTime), 10/2/2012 is the same as 10/02/2012.
But for string, these values are (of course) different. It's just as if you'd compare 'abcd' with 'ab0cd'
SELECT COUNT(*)
FROM book_data
WHERE CONVERT(DATETIME,batch_dt,101) BETWEEN '11/2/2012' and '10/31/2012'
This would be more appropriate
I am using MS SQLServer and trying to insert a month/year combination to a table like this:
INSERT INTO MyTable VALUES (1111, 'item_name', '9/1998')
apparently, the above command cannot work since
Conversion failed when converting date and/or time from character string.
Because 9/1998 is a bad format. I want to fix this and this column of the table will show something like:
9/1998
12/1998
(other records with month/year format)
...
Can someone help me with this?
thank you
SQL Server only supports full dates (day, month, and year) or datetimes, as you can see over on the MSDN data type list: http://msdn.microsoft.com/en-us/library/ff848733(v=sql.105).aspx
You can use a date value with a bogus day or store the value as a string, but there's no native type that just stores month/year pairs.
I see this is an old post but my recent tests confirm that storing Date or splitting the year and month to two columns (year smallint, month tinyint) results in the overall same size.
The difference will be visible when you actually need to parse the date to the filter you need (year/month).
Let me know what do you think of this solution! :)
Kind regards
You can just use "01" for the day:
INSERT INTO MyTable VALUES (1111, 'item_name', '19980901')
You can:
1) Change the column type to varchar
2) Take the supplied value and convert it to a proper format that sql server will accept before inserting, and format it back to 'M/YYYY' format when you pull the data: SELECT MONTH([myDate]) + '/' + YEAR([myDate]) ...
You may want to consider what use you will have for your data. At the moment, you're only concerned with capturing and displaying the data. However, going forward, you may need to perform date calculations on it (ie, compare the difference between two records). Because of this and also since you're about two-thirds of the way there, you might as well convert this field to a Date type. Your presentation layer can then be delegated with the task of displaying it appropriately as "MM/yyyy", a function which is available to just about any programming language or reporting platform you may be using.
if you want use date type, you should format value:
declare #a date
SELECT #a='2000-01-01'
select RIGHT( convert (varchar , #a, 103), 7) AS 'mm/yyyy'
if you want make query like SELECT * FROM...
you should use varchar instead date type.