I am making a new column which will became a mathematical expression after replacing the Days/Hours/Minutes with asterisk(*) and Plus(+).
I am using below sql code to make a mathematical expression column :
SELECT replace(replace(replace(VAL,'days','*24 +'),'Hours','*'),'Minutes','/60') as Col from #t)
Example the string ('5 days 12 minutes')will became 5 *24 + 12 /60 after replacing, Now is there any solution that I can evaluate this expression i.e will get result 120 .
One way is to use sp_executesql
DECLARE #testString nvarchar(256);
DECLARE #replacement nvarchar(256);
SET #testString = '5 days 12 minutes';
SET #replacement = (SELECT replace(replace(replace(#testString,'days','*24 +'),'Hours','*'),'Minutes','/60'));
DECLARE #statement nvarchar(256);
SET #statement = N'SELECT ' + #replacement;
EXECUTE sp_executesql #statement;
If you want a set-based approach (usable in VIEWs and functions or directly within a query) you might try something like this:
DECLARE #mockup TABLE(ID INT IDENTITY, SomeString VARCHAR(100));
INSERT INTO #mockup VALUES('5 days 12 minutes')
,('3 days 2 hours 3 minutes');
WITH Splitted AS
(
SELECT m.ID
,m.SomeString
,CAST('<x>' + REPLACE(SomeString,' ','</x><x>') + '</x>' AS XML) AS Casted
FROM #mockup AS m
)
,oddNumbers AS (SELECT Nr FROM (VALUES(1),(3),(5),(7),(9),(11)) AS A(Nr))
,Params AS
(
SELECT s.ID
,s.SomeString
,s.Casted.value('(/x[sql:column("n.Nr")])[1]','decimal(10,4)') AS TheVal
,s.Casted.value('(/x[sql:column("n.Nr")+1])[1]','nvarchar(max)') AS TheUnit
FROM Splitted AS s
CROSS JOIN oddNumbers AS n
WHERE s.Casted.value('(/x[sql:column("n.Nr")])[1]','nvarchar(max)') IS NOT NULL
)
SELECT p.ID
,p.SomeString
,SUM(TheVal * CASE TheUnit WHEN 'days' THEN 24.0
WHEN 'hours' THEN 1.0
WHEN 'Minutes' THEN (1.0/60.0) END) AS Agg
FROM Params AS p
GROUP BY p.ID,p.SomeString;
Assumption: Always number-unit separated by exactly one blank
The query will split your values to get the numbers together with ther associated unit.
This question already has answers here:
Using T-SQL, return nth delimited element from a string
(14 answers)
Closed 6 years ago.
I want to extract a string which has semi-colon as a delimiter. I tried using Substring, Charindex and Left function. But I'm not able to get the desired result. Below is my select statement. Output result must be "Unsure how to perform task. Meter read 10 in office before testing". Thanks
Declare #string Varchar(max)='Sampling:45;Traveling:30;CalibratedNo;uncalibratedReason:: ' +
'Unsure how to perform task. Meter read 10 in office before ' +
'testing.;pH1:6.5;pH2:6.5;Dis.Oxygen1:7.4'
Select SubString(#string, (CHARINDEX('uncalibratedReason:', #string, 0) + 19),
(CharIndex('uncalibratedReason:', LEFT(#string, (LEN(#string) -
(CharIndex(';', #string, 0)))), 0) - 0)) As New
Try it like this:
Declare #string Varchar(max) = 'Sampling:45;Traveling:30;CalibratedNo;uncalibratedReason:Unsure how to perform task. Meter read 10 in office before testing.;pH1:6.5;pH2:6.5;Dis.Oxygen1:7.4';
SELECT CAST('<x>' + REPLACE(#string,';','</x><x>') + '</x>' AS XML).value('x[4]','nvarchar(max)')
The result is:
uncalibratedReason:Unsure how to perform task. Meter read 10 in office before testing.
You can take away the leading uncalibratedReason: simply with SUBSTRING and CHARINDEX looking for : if you need this.
UPDATE
Here is the full code:
DECLARE #result NVARCHAR(MAX)=
(SELECT CAST('<x>' + REPLACE(#string,';','</x><x>') + '</x>' AS XML).value('x[4]','nvarchar(max)'));
SELECT SUBSTRING(#result,CHARINDEX(':',#result)+1,10000)
UPDATE 2: Find position by starting string
DECLARE #result NVARCHAR(MAX)=
(SELECT CAST('<x>' + REPLACE(#string,';','</x><x>') + '</x>' AS XML).value('(x[substring(.,1,string-length("uncalibratedReason:")) eq "uncalibratedReason:"])[1]','nvarchar(max)'));
SELECT SUBSTRING(#result,CHARINDEX(':',#result)+1,10000)
UPDATE 3 The ultimative solution :-)
Declare #string Varchar(max) = 'Sampling:45;Traveling:30;CalibratedNo;uncalibratedReason:Unsure how to perform task. Meter read 10 in office before testing.;pH1:6.5;pH2:6.5;Dis.Oxygen1:7.4';
WITH Casted(ThePart) AS
(
SELECT Node.value('.','nvarchar(max)')
FROM
(
SELECT CAST('<x>' + REPLACE(#string,';','</x><x>') + '</x>' AS XML)
) AS tbl(AsXML)
CROSS APPLY AsXML.nodes('/x') AS The(Node)
)
,Splitted(SpecificPart) AS
(
SELECT CAST('<x>' + REPLACE(ThePart,':','</x><x>') + '</x>' AS XML)
FROM Casted
)
SELECT SpecificPart.value('x[1]','nvarchar(max)') AS Caption
,SpecificPart.value('x[2]','nvarchar(max)') AS Data
FROM Splitted
The result
Caption Data
CalibratedNo NULL
Dis.Oxygen1 7.4
pH1 6.5
pH2 6.5
Sampling 45
Traveling 30
uncalibratedReason Unsure how to perform task. Meter read 10 in office before testing.
Shnugo anwser very cool. (UpVote)
However, this UDF Parser returns the sequence and value
Declare #string Varchar(max)='Sampling:45;Traveling:30;CalibratedNo;uncalibratedReason:: ' +
'Unsure how to perform task. Meter read 10 in office before ' +
'testing.;pH1:6.5;pH2:6.5;Dis.Oxygen1:7.4'
Select * from [dbo].[udf-Str-Parse](#String,';')
--Where Key_PS = 5
--Where Key_Value Like '%:%'
--Where Key_Value Like 'pH1%'
Returns
Key_PS Key_Value
1 Sampling:45
2 Traveling:30
3 CalibratedNo
4 uncalibratedReason:: Unsure how to perform task. Meter read 10 in office before testing.
5 pH1:6.5
6 pH2:6.5
7 Dis.Oxygen1:7.4
The UDF
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimeter varchar(10))
--Usage: Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
-- Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
Returns #ReturnTable Table (Key_PS int IDENTITY(1,1), Key_Value varchar(max))
As
Begin
Declare #XML xml;Set #XML = Cast('<x>' + Replace(#String,#Delimeter,'</x><x>')+'</x>' as XML)
Insert Into #ReturnTable Select ltrim(rtrim(String.value('.', 'varchar(max)'))) FROM #XML.nodes('x') as T(String)
Return
End
Given the below table and data:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
CREATE TABLE #Temp
(
ID INT,
Code INT,
PDescription VARCHAR(2000)
)
INSERT INTO #Temp
(ID,
Code,
PDescription)
VALUES (1,0001,'c and d, together'),
(2,0002,'equals or Exceeds $27.00'),
(3,0003,'Fruit Evaporating Or preserving'),
(4,0004,'Domestics And domestic Maintenance'),
(5,0005,'Bakeries and cracker')
SELECT *
FROM #Temp
DROP TABLE #Temp
Output:
ID Code PDescription
1 1 c and d, together
2 2 equals or Exceeds $27.00
3 3 Fruit Evaporating Or preserving
4 4 Domestics And domestic Maintenance
5 5 Bakeries and cracker
I need a way to achieve the below update to the description field:
ID Code PDescription
1 1 C and D, Together
2 2 Equals or Exceeds $27.00
3 3 Fruit Evaporating or Preserving
4 4 Domestics and Domestic Maintenance
5 5 Bakeries and Cracker
If you fancied going the SQL CLR route the function could look something like
using System.Data.SqlTypes;
using System.Text.RegularExpressions;
public partial class UserDefinedFunctions
{
//One or more "word characters" or apostrophes
private static readonly Regex _regex = new Regex("[\\w']+");
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString ProperCase(SqlString subjectString)
{
string resultString = null;
if (!subjectString.IsNull)
{
resultString = _regex.Replace(subjectString.ToString().ToLowerInvariant(),
(Match match) =>
{
var word = match.Value;
switch (word)
{
case "or":
case "of":
case "and":
return word;
default:
return char.ToUpper(word[0]) + word.Substring(1);
}
});
}
return new SqlString(resultString);
}
}
Doubtless there may be Globalization issues in the above but it should do the job for English text.
You could also investigate TextInfo.ToTitleCase but that still leaves you needing to handle your exceptions.
The following function is not the most elegant of solutions but should do what you want.
ALTER FUNCTION [dbo].[ToProperCase](#textValue AS NVARCHAR(2000))
RETURNS NVARCHAR(2000)
AS
BEGIN
DECLARE #reset BIT;
DECLARE #properCase NVARCHAR(2000);
DECLARE #index INT;
DECLARE #character NCHAR(1);
SELECT #reset = 1, #index=1, #properCase = '';
WHILE (#index <= len(#textValue))
BEGIN
SELECT #character= substring(#textValue,#index,1),
#properCase = #properCase + CASE WHEN #reset=1 THEN UPPER(#character) ELSE LOWER(#character) END,
#reset = CASE WHEN #character LIKE N'[a-zA-Z\'']' THEN 0 ELSE 1 END,
#index = #index +1
END
SET #properCase = N' ' + #properCase + N' ';
SET #properCase = REPLACE(#properCase, N' And ', N' and ');
SET #properCase = REPLACE(#properCase, N' Or ', N' or ');
SET #properCase = REPLACE(#properCase, N' Of ', N' of ');
RETURN RTRIM(LTRIM(#properCase))
END
Example use:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
CREATE TABLE #Temp
(
ID INT,
Code INT,
PDescription VARCHAR(2000)
)
INSERT INTO #Temp
(ID,
Code,
PDescription)
VALUES (1,0001, N'c and d, together and'),
(2,0002, N'equals or Exceeds $27.00'),
(3,0003, N'Fruit Evaporating Or preserving'),
(4,0004, N'Domestics And domestic Maintenance'),
(5,0005, N'Bakeries and cracker')
SELECT ID, Code, dbo.ToProperCase(PDescription) AS [Desc]
FROM #Temp
DROP TABLE #Temp
If you want to convert your text to proper case before inserting into table, then simply call function as follow:
INSERT INTO #Temp
(ID,
Code,
PDescription)
VALUES (1,0001, dbo.ToProperCase( N'c and d, together and')),
(2,0002, dbo.ToProperCase( N'equals or Exceeds $27.00')),
(3,0003, dbo.ToProperCase( N'Fruit Evaporating Or preserving')),
(4,0004, dbo.ToProperCase( N'Domestics And domestic Maintenance')),
(5,0005, dbo.ToProperCase( N'Bakeries and cracker'))
This is a dramatically modified version of my Proper UDF. The good news is you may be able to process the entire data-set in ONE SHOT rather than linear.
Take note of #OverR (override)
Declare #Table table (ID int,Code int,PDescription varchar(150))
Insert into #Table values
(1,1,'c and d, together'),
(2,2,'equals or Exceeds $27.00'),
(3,3,'Fruit Evaporating Or preserving'),
(4,4,'Domestics And domestic Maintenance'),
(5,5,'Bakeries and cracker')
-- Generate Base Mapping Table - Can be an Actual Table
Declare #Pattn table (Key_Value varchar(25));Insert into #Pattn values (' '),('-'),('_'),(','),('.'),('&'),('#'),(' Mc'),(' O''') -- ,(' Mac')
Declare #Alpha table (Key_Value varchar(25));Insert Into #Alpha values ('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('I'),('J'),('K'),('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U'),('V'),('W'),('X'),('Y'),('X')
Declare #OverR table (Key_Value varchar(25));Insert Into #OverR values (' and '),(' or '),(' of ')
Declare #Map Table (MapSeq int,MapFrom varchar(25),MapTo varchar(25))
Insert Into #Map
Select MapSeq=1,MapFrom=A.Key_Value+B.Key_Value,MapTo=A.Key_Value+B.Key_Value From #Pattn A Join #Alpha B on 1=1
Union All
Select MapSeq=99,MapFrom=A.Key_Value,MapTo=A.Key_Value From #OverR A
-- Convert Base Data Into XML
Declare #XML xml
Set #XML = (Select KeyID=ID,String=+' '+lower(PDescription)+' ' from #Table For XML RAW)
-- Convert XML to varchar(max) for Global Search & Replace
Declare #String varchar(max)
Select #String = cast(#XML as varchar(max))
Select #String = Replace(#String,MapFrom,MapTo) From #Map Order by MapSeq
-- Convert Back to XML
Select #XML = cast(#String as XML)
-- Generate Final Results
Select KeyID = t.col.value('#KeyID', 'int')
,NewString = ltrim(rtrim(t.col.value('#String', 'varchar(150)')))
From #XML.nodes('/row') AS t (col)
Order By 1
Returns
KeyID NewString
1 C and D, Together
2 Equals or Exceeds $27.00
3 Fruit Evaporating or Preserving
4 Domestics and Domestic Maintenance
5 Bakeries and Cracker
You don't even need functions and temporary objects. Take a look at this query:
WITH Processor AS
(
SELECT ID, Code, 1 step,
CONVERT(nvarchar(MAX),'') done,
LEFT(PDescription, CHARINDEX(' ', PDescription, 0)-1) process,
SUBSTRING(PDescription, CHARINDEX(' ', PDescription, 0)+1, LEN(PDescription)) waiting
FROM #temp
UNION ALL
SELECT ID, Code, step+1,
done+' '+CASE WHEN process IN ('and', 'or', 'of') THEN LOWER(process) ELSE UPPER(LEFT(process, 1))+LOWER(SUBSTRING(process, 2, LEN(process))) END,
CASE WHEN CHARINDEX(' ', waiting, 0)>0 THEN LEFT(waiting, CHARINDEX(' ', waiting, 0)-1) ELSE waiting END,
CASE WHEN CHARINDEX(' ', waiting, 0)>0 THEN SUBSTRING(waiting, CHARINDEX(' ', waiting, 0)+1, LEN(waiting)) ELSE NULL END FROM Processor
WHERE process IS NOT NULL
)
SELECT ID, Code, done PSDescription FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY step DESC) RowNum FROM Processor
) Ordered
WHERE RowNum=1
ORDER BY ID
It produces desired result as well. You can SELECT * FROM Processor to see all steps executed.
I have a table with a nvarchar column called Custom#5 which has the following data.
row number Custom#5
1 267.5
2 tbc
3
4 34
I want to be able to clean this data up so always returns a numerical value.
row number Custom#5
1 267.5
2 0
3 0
4 34
My current query is;
SELECT CASE
WHEN BomHeaders_1.Custom#5 NOT LIKE '%[^0-9]%'
THEN 0
WHEN BomHeaders_1.Custom#5 IS NULL
THEN 0
ELSE BomHeaders_1.Custom#5
END AS Custom5
FROM [FS25-W2K8\SQLEXPRESS].sagel50_46772.dbo.BomHeaders AS BomHeaders_1
INNER JOIN [FS25-W2K8\SQLEXPRESS].sagel50_46772.dbo.BomComponents AS BomComponents_1 ON BomHeaders_1.ID = BomComponents_1.HeaderID
INNER JOIN [FS25-W2K8\SQLEXPRESS].sagel50_46772.dbo.BomHeaders AS BomHeaders_2 ON BomComponents_1.StockCode = BomHeaders_2.BomReference
INNER JOIN manu_STOCK ON BomHeaders_1.BomReference = manu_STOCK.STOCK_CODE
WHERE (BomComponents_1.StockCode LIKE N'21%')
The current error i'm getting with this is
"Conversion failed when converting the nvarchar value '267.5' to data type int."
If you are using 2012 or greater, I would use the tryparse function
select coalesce(TRY_PARSE ( [Custom#5] AS decimal(18,2)),0)
declare #t table (R INT,C varchar(10))
inSERT INTO #t(R,c)values (1,'267.5'),(2,'tbc'),(3,''),(4,'34')
select R,CASE WHEN C LIKE '%[^a-zA-Z]%' THEN C ELSE CAST(0 AS VARCHAR) END from #t
If your using SQL Server < 2012 and have no option in using try_parse, you can use this:
DECLARE #string nvarchar(255)
SET #string = 'Hali891236.5€hHalo'
SELECT Substring(
#string,
PATINDEX('%[0-9.]%',#string),
PATINDEX('%[^0-9.]%',
Substring(
#string,
PATINDEX(
'%[0-9.]%',
#string
),
LEN(#string)
)
)-1
)
GO