Related
I have a string made up of numbers. The length of the string ranges anywhere from 1 character to 9 characters.
I want to insert a dash (-) every three characters from the right. This will only be relevant for strings with upwards of three characters.
This will just be a select statement, as I do not want to actually update the string itself.
For example,
8 should return 8
476 should return 476
4767 should return 4-767
45907392 should return 45-907-392
845907392 should return 845-907-392
This should work:
select
*,
replace(format(n,'#,#'),',','-')
from (values (8),(476),(4767),(45907392),(845907392)) x (n)
Change , to . if it's thousand separator in your system, or provide a culture as third parameter in FORMAT function so it always works the same.
Since SQL Server 2012 you can use the FORMAT function as described in the other answers. In case you want to format a string value like 'abcde' too you can use only string functions like this:
DECLARE #str VARCHAR(100) = '845907392';
-- 845-907-392
SELECT REVERSE(SUBSTRING(REVERSE(#str), 7, 3))
+ CASE WHEN LEN(#str)>6 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 4, 3))
+ CASE WHEN LEN(#str)>3 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 1, 3))
You can also create a function:
CREATE FUNCTION dbo.GetFormatTripleDash (#str varchar(255))
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #retStr VARCHAR(255) = REVERSE(SUBSTRING(REVERSE(#str), 7, 3))
+ CASE WHEN LEN(#str)>6 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 4, 3))
+ CASE WHEN LEN(#str)>3 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 1, 3))
RETURN(#retStr)
END
You can use this funtion like this:
-- 845-907-392
SELECT dbo.GetFormatTripleDash('845907392')
A more flexible solution using the function. Now you can use a much longer string value and you can define the part size separated by - character.
CREATE FUNCTION dbo.GetFormatDash (#str varchar(255), #partSize INT = 3)
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #startSize INT = 0;
DECLARE #retStr VARCHAR(255) = '';
WHILE #startSize < LEN(#str)
BEGIN
SET #retStr = REVERSE(SUBSTRING(REVERSE(#str), #startSize + 1, #partSize)) + CASE WHEN #startSize > 0 THEN '-' ELSE '' END + #retStr;
SET #startSize = #startSize + #partSize;
END
RETURN(#retStr)
END
You can use this improved function like this:
-- 12-345-678-901-234-567-890
SELECT dbo.GetFormatDash('12345678901234567890', DEFAULT)
SELECT dbo.GetFormatDash('12345678901234567890', 3)
-- 12345-67890-12345-67890
SELECT dbo.GetFormatDash('12345678901234567890', 5)
demo on dbfiddle.uk
Its a bit gross but it works! Try i
t and let me know if you agree
SELECT
CASE
WHEN LEN(yourColumn)>6 THEN format(CAST(YourColumn AS NUMERIC), '###-###-###')
WHEN LEN(YourColumn)>3 THEN format(CAST(YourColumn AS NUMERIC), '###-###')
ELSE YourColumn
END
Here is another way to do it, albeit more typing
SELECT
CASE
WHEN LEN(yourColumn)>6 THEN SUBSTRING(YourColumn, 1, 3) +'-'+ SUBSTRING(YourColumn, 4, 3)
WHEN LEN(YourColumn)>3 THEN REVERSE(SUBSTRING(REVERSE(YourColumn), 1, 3) +'-'+ SUBSTRING(REVERSE(YourColumn), 4, 3))
ELSE YourColumn
END
Try this. I'm sure it can also be extended to allow for any number of characters with a bit of effort
declare #input nvarchar(100) = '845907392'
declare #separator char(1) = '-'
--option1 - CTE
;with dash1 as (
select isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input)) as v
)
, dash2 as(
select isnull(stuff(v, 1+7, 0, #separator), v) as v from dash1
)
select reverse(v) from dash2
--option2 - Non CTE
select reverse(isnull(stuff(isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input)), 1+7, 0, #separator), isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input))))
Try this using format() function.
Select Replace(format(8, '#,##0'), ',', '-')
Select Replace(format(476, '#,##0'), ',', '-')
Select Replace(format(45907392, '#,##0'), ',', '-')
Select Replace(format(845907392, '#,##0'), ',', '-')
Live db<>fiddle demo.
A solution with a recursive CTE:
with cte as (
select col, len(col) - 3 pos from tablename
union all
select
cast(left(col, pos) + '-' + right(col, len(col) - pos) as varchar(100)),
pos - 3
from cte
where pos > 0
)
select col from cte
where pos <= 0
See the demo.
Results:
> | col |
> | :---------- |
> | 8 |
> | 476 |
> | 845-907-392 |
> | 45-907-392 |
> | 4-767 |
I want to extract the four digit number between the last "-" and last "," into its own column. And then I want to extract the month and year into two more columns.
Below is an example of the data I get from the column that I want to push out into 3 additional columns:
"Captured Credit Card: Visa, xxxx-xxxx-xxxx-1234, 12/20. Set as Default."
I want the output to be:
CC: 1234
Month: 12
Year: 20
Any help with this is appreciated.
I tried this using REVERSE() to flip the string then used CHARINDEX() to find the char location from the end of the string.
Then, using LEN() and a subtraction, the position of the last char is where it would be located in the original (un-flipped) string.
DECLARE #temp varchar(1000) = 'Captured Credit Card: Visa, xxxx-xxxx-xxxx-1234, 12/20. Set as Default.'
SELECT 'CC: '
+ SUBSTRING(#temp
, LEN(#temp) - CHARINDEX('-', REVERSE(#temp), 0) + 2
, LEN(#temp) - CHARINDEX(',', REVERSE(#temp), 0) --find the last ','
-(LEN(#temp) - CHARINDEX('-', REVERSE(#temp), 0)) --find the last '-')
-1
)
+ ' Month: '
+ SUBSTRING(#temp, CHARINDEX('/', #temp, 0) -2, 2)
+ ' Year: '
+ SUBSTRING(#temp, CHARINDEX('/', #temp, 0) + 1, 2)
AS [output]
I had to add or subtract 1 or 2 for the correct index used in the functions.
Produces Output:
output
------
CC: 1234 Month: 12 Year: 20
If your string is going to be in a consistent format as above then try below.
DECLARE #string VARCHAR(MAX) = 'Captured Credit Card: Visa, xxxx-xxxx-xxxx-1234, 12/20. Set as Default.'
SELECT Column1 = 'CC:' + RIGHT(REPLACE(#string, RIGHT(#string, CHARINDEX(',', REVERSE(#string))),''), CHARINDEX('-', REVERSE(REPLACE(#string, RIGHT(#string, CHARINDEX(',', REVERSE(#string))),''))) -1),
Column2 = 'Month: ' + SUBSTRING(#string,CHARINDEX('/', #string)-2,2),
Column3 = 'Year: ' + SUBSTRING(#string,CHARINDEX('/', #string)+1,2)
I have a query on SQL Server that's finding wrong results based on a datetime condition.
SET LANGUAGE Português
select distinct
CONVERT(varchar(10), DATENAME(MONTH, i9_parcelaBase.i9_data_vencimento)) + '/' + CONVERT(varchar(10),
DATENAME(YEAR,i9_parcelaBase.i9_data_vencimento)) AS Periodo,
i9_parcelaBase.i9_data_vencimento,
i9_parcelaBase.i9_data_limite_envio_nf,
i9_parcelaBase.i9_status_atual,
i9_processo_statusBase.i9_status,
i9_processo_statusBase.i9_name
--i9_nota_fiscalBase.i9_nota_fiscalid as ID
from i9_parcelaBase
left join i9_processo_statusBase
on i9_parcelaBase.i9_status_atual = i9_processo_statusBase.i9_processo_statusId
where i9_processo_statusBase.i9_entidade = 100000002
and i9_parcelaBase.i9_data_vencimento between CONVERT(varchar(10), year(getdate())-1) + '-' + CONVERT(varchar(10), month(getdate())+1) + '-1'
and CONVERT(varchar(10), year(getdate())) + '-' + CONVERT(varchar(10), month(getdate())) + '-1'
order by i9_data_vencimento asc
Basically, Month that's receiving data without '0' before (when necessary).
Sample:
Actual return: '2017-6-1'
Expected return '2017-06-01'
Can anyone help me to solve this problem?
By the look of your query, i9_data_vencimento is expected to end with just -1, not -01. You can cast the string into a date, then recast it back as a string as a way to format it:
select distinct
CONVERT(varchar(10), DATENAME(MONTH, i9_parcelaBase.i9_data_vencimento)) + '/' + CONVERT(varchar(10),
DATENAME(YEAR,i9_parcelaBase.i9_data_vencimento)) AS Periodo,
CONVERT( VARCHAR(10), CONVERT( DATETIME, i9_parcelaBase.i9_data_vencimento ), 120 ) AS i9_data_vencimento,
:
:
Also, considering that i9_data_vencimento is just a string, and assuming it always ends with '1' in the format 'yyyy-mm-1', you could use a LEFT function to strip off the last character and replace it with '01':
SELECT DISTINCT
:
LEFT(i9_parcelaBase.i9_data_vencimento, 8) + '01' AS i9_data_vencimento,
:
I have a table and it has a 3 columns. The first column is the data that contains value(numeric) and unit(percentage and etc..), the second column is numeric column, the third is Unit column. What I want to do is split the numeric and the unit from the first column then put those split-ted data to its designated column.
Here is my table:
I tried this function:SO link here..., it really does splitting alpha and numeric but then I'm new in using SQL Function, my problem there is the parameter must be in string STRING, so what I did is change it to Sub Query but it gives me error.
Sample COde:
SQL FUNCTION:
create function [dbo].[GetNumbersFromText](#String varchar(2000))
returns table as return
(
with C as
(
select cast(substring(S.Value, S1.Pos, S2.L) as int) as Number,
stuff(s.Value, 1, S1.Pos + S2.L, '') as Value
from (select #String+' ') as S(Value)
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
union all
select cast(substring(S.Value, S1.Pos, S2.L) as int),
stuff(S.Value, 1, S1.Pos + S2.L, '')
from C as S
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
where patindex('%[0-9]%', S.Value) > 0
)
select Number
from C
)
SELECT STATEMENT with SUB Query:
declare #S varchar(max)
select number from GetNumbersFromText(Select SomeColm From Table_Name) option (maxrecursion 0)
BTW, im using sql server 2005.
Thanks!
If the numeric part is always at the beginning, then you can use this:
PATINDEX('%[0-9][^0-9]%', ConcUnit)
to get the index of the last digit.
Thus, this:
DECLARE #str VARCHAR(MAX) = '4000 ug/ML'
SELECT LEFT(#str, PATINDEX('%[0-9][^0-9]%', #str )) AS Number,
LTRIM(RIGHT(#str, LEN(#str) - PATINDEX('%[0-9][^0-9]%', #str ))) As Unit
gives you:
Number Unit
-------------
4000 ug/ML
EDIT:
If numeric data include double values as well, then you can use this:
SELECT LEN(#str) - PATINDEX ('%[^0-9][0-9]%', REVERSE(#str))
to get the index of the last digit.
Thus, this:
SELECT LEFT(#str, LEN(#str) - PATINDEX ('%[^0-9][0-9]%', REVERSE(#str)))
gives you the numeric part.
And this:
SELECT LEFT(#str, LEN(#str) - PATINDEX ('%[^0-9][0-9]%', REVERSE(#str))) AS Numeric,
CASE
WHEN CHARINDEX ('%', #str) <> 0 THEN LTRIM(RIGHT(#str, LEN(#str) - CHARINDEX ('%', #str)))
ELSE LTRIM(RIGHT(#str, PATINDEX ('%[^0-9][0-9]%', REVERSE(#str))))
END AS Unit
gives you both numberic and unit part.
Here are some tests that I made with the data you have posted:
Input:
DECLARE #str VARCHAR(MAX) = '50 000ug/ML'
Output:
Numeric Unit
------------
50 000 ug/ML
Input:
DECLARE #str VARCHAR(MAX) = '99.5%'
Output:
Numeric Unit
------------
99.5
Input:
DECLARE #str VARCHAR(MAX) = '4000 . 35 % ug/ML'
Output:
Numeric Unit
------------------
4000 . 35 ug/ML
Here is my answer. Check output in SQLFiddle for the same.
create TABLE temp
(
string NVARCHAR(50)
)
INSERT INTO temp (string)
VALUES
('4000 ug\ml'),
('2000 ug\ml'),
('%'),
('ug\ml')
SELECT subsrtunit,LEFT(subsrtnumeric, PATINDEX('%[^0-9]%', subsrtnumeric+'t') - 1)
FROM (
SELECT subsrtunit = SUBSTRING(string, posofchar, LEN(string)),
subsrtnumeric = SUBSTRING(string, posofnumber, LEN(string))
FROM (
SELECT string, posofchar = PATINDEX('%[^0-9]%', string),
posofnumber = PATINDEX('%[0-9]%', string)
FROM temp
) d
) t
Updated Version to handle 99.5 ug\ml
create TABLE temp
(
string NVARCHAR(50)
)
INSERT INTO temp (string)
VALUES
('4000 ug\ml'),
('2000 ug\ml'),
('%'),
('ug\ml'),
('99.5 ug\ml')
SELECT subsrtunit,LEFT(subsrtnumeric, PATINDEX('%[^0-9.]%', subsrtnumeric+'t') - 1)
FROM (
SELECT subsrtunit = SUBSTRING(string, posofchar, LEN(string)),
subsrtnumeric = SUBSTRING(string, posofnumber, LEN(string))
FROM (
SELECT string, posofchar = PATINDEX('%[^0-9.]%', string),
posofnumber = PATINDEX('%[0-9.]%', string)
FROM temp
) d
) t
Updated Version: To handle 1 000 ug\ml,20 000ug\ml
create TABLE temp
(
string NVARCHAR(50)
)
INSERT INTO temp (string)
VALUES
('4000 ug\ml'),
('2000 ug\ml'),
('%'),
('ug\ml'),
('99.5 ug\ml'),
('1 000 ug\ml'),
('20 000ug\ml')
SELECT substring(replace(subsrtunit,' ',''),PATINDEX('%[0-9.]%', replace(subsrtunit,' ',''))+1,len(subsrtunit)),
LEFT(replace(subsrtnumeric,' ',''), PATINDEX('%[^0-9.]%', replace(subsrtnumeric,' ','')+'t') - 1)
FROM (
SELECT subsrtunit = SUBSTRING(string, posofchar, LEN(string)),
subsrtnumeric = SUBSTRING(string, posofnumber, LEN(string))
FROM (
SELECT string, posofchar = PATINDEX('%[^0-9.]%', replace(string,' ','')),
posofnumber = PATINDEX('%[0-9.]%', replace(string,' ',''))
FROM temp
) d
) t
Check out SQLFiddle for the same.
Would something like this work? Based on the shown data it looks like it would.
Apply it to your data set as a select and if you like the results then you can make an update from it.
WITH cte as (SELECT 'ug/mL' ConcUnit, 500 as [Numeric], '' as Unit
UNION ALL SELECT '2000 ug/mL', NULL, '')
SELECT
[ConcUnit] as [ConcUnit],
[Numeric] as [Original Numeric],
[Unit] as [Original Unit],
CASE WHEN ConcUnit LIKE '% %' THEN
SUBSTRING(ConcUnit, 1, CHARINDEX(' ', ConcUnit) - 1)
ELSE [Numeric] END as [New Numeric],
CASE WHEN ConcUnit LIKE '% %'
THEN SUBSTRING(ConcUnit, CHARINDEX(' ', ConcUnit) + 1, LEN(ConcUnit))
ELSE ConcUnit END as [New Unit]
FROM cte
change #concunit & #unitx Respectively
DECLARE #concunit varchar(10)='45.5%'
DECLARE #unitx varchar(10)='%'
BEGIN
SELECT RTRIM(SUBSTRING( #concunit , 1 , CHARINDEX( #unitx , #concunit
) - 1
)) AS Number,
RTRIM(SUBSTRING( #concunit , CHARINDEX( #unitx , #concunit
) , LEN( #concunit
) - (CHARINDEX( #unitx , #concunit
) - 1)
)) AS Unit
end
I had the same dilemma, but in my case the alpha's were in front of the numerics.
So using the logic that #Giorgos Betsos added to his answer, I just reversed it.
I.e., when your input is :
abc123
You can split it like this:
declare #input varchar(30) = 'abc123'
select
replace(#input,reverse(LEFT(reverse(#input), PATINDEX('%[0-9][^0-9]%', reverse(#input) ))),'') Alpha
, reverse(LEFT(reverse(#input), PATINDEX('%[0-9][^0-9]%', reverse(#input) ))) Numeric
Results :
Every year I have to update my company's financial reports to include the new financial year (as the year isn't coterminus with the calendar year), so I do.....
Case
when ST_date >= '1996.11.01 00:00:00' and st_date < '1997.11.01 00:00:00'
then '96-97'
[etc]
end as year,
Every year I have to remember which reports I need to amend - most years I forget one!
...Is there a simple dynamic way to determine this?
You could definitely write a simple stored function in SQL Server to determine the financial year based on the date:
CREATE FUNCTION dbo.GetFinancialYear (#input DATETIME)
RETURNS VARCHAR(20)
AS BEGIN
DECLARE #FinYear VARCHAR(20)
SET #FinYear =
CASE
WHEN #INPUT >= '19961101' AND #input < '19971101' THEN '96-97'
WHEN #INPUT >= '19971101' AND #input < '19981101' THEN '97-98'
ELSE '(other)'
END
RETURN #FinYear
END
and then just use that in all your queries.
SELECT
somedate, dbo.GetFinancialYear(somedate)
......
If you need to add a new financial year - just update the one function, and you're done !
Update: if you want to make this totally dynamic, and you can rely on the fact that the financial year always starts on Nov 1 - then use this approach instead:
CREATE FUNCTION dbo.GetFinancialYear (#input DATETIME)
RETURNS VARCHAR(20)
AS BEGIN
DECLARE #FinYear VARCHAR(20)
DECLARE #YearOfDate INT
IF (MONTH(#input) >= 11)
SET #YearOfDate = YEAR(#input)
ELSE
SET #YearOfDate = YEAR(#input) - 1
SET #FinYear = RIGHT(CAST(#YearOfDate AS CHAR(4)), 2) + '-' + RIGHT(CAST((#YearOfDate + 1) AS CHAR(4)), 2)
RETURN #FinYear
END
This will return:
05/06 for a date such as 2005-11-25
04/05 for a date such as 2005-07-25
Have a look at this example:
declare #ST_Date datetime = '20120506'
SELECT
convert(char(2),DateAdd(m,-10,#ST_DATE),2)+'-'+
convert(char(2),DateAdd(m,+ 2,#ST_DATE),2) as year
As a column expression:
convert(char(2),DateAdd(m,-10,ST_DATE),2)+'-'+
convert(char(2),DateAdd(m,+ 2,ST_DATE),2) as year
Pretty trivial!
The way I handle these problems (financial year, pay period etc) is to recognize the fact that financial years are the same as any year, except they start X months later. The straightforward solution is therefore to shift the FY by the number of months back to the calendar year, from which to do any "annual" comparisons or derivation of "year" (or "month").
Declare #FinancialMonth Varchar(100)=NULL,#Month smallint,#Date DateTime='04/06/2013'
BEGIN TRY
SELECT #FinancialMonth='01-'+IsNULL(#FinancialMonth,'April')+'-'+Cast(year(getdate()) as varchar)
SELECT #Month=(Month(Cast(#FinancialMonth as datetime))-1) * -1
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,'Invalid Financial Month' ErrorMessage
END CATCH
SELECT Month((CONVERT([varchar](10),dateadd(month,(#Month),#Date),(101)))) FinancialMonth,
Year((CONVERT([varchar](10),dateadd(month,(#Month),#Date),(101)))) FinancialYear
,DatePart(qq,(CONVERT([varchar](10),dateadd(month,(#Month),#Date),(101)))) FinancialQuarter
This one works for me and sets it as the actual FY end date.
SET #enddatefy = convert(DATE, str(datepart(yyyy,DateAdd(m,-6,#enddate))+1)+'0630',112)
SET #enddatefyid = str(datepart(yyyy,DateAdd(m,-6,#enddate))+1)+'0630'
datename(YEAR, DATEADD(M,-3,Date)) +'-'+ cast((datepart(YEAR, DATEADD(M,-3,Date)) + 1) %100 as varchar(2))
Calculate on Column 'Date'
Financial year ranges from 1st April to 31st March
Create FUNCTION dbo.GetFinancialYear (#input DATETIME)
RETURNS VARCHAR(20)
AS BEGIN
DECLARE #FinYear VARCHAR(20)
IF (MONTH(#input) > 3)
SET #FinYear = RIGHT(CAST(Year(#input) AS CHAR(4)), 4) + '-' + RIGHT(CAST((Year(#input) + 1) AS CHAR(4)), 2)
ELSE
SET #FinYear = RIGHT(CAST((Year(#input) - 1) AS CHAR(4)), 4) + '-' + RIGHT(CAST(Year(#input) AS CHAR(4)), 2)
RETURN #FinYear
END
Declare #date1 datetime = '2017-07-01'
Select Case
When Month(#date1)>=7 Then 'FY'+Convert(NVARCHAR(10),(Right(year(getdate()),2)+1))
Else 'FY'+Convert(NVARCHAR(10),(Right(year(getdate()),2)))
End
This works for me, where the financial year starts in July.
CASE WHEN DatePart(mm, [YourDate]) >= 7
THEN convert(varchar(10), YEAR([YourDate])) +' / '+ Convert(varchar(10), YEAR([YourDate]) + 1 )
ELSE Convert(varchar(10), YEAR([YourDate]) - 1) +' / '+ Convert(varchar(10), YEAR([YourDate]) )
END AS [Financial Year],