column value is varchar which needs to be converted to decimal. column name is in date format. sql server - sql

I have a table tblSample which has columns names as date format value i.e. [202007], [202006]---[202001] and datatype is varchar.
I want to update another column(X) of tblSample with [202006]-[202005] and column(Y) with [202005]-[202004].
I am using below query which is giving output as 1 for all the records(around 50 records in tblSample).
UPDATE tblSample
SET
X = CAST(ISNULL(SELECT LEFT(CONVERT(varchar, DATEADD(month, -3, GETDATE(), 112), 6)), 0) AS DECIMAL(18,2)) - CAST(ISNULL((SELECT LEFT(CONVERT(varchar, DATEADD(month, -4, GETDATE()), 112), 6)), 0) AS DECIMAL(18, 2))
I am using LEFT(CONVERT(varchar, DATEADD(month, -3, GETDATE(), 112), 6)) since the column name value will be updated everymonth to current month.
I have to convert to varchar to decimal since I am getting error saying subtraction cannot be performed on varchar values.
Where is that I am going wrong and will my update query, updates column-X and column-Y value correct for each row in the table
Values in columns of tblSample [2006]-3565.24, [2005]-3461.36, [2004]-3510.36.
Output
Thanks Shyam

You can do the update using the column names:
update tblsamples
set x = [202006]-[202005],
y = [202005]-[202004] ;
Changing names in tables is a really bad idea. You should instead have one row per month. You can then easily calculate the difference using lag():
select t.*, (value - lag(value) over (partition by ? order by yyyymm)) as diff
from t;
Perhaps re-pivoting if necessary.
If you are constructing the table each month, then you should construct the differences as well. Or, perhaps more simply, just add columns called latest_month and previous_month and use those in the update.
Finally, if you are stuck with this format, you will need to use dynamic SQL. That seems like a bad idea, though.

Related

How to split dash-separated values in SQL Server

I have a date saved in an nvarchar type and I want to split the day, month and year into separate nvarchar variables (that means three variables). The date looks as follows: exposure_date ='2018-12-04' and the format is yyyy-dd-mm
any help please?
My whole project is stuck on this.
The "correct" answer here is to fix your datatype. When storing data always choose an appropriate data type for the data you're storing. For a date (with no time part) then the correct datatype is date. if you're storing numerical data, then use a numerical datatype, such as int or decimal. (n)varchar is not a one size fits all datatype and using it to store data that has a data type designed for it is almost always a bad choice. I'm storing the data as an (n)varchar because I need it in a specific format is never an excuse; have your presentation layer handle to display format, not your RDBMS.
The first step, therefore would be to change your string representation yyyy-dd-MM of a date to the ISO format yyyyMMdd by doing:
UPDATE YourTable
SET exposure_date = LEFT(exposure_date,4) + RIGHT(exposure_date,2) + SUBSTRING(exposure_date,6,2);
Now you have a unambiguous representation, you can change the data type of your column without concerns of incorrect implicit casts or error:
ALTER YourTable ALTER COLUMN exposure_date date;
Then, finally, you can treat your data as what it is, a date, and use the DATEPART function:
SELECT DATEPART(YEAR,exposure_date) AS Exposure_Year,
DATEPART(MONTH,exposure_date) AS Exposure_Month,
DATEPART(DAY,exposure_date) AS Exposure_Day
FROM YourTable;
You can also try the following
Declare #myDate date
select #myDate= Cast(substring('2011-29-12', 1, 4)
+ '-' + substring('2011-29-12', 9, 2)
+ '-' + substring('2011-29-12', 6, 2)
as Date) --YYYY-MM-DD
Select #myDate as DateTime,
datename(day,#myDate) as Date,
month(#myDate) as Month,
datename(year,#myDate) as Year,
Datename(weekday,#myDate) as DayName
The output is as shown below
DateTime Date Month Year DayName
--------------------------------------------
2011-29-12 29 12 2011 Thursday
You can find the live demo here
You can try below -
select concat(cast(year(cast('2018-12-04' as date)) as varchar(4)),'-',
cast(month(cast('2018-12-04' as date)) as varchar(2)), '-',
cast(day(cast('2018-12-04' as date)) as varchar(2)))
from tablename
If you have fixed format, then you could use this simple query with substring method:
select substring(dt, 1, 4) + '-' +
substring(dt, 9, 2) + '-' +
substring(dt, 6, 2) [YYYY-MM-DD]
from (values ('2018-31-12')) tbl(dt)
Let's go directly to the main issue, which is you are using the wrong datatype to store dates, you should store them as DATE, the datatypes are there for a reason and you need to choose a proper one for your column.
So, you need to ALTER your table and change the column datatype to DATE instead of NVARCHAR datatype.
ALTER <Table Name Here>
ALTER COLUMN <Column Name Here> DATE;
Then all things will easy, you just run the following query to get the desired output
SELECT YEAR(<Column Name Here>) TheYear,
MONTH(<Column Name Here>) TheMonth,
DAY(<Column Name Here>) TheDay
FROM <Table Name Here>
Which is the right and the best solution.
You can also (if you are not going to alter your table) do as
CREATE TABLE Dates(
StrDate NVARCHAR(10)
);
INSERT INTO Dates VALUES
(N'2018-12-04'),
(N'Invalid');
SELECT LEFT(StrDate, 4) StrYear,
SUBSTRING(StrDate, 6, 2) StrMonth,
RIGHT(StrDate, 2) StrDay
FROM Dates;
OR
SELECT YEAR(StrDate) StrYear,
MONTH(StrDate) StrMonth,
DAY(StrDate) StrDay
FROM (
SELECT TRY_CAST(StrDate AS DATE) StrDate
FROM Dates
)T

SQL WHERE clause to pick data for last 7 days

I want to build a view on the server with a SELECT statement and pick all records that are created at the last 7 days?
Original creation_date field is in varchar like '18/11/08' and I use the CONVERT(datetime, creation_date,11) to convert it into 2018-11-08 00:00:00.000, but I don't know how to do in the WHERE clause, so it only selects all records created for last 7 days.
use where clause like below
select t.* from your_table t
where CONVERT(datetime, creation_date,11)>= DATEADD(day,-7, GETDATE())
In order to get the best performance, you should keep the calculation away from the column:
SELECT *
FROM <YourTable>
WHERE creation_date >= CONVERT(char(8), DATEADD(day,-7, GETDATE()), 11)
This will handle it well because of the format of the varchar - yy/mm/dd. Would not have worked with all formats
The best thing to do is store dates properly to begin with - in a column with a Date data type.
Assuming you can't change the database structure, you can use DateDiff with GetDate():
SELECT <ColumnsList>
FROM <TableName>
WHERE DATEDIFF(DAY, CONVERT(datetime, creation_date,11), GETDATE()) <= 7
Of course, you need to replace the <ColumnsList> with the list of columns and <TableName> with the actual table name.

I have a column of dates in varchar and i need to convert to date

The dates are currently displayed as: ddmmmyyyy (12DEC2013)
I've been playing around with this formula:
DECLARE #Date char(8)
set #Date='12312009'
SELECT CONVERT(datetime,RIGHT(#Date,4)+LEFT(#Date,2)+SUBSTRING(#Date,3,2))
but I didn't have any success, can someone help me out with this. Additionally all my dates are in a column called TERMDT and I'd like to put all the new date values in a new column formatted as such.
Just give convert() an appropriate 3rd argument (the format):
SELECT CONVERT(datetime,
RIGHT(d, 4) + LEFT(d,2) + SUBSTRING(d, 3, 2),
112)
from (select '12312009' as d) t

SQL: How to find records between 2 dates

The dates are in Y2K date string format
eg) 1120104 = 20120104
Running SQL Server 2008
Well I agree with #usr, first, stop storing dates this way.
If you can't, then add a computed column (which you could even persist and/or index):
ALTER TABLE dbo.MyTable ADD RealDate
AS CONVERT(DATE, RIGHT(col, 6), 12);
If you can't do that, then create a view:
CREATE VIEW dbo.SmarterView
AS
SELECT /* other columns, */
RealDate = CONVERT(DATE, RIGHT(col, 6), 12)
FROM dbo.MyTable;
Then:
SELECT ... FROM dbo.MyTable -- or dbo.SmarterView
WHERE RealDate >= #Start
AND RealDate < DATEADD(DAY, 1, #End);
Open-ended ranges are far better than BETWEEN - calculating the end of the month sucks, especially in February, and especially if you have any potential for date/time data type changes. See:
What do BETWEEN and the devil have in common?
If you have dates in the 1900s, then it is slightly more involved:
CONVERT(DATE, CONVERT(CHAR(2), 19 + CONVERT(INT, LEFT(col, 1))) + RIGHT(col, 6), 112);
In any case, it's irrelevant, since the OP stated they will store it correctly as DATE.
First advice: store the dates as data type date or datetime so that you can actually search on them.
If you don't want that, you need to convert the strings to date first so that you can search on them:
where convert(date, my_nasty_string_column_with_dates) between #a and #b
This is a perf problem because the query will never be able to seek on an index.
You should be able to compare at Y2K date with another with simple equality checks. If you want to convert the Y2K to DateTime then you need to add 19000000 then convert it like this example:
DECLARE #Y2KDate INT
SET #Y2KDate = 1120104
DECLARE #DateTime DateTime
SET #DateTime = CONVERT(DATETIME, CONVERT(CHAR(8), #Y2KDate + 19000000), 112)
PRINT #DateTime
Prints the following output
1120104
20120104
Jan 4 2012 12:00AM

How to only check the time on datetime fields but ignore the date?

I have a column that stores data in datetime format. I want to check for all instances where the time part of this column is not equal to 00:00:00:000 - the date does not matter.
Basically, if time() was a function, something like this:
SELECT *
FROM progen.DY
WHERE TIME(DY_DATE) <> '00:00:00:000'
How do I go about doing this?
You only need a minor tweak on what you already have.
SELECT *
FROM progen.DY
WHERE TIME(DY_DATE) <> '00:00:00:000'
Use CONVERT to change your DATETIME to a TIME.
SELECT *
FROM progen.DY
WHERE CONVERT(TIME, DY_DATE) <> '00:00:00:000'
Another way is to convert it to different datatype, eg
SELECT *
FROM progen.DY
WHERE CAST(DY_DATE as float) - CAST(DY_DATE as int) > 0
SQLFiddle Demo
I do this all the time when trying to see if a table's column should be turned into a date instead of a datetime, which is really the answer.
select *
from progen.dy
where cast(dy_date as Date) <> dy_date
the cast removes the time and datetime has higher precedence, so when compared, if the are unequal then it has a time value. Same thing could be done with a cast to time, with a bit of different syntax.
Use DATEDIFF and DATEADD to instead get the date part of the datetime. Compare the column against the date only, and it will return those rows that have a non-zero time.
The way this works is that we first calculate the difference (in days) between the epoch and the value. We add that number to the epoch to create a new datetime. Since the result of DATEDIFF is an integer, any time component gets rounded off.
SELECT *
FROM Table
WHERE DateColumn <> DATEADD(d, DATEDIFF(d, 0, DateColumn), 0)
The time function could then be implemented by the following, not that I recommend it for this specific scenario:
SELECT DATEDIFF(minute, DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), DateColumn) as MinutesIntoDay,
-- or, if you require higher precision
DATEDIFF(second, DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), DateColumn) as MinutesIntoDay
FROM Table
Edit: As mentioned in other answers, you can cast to DATE to achieve the same effect as DATEADD(d, DATEDIFF(d, 0, DateColumn), 0), which cleans up nicely. However, DATE was only added in SQL Server 2008, whereas the formula has compatibility back to at least SQL 2000. So if you need the backwards compatibility or are dealing with SQL CE, casting to DATE is unavailable.
SELECT *
FROM progen.DY
WHERE CONVERT(TIME, DY_DATE - CONVERT(DATE, DY_DATE)) > '00:00'