SQL Server: substring leading zero counting is off - sql-server

I'm trying to make 3 separate fields from a date and now I ran into this problem. When the (DDMMYYYY European style) date is like 04032017, the code:
SELECT SUBSTRING(CAST(04032017 AS VARCHAR(38)), 2, 2) AS Month
returns 03 (perfect).
But when the code is like (the first zero is now a 1!) :
SELECT SUBSTRING(CAST(18022017 AS VARCHAR(38)), 2, 2) AS Month
the result is 80 because SUBSTRING now is counting from the (1) first position and in the first example it took 4 as the starting point.
Obviously I need to have 1 code for all occurrences but I just don't get it right.
Some help would be appreciated!
Regards, J

This should work for you:
select SUBSTRING(right('00000000' + CAST(04032017 AS varchar(38)),8), 3, 2) as Month

You might require to parse this as varchar and then convert into valid date and use month() function to get clear approach
declare #date nvarchar(10)='18022017'
select Month(cast(CONCAT(SUBSTRING(#date,3,2),'/',SUBSTRING(#date,1,2),'/',SUBSTRING(#date,5,4)) as date))

Ok, just solved it. Just remove the cast (why did I use that in the first place?) and put single quotes around the date:
select SUBSTRING('04032017'), 2, 2) as Month
This works just fine!

Related

substring to retrieve a date between two characters

Could someone shed some light on how I could extract the date field from this string below
'NA,Planning-Completed=17-07-2019 10:38,Print-Dispatch-Date=10-02-2020 13:06,Award-Complete=NA'
Expected Output:
10-02-2020
This is what I have written so far, it seems to work sometimes but I noticed some rows are incorrect.
[Dispatch Date] = SUBSTRING(dates,CHARINDEX('Print-Dispatch-Date=',dates) + LEN('Print-Dispatch-Date='), LEN(dates) )
The 3rd parameter for SUBSTRING is wrong. You have it as LEN(dates), which means return every character after 'Print-Dispatch-Date='; in this case that returns '10-02-2020 13:06,Award-Complete=NA'.
As your date is 10 characters long, just use 10:
SELECT dates, SUBSTRING(dates,CHARINDEX('Print-Dispatch-Date=',dates) + LEN('Print-Dispatch-Date='), 10)
FROM (VALUES('NA,Planning-Completed=17-07-2019 10:38,Print-Dispatch-Date=10-02-2020 13:06,Award-Complete=NA'))V(dates)

T-SQL: SUM Number between Delimiters from String

I need to get numbers with a variable length out of a string and sum them.
The strings got the following format:
EH:NUMBER=SomeOtherStuff->Code
I'm extracting the code via RIGHT() and join with another table to get the group right, at the moment I'm using sum to get it together via date:
SUM(CASE WHEN (MONTH(data.DATE1) = 5 AND YEAR(data.DATE1) = YEAR(GETDATE())) THEN 1 ELSE 0 END) N'Mai',
I then need to sum the numbers from the string and not the number of rows.
Some Examples:
Month1 EH:1=24->ZTM
Month1 EH:4=13-21->LKm
Month2 EH:3=34,33,43->LKm
Month2 EH:7=12,92-29,29->LKm
Month2 EH:5=24-26,11,21,22->ZOL
What i need:
Material - Month1 - Month2
ZTM - 1 - 0
LKM - 4 - 10
ZOL - 0 - 5
Could you help me please?
Greetings
Short version:
What you are looking for is SUBSTRING.
Longer version:
To get the the sum of the numerical value of NUMBER you need think about how break it down.
I'd recommend following these steps:
Extract the NUMBER part from the string. This should be done with SUBSTRING (much like you extract Code with RIGHT). To get the start and and length och your substring use charindex ( or patindex if you like).
Convert the NUMBER part to a numerical value with cast (or convert or what you are familiar with)
Now you can do your aggregation.
So SUM(CAST(SUBSTRING(*this part you will have to figure out by yourself)) as correct numerical data type)).
I'll let you figure out the values to insert by yourself and would recommend to first find the positions of the delimiting characters, then extract the NUMBER part, then get the numerical value .... you get it .
This to gain a better understanding of what you are actually doing.
Cheers, and good luck with your assignment
Martin

Is there any way to combine the functionality of LIKE and BETWEEN in a query?

Im working on a query where the most obvious solution would be to combine the functionality of Like and Between, but im not sure if its possible.
My goal is to get ID's between a certain range, where the IDs are constructed of a leading code, followed by datetime, and the possible a couple more caracters like so:
ABC180715051623XYZ
The range would be from the current time, to 10 minutes prior. the leading characters don't matter for what is chosen, just the date time numbers in the center. an additional issue is that these leading characters can vary in length, somtimes 2, and other times 4.
On thatnote, ive been trying to use wildcards, and the like function, but they dont work as needed on there own. Is there any way to combine them?
Thank You
Assuming that:
The leading ID characters are always letters (actually just not numbers)
The date is always in the format yyMMddhhmmss
Then you could have your query as such:
SELECT * FROM [your_table]
WHERE CONVERT( -- Converts to a datetime
DATETIME, STUFF( -- STUFF #1
STUFF( -- STUFF #2
STUFF( -- STUFF #3
SUBSTRING([id_column],
PATINDEX('%[0-9]%',[id_column]), -- gets the index of the first number
12), -- gets the 12 digit date string (SUBSTRING)
7, 0, ' '), -- adds a space between date and time portions (STUFF #3)
10, 0, ':'), -- adds a ':' between hours and minutes (STUFF #2)
13, 0, ':')) -- adds a ':' between minutes and seconds (STUFF #1)
BETWEEN DATEADD(minute,-10, CURRENT_TIMESTAMP) -- 10 minutes ago
AND CURRENT_TIMESTAMP; -- now
This is taking the date and time part of the ID and forming it into a string that can then be converted into a datetime which can then be used in a the between of 10 mins ago and now. I've tried to format it so that it can be read but I'll explain the parts from the inside out below so you can edit if required.
Using your given value for ID of
ABC180715051623XYZ
First PATINDEX('%[0-9]%',[id_column]) This gets the (1-based) index first number in the id column. So this would be 4
This makes SUBSTRING([id_column], 4, 12) which gets out 180715051623
So then STUFF('180715051623', 7, 0, ' ') puts a space at the 7th index, giving 180715 051623
Then STUFF('180715 051623', 10, 0, ':') puts a ':' at the 10th index, giving 180715 05:1623
Then STUFF('180715 05:1623', 13, 0, ':') puts a ':' at the 13th index, giving 180715 05:16:23
This is then converted into the date '2018-07-15 05:16:23.000' and then used in the the between clause of two other datetimes.

DateAdd( on parameter

Hello I'm currently writing a report based on weekly sales,
I've attained my current figure correctly and that works with my #FirstdayofWeek
and #LastDayOfWeek parameters
and im now trying to replicate it for my Previous week, as you know previous week is -7 days behind
when I run it with this in my where clause
and FirstDayOfWeek = dateadd(day,-7,'2014/06/02')
and LastDayOfWeek = dateadd(day,-7,'2014/06/08')
it works and i get this figure for pre quantity and its correct
BUT when I do this for my parameter
AND dateadd(day,-7,w.FirstDayOfWeek) in (
SELECT Item
FROM DataWarehouse.dbo.ufnSplit(#FirstDayOfWeek, ',')
)
AND dateadd(day,-7,w.LastDayOfWeek) in (
SELECT Item
FROM DataWarehouse.dbo.ufnSplit(#LastDayOfWeek, ',')
)
I get the column headers nothing anywhere.
any ideas?
Here is the code I am using to execute the stored proc:
exec WeeklySalesAndUSW #BD=N'798664',
#CGNo=N'47',
#SCGNo=N'01,02,03,04,05,06,07,08',
#ProductClass=N'1',
#‌​ProductCode=N'1108',
#Region=N'772',
#FirstDayOfWeek = '2014/06/02',
#LastDayOfWeek = '2014/06/08'
Why isnt my parameter passing this through? why does it work if I hard code the date in but when i make it dynamic it gets nothing?
It's probably not working because you reversed the equation.
In the code that works, you are doing this:
and FirstDayOfWeek = dateadd(day,-7,'2014/06/02')
and LastDayOfWeek = dateadd(day,-7,'2014/06/08')
Notice that you are subtracting 7 days from the hard-coded "parameters", and not from the columns in the table.
Now that you are trying to use dynamic parameter values, you are doing the opposite:
AND dateadd(day,-7,w.FirstDayOfWeek) in (
SELECT Item
FROM DataWarehouse.dbo.ufnSplit(#FirstDayOfWeek, ',')
)
AND dateadd(day,-7,w.LastDayOfWeek) in (
SELECT Item
FROM DataWarehouse.dbo.ufnSplit(#LastDayOfWeek, ',')
)
You are subtracting 7 days from the table column, and not the parameter values.
You can correct this by simply removing the minus (-) sign before both of the 7's. This is because adding 7 to the left side of the equation is mathematically the same as subtracting 7 from the right side.

Converting text to Datetime in specific format

I need an expression which will convert a text (VARCHAR) column to a DATETIME if, and only if, it matches dd/MM/yyyy, d/MM/yyyy, dd/M/yyyy or d/M/yyyy. If it doesn't match then I want a NULL.
I have this...
CASE ISDATE([DateField])
WHEN 1 THEN CONVERT(DATETIME,[DateField],103)
ELSE NULL
END
However this fails for '15/04/76' for example - with a "Conversion failed when converting datetime from character string" error - whereas I would want it to return NULL
Example output
'1/6/1976' -> 1976-06-01
'01/06/1976' -> 1976-06-01
'13/06/2001' -> 2001-06-13
'06/13/2001' -> NULL
'13/06/76' -> NULL
Is there a way of forcing ISDATE to validate a given format?
The documentation seems to suggest so...
ISDATE is deterministic only if used with the CONVERT function, the
CONVERT style parameter is specified and style is not equal to 0, 100,
9, or 109.
But ISDATE only takes one argument, so how do I "use it with CONVERT function" if I am not doing so already?
You could do a nested case statement here. The first could check to see if you have a 10 character string 2 for day, 2 for month, 4 for year and 2 for separators = 10 characters.
SET DATEFORMAT DMY;
Case When DateField Like '%/%/[0-9][0-9][0-9][0-9]'
Then Case When IsDate(DateField) = 1
Then CONVERT(DATETIME,[DateField],103)
End
End
Revised: I changed the code to use a like search which forces there to be a /YYYY at the end of the string, and then does an IsDate check to allow for a single day and/or month.
Well, first off, why on earth are you storing datetime values in a varchar column? This is a cardinal sin for a variety of reasons, not the least of which is that you get no validation whatsoever that the data is (or is convertible to) a datetime. You should also consider validating the input, even if you leave the column as varchar, so you don't have such a wide variety of potential formats that you want to consider valid.
So here is one way, borrowing a bit from #G Mastros:
DECLARE #f TABLE(i INT, d VARCHAR(32));
INSERT #f VALUES
(1,'15/04/76'),
(2,'15/04/1976'),
(3,'1/3/1976'),
(4,'1/3/76'),
(5,'15/3/1976'),
(6,'22/22/22'),
(7,'Yesterday');
SET DATEFORMAT DMY;
SELECT i, d, d2 = CASE WHEN ISDATE(d) = 1
AND d LIKE '%/[0-9][0-9][0-9][0-9]'
THEN CONVERT(DATETIME, d, 103) END
FROM #f;
Results:
i d d2
- ---------- -----------------------
1 15/04/76 NULL
2 15/04/1976 1976-04-15 00:00:00.000
3 1/3/1976 1976-03-01 00:00:00.000
4 1/3/76 NULL
5 15/3/1976 1976-03-15 00:00:00.000
6 22/22/22 NULL
7 Yesterday NULL
PS this will be a great case for TRY_CONVERT in SQL Server 2012. It does exactly what you're asking - it tries to convert to the specified data type; if it can't, it returns NULL.
Thanks for the responses folks.
I've done it like this...
CASE ISDATE([DateField]) WHEN 1 THEN
CASE WHEN SUBSTRING([DateField],LEN([DateField])-4,1) = '/' THEN
CASE WHEN CHARINDEX('/',[DateField],LEN([DateField])-3)=0 THEN
CONVERT(datetime, [DateField] , 103)
END
END
END
which is pretty nasty business so would still appreciate something neater!
But this doesn't work either - it still errors on mm/dd/yyyy format dates!
Scrap that last comment - it does seem to work now? Probably something to do with SET DATEFORMAT

Resources