I have column 'GUI_KVLevelName'.
It has data as :
500.00/69.00/34.50
500.00/400.00/138.00
500.00/69.00
500.00/400.00
500.00/345.00/34.50
57.00/8.30
I want to use order by with this. It is a varchar column but i want to order it as numeric. So how could i use order by this column?
Here is a little cheat that may work
By removing the decimal points, we are converting the individual values into a larger INT. This is then converted to and then sorted via a hierarchyid type
Example
Declare #YourTable Table ([GUI_KVLevelName] varchar(50))
Insert Into #YourTable Values
('500.00/69.00/34.50')
,('500.00/400.00/138.00')
,('500.00/69.00')
,('500.00/400.00')
,('500.00/345.00/34.50')
,('0.45/5.30') -- Added for leading zero
,('0.10/9.30') -- Added for leading zero
Select *
From #YourTable
Order By try_convert(hierarchyid,replace('/'+replace([GUI_KVLevelName],'.','1')+'/','/0','/'))
Returns
GUI_KVLevelName
0.10/9.30
0.45/5.30
500.00/69.00
500.00/69.00/34.50
500.00/345.00/34.50
500.00/400.00
500.00/400.00/138.00
Second answer in case you can't GTD two decimal places.
Example
Declare #YourTable Table ([GUI_KVLevelName] varchar(50))
Insert Into #YourTable Values
('500.00/69.00/34.50')
,('500.00/400.00/138.00')
,('500.00/69.00')
,('500.00/400.00')
,('500.00/345.00/34.50')
,('0.45/5.30')
,('0.1/9.30')
,('0.01/9.30')
,('0.05/9.30')
,('1.3/4.30')
Select A.*
From #YourTable A
Cross Apply (
Select Pos1 = xDim.value('/x[1]','money')
,Pos2 = xDim.value('/x[2]','money')
,Pos3 = xDim.value('/x[3]','money')
,Pos4 = xDim.value('/x[4]','money')
,Pos5 = xDim.value('/x[5]','money')
,Pos6 = xDim.value('/x[6]','money')
,Pos7 = xDim.value('/x[7]','money')
From (Select Cast('<x>' + replace([GUI_KVLevelName],'/','</x><x>')+'</x>' as xml) as xDim) as A
) B
Order By Pos1,Pos2,Pos3,Pos4,Pos5,Pos6,Pos7
Returns
GUI_KVLevelName
0.01/9.30
0.05/9.30
0.1/9.30
0.45/5.30
1.3/4.30
500.00/69.00
500.00/69.00/34.50
500.00/345.00/34.50
500.00/400.00
500.00/400.00/138.00
Related
I am trying to create a bit complex stored procedure in ms sql database. My main focus is take a input of long string text then split the data by characters and update that data where matching. In detail: DataString is the input long text string which will contain value like bellow example.
'-' is separator between name of data type and its value
',' is separator between two different kind of value
':' is separator between two set of data, each set of data separates by :
Now can u tell me how can i grab each data from long string and insert them where matching? Ask question if you still need to know something. Thanks in advance
Example of long text string:
ASIN-NsQf8,type-0,Price-7,IsPrime-1:ASIN-fD5tsQ,type-1,Price-13,IsPrime-0:ASIN-tvQtsu,type-1,Price-14,IsPrime-1
The Unfinished SQL Code:
CREATE PROCEDURE dbo.lk_UpdateMatchingDataOfThirdparty
#DataString VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
UPDATE ThirdPartyData SET Price = #value_get_from_string, IsPrime = #value_get_from_string, DateChecked = GETDATE()
WHERE ASIN = '#value_get_from_string' AND type = '#value_get_from_string';
END
GO
Unfortunately string_split() does NOT return a sequence number. So if you are open to a an alternative split/parse function which is also performant.
Note: I made assumptions with the try_convert() portion
Example
Declare #DataString varchar(max) = 'ASIN-NsQf8,type-0,Price-7,IsPrime-1:ASIN-fD5tsQ,type-1,Price-13,IsPrime-0:ASIN-tvQtsu,type-1,Price-14,IsPrime-1'
UPDATE A
SET Price = B.Price
, IsPrime = B.IsPrime
, DateChecked = GETDATE()
From ThirdPartyData A
Join (
Select [ASIN] =replace(max(case when B.RetVal Like 'ASIN-%' then B.RetVal end),'ASIN-','')
,[Type] =try_convert(int,replace(max(case when B.RetVal Like 'type-%' then B.RetVal end),'type-',''))
,[Price] =try_convert(money,replace(max(case when B.RetVal Like 'price-%' then B.RetVal end),'price-',''))
,[IsPrime]=try_convert(bit,replace(max(case when B.RetVal Like 'IsPrime-%' then B.RetVal end),'IsPrime-',''))
From [dbo].[tvf-Str-Parse](#DataString,':') A
Cross Apply [dbo].[tvf-Str-Parse](A.RetVal,',') B
Group By A.RetSeq
) B
on A.[ASIN]=B.[ASIN] and A.[Type]=B.[Type]
If it Helps with the Visualization, the sub-query Returns
ASIN Type Price IsPrime
tvQtsu 1 14.00 1
NsQf8 0 7.00 1
fD5tsQ 1 13.00 0
The TVF if Interested
CREATE FUNCTION [dbo].[tvf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
EDIT: If you want the 2016+ string_split() approach
Select [ASIN] =replace(max(case when Value Like 'ASIN-%' then Value end),'ASIN-','')
,[Type] =try_convert(int,replace(max(case when Value Like 'type-%' then Value end),'type-',''))
,[Price] =try_convert(money,replace(max(case when Value Like 'price-%' then Value end),'price-',''))
,[IsPrime]=try_convert(bit,replace(max(case when Value Like 'IsPrime-%' then Value end),'IsPrime-',''))
From (
Select A.RN
,B.*
From ( Select RN=Row_Number() over (Order by (select null)),* from string_split(#DataString,':') ) A
Cross Apply string_split(A.Value,',') B
) A
Group By RN
So, my first post is less a question and more a statement! Sorry.
I needed to convert delimited strings stored in VarChar table columns to multiple/separate columns for the same record. (It's COTS software; so please don't bother telling me how the table is designed wrong.) After searching the internet ad nauseum for how to create a generic single line call to do that - and finding lots of how not to do that - I created my own. (The name is not real creative.)
Returns: A table with sequentially numbered/named columns starting with [Col1]. If an input value is not provided, then an empty string is returned. If less than 32 values are provided, all past the last value are returned as null. If more than 32 values are provided, they are ignored.
Prerequisites: A Number/Tally Table (luckily, our database already contained 'dbo.numbers').
Assumptions: Not more than 32 delimited values. (If you need more, change "WHERE tNumbers.Number BETWEEN 1 AND XXX", and add more prenamed columns ",[Col33]...,[ColXXX]".)
Issues: The very first column always gets populated, even if #InputString is NULL.
--======================================================================
--SMOZISEK 2017/09 CREATED
--======================================================================
CREATE FUNCTION dbo.fStringToPivotTable
(#InputString VARCHAR(8000)
,#Delimiter VARCHAR(30) = ','
)
RETURNS TABLE AS RETURN
WITH cteElements AS (
SELECT ElementNumber = ROW_NUMBER() OVER(PARTITION BY #InputString ORDER BY (SELECT 0))
,ElementValue = NodeList.NodeElement.value('.','VARCHAR(1022)')
FROM (SELECT TRY_CONVERT(XML,CONCAT('<X>',REPLACE(#InputString,#Delimiter,'</X><X>'),'</X>')) AS InputXML) AS InputTable
CROSS APPLY InputTable.InputXML.nodes('/X') AS NodeList(NodeElement)
)
SELECT PivotTable.*
FROM (
SELECT ColumnName = CONCAT('Col',tNumbers.Number)
,ColumnValue = tElements.ElementValue
FROM DBO.NUMBERS AS tNumbers --DEPENDENT ON ANY EXISTING NUMBER/TALLY TABLE!!!
LEFT JOIN cteElements AS tElements
ON tNumbers.Number = tElements.ElementNumber
WHERE tNumbers.Number BETWEEN 1 AND 32
) AS XmlSource
PIVOT (
MAX(ColumnValue)
FOR ColumnName
IN ([Col1] ,[Col2] ,[Col3] ,[Col4] ,[Col5] ,[Col6] ,[Col7] ,[Col8]
,[Col9] ,[Col10],[Col11],[Col12],[Col13],[Col14],[Col15],[Col16]
,[Col17],[Col18],[Col19],[Col20],[Col21],[Col22],[Col23],[Col24]
,[Col25],[Col26],[Col27],[Col28],[Col29],[Col30],[Col31],[Col32]
)
) AS PivotTable
;
GO
Test:
SELECT *
FROM dbo.fStringToPivotTable ('|Height|Weight||Length|Width||Color|Shade||Up|Down||Top|Bottom||Red|Blue|','|') ;
Usage:
SELECT 1 AS ID,'Title^FirstName^MiddleName^LastName^Suffix' AS Name
INTO #TempTable
UNION SELECT 2,'Mr.^Scott^A.^Mozisek^Sr.'
UNION SELECT 3,'Ms.^Jane^Q.^Doe^'
UNION SELECT 5,NULL
UNION SELECT 7,'^Betsy^^Ross^'
;
SELECT SourceTable.*
,ChildTable.Col1 AS ColTitle
,ChildTable.Col2 AS ColFirst
,ChildTable.Col3 AS ColMiddle
,ChildTable.Col4 AS ColLast
,ChildTable.Col5 AS ColSuffix
FROM #TempTable AS SourceTable
OUTER APPLY dbo.fStringToPivotTable(SourceTable.Name,'^') AS ChildTable
;
No, I have not tested any plan (I just needed it to work).
Oh, yeah: SQL Server 2012 (12.0 SP2)
Comments? Corrections? Enhancements?
Here is my TVF. Easy to expand up to the 32 (the pattern is pretty clear).
This is a straight XML without the cost of the PIVOT.
Example - Notice the OUTER APPLY --- Use CROSS APPLY to Exclude NULLs
Select A.ID
,B.*
From #TempTable A
Outer Apply [dbo].[tvf-Str-Parse-Row](A.Name,'^') B
Returns
The UDF if Interested
CREATE FUNCTION [dbo].[tvf-Str-Parse-Row] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
From (Select Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A
Where #String is not null
)
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[tvf-Str-Parse-Row]('Dog,Cat,House,Car',',')
--Select * from [dbo].[tvf-Str-Parse-Row]('John <test> Cappelletti',' ')
How to get the string between 5th and 6th comma, without using a function, because I am selecting other columns as well, so using a function will not help me
This is the string value
"RBC,Dev,PROM0006581,T85230,P0263240,**Dev**,CHG0048754_DYN_DIF,Code changes as part of DYN -Tiered/Scaled & Bonus Interest for DIF Products
"
can someone please help me
You can do following:
DECLARE #Values VARCHAR(MAX) = 'RBC,Dev,PROM0006581,T85230,P0263240,**Dev**,CHG0048754_DYN_DIF,Code changes as part of DYN -Tiered/Scaled & Bonus Interest for DIF Products'
IF OBJECT_ID('TEMPDB..#Values') IS NOT NULL DROP TABLE #Values;
CREATE TABLE #Values (ID INT IDENTITY(1,1),Item VARCHAR(100));
DECLARE #Insert VARCHAR(MAX) = 'INSERT INTO #Values VALUES ('''+REPLACE(#Values,',','''),(''')+''');';
EXEC (#Insert);
SELECT * FROM #Values
Resulting:
And
SELECT Item FROM #Values WHERE ID=5
Resulting
This should get you what you're looking for...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #testData;
CREATE TABLE #TestData (
String VARCHAR(1000) NOT NULL
);
INSERT #TestData (String) VALUES ('RBC,Dev,PROM0006581,T85230,P0263240,**Dev**,CHG0048754_DYN_DIF,Code changes as part of DYN -Tiered/Scaled & Bonus Interest for DIF Product');
SELECT *,
RequestedValue = SUBSTRING(td.String, c4.Comma + 1, ISNULL(c5.Comma - c4.Comma - 1, 1000))
FROM
#TestData td
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, 1), 0)) ) c1 (Comma)
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, c1.Comma + 1), 0)) ) c2 (Comma)
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, c2.Comma + 1), 0)) ) c3 (Comma)
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, c3.Comma + 1), 0)) ) c4 (Comma)
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, c4.Comma + 1), 0)) ) c5 (Comma);
HTH, Jason
Here is a simple in-line and XML Safe approach (not just &'s).
Grabbing the 5th value. Not 100% sure if this needs to be adjusted up or down
Sample Data
Declare #YourTable table (ID int,SomeColumn varchar(max))
Insert Into #YourTable values
(1,'RBC,Dev,PROM0006581,T85230,P0263240,**Dev**,CHG0048754_DYN_DIF,Code changes as part of DYN -Tiered/Scaled & Bonus Interest for DIF Products')
The Query
Select ID
,SomeValue = Cast('<x>' + replace((Select replace(SomeColumn,',','|||') as [*] For XML Path('')),'|||','</x><x>')+'</x>' as xml).query('.').value('/x[5]','varchar(max)')
From #YourTable
Returns
ID SomeValue
1 P0263240
There is one solution that involve replacing comma , with xml tags and thereby converting column into an XML datatype.
Below query will give you 6th item (i.e between 5th and 6th comma as you want)
select *
, Column2 = convert(XML,'<s>' + REPLACE(REPLACE(MyColumn,'&','&'),',','</s><s>') + '</s>').value('/s[6]','varchar(200)')
FROM [dbo].[Table1]
SQL Fiddle Demo
But you have to escape any reserved XML character in your data by replacing it with entity references otherwise XML cast will fail. like & is replaced with & in the above query
XML reserved character on Technet
Updated Query
As #John mentioned in his answer FOR XML Path is a neat and elegant way to escape special XML characters.So my updated query would be.
SELECT *
, Column2 = convert(XML,'<s>' + REPLACE((SELECT ISNULL(MyColumn,'') FOR XML Path('')),',','</s><s>') + '</s>').value('/s[6]','varchar(200)')
FROM [dbo].[Table1]
Write a table-valued function that accepts the string and a delimiter character as parameters and returns the parsed elements of the string and the element's position as a two-column table. You can then JOIN to an invocation of that function, which SQL will treat just like a table, so if you want the text between the 5th and 6th comma then you'd put "and position = 6" in the JOIN clause.
I have a string called Dats which is either of the general appearence xxxx-nnnnn (where x is a character, and n is a number) or nnn-nnnnnn.
I want to return only the numbers.
For this I've tried:
SELECT Distinct dats,
Left(SubString(artikelnr, PatIndex('%[0-9.-]%', artikelnr), 8000), PatIndex('%[^0-9.-]%', SubString(artikelnr, PatIndex('%[0-9.-]%', artikelnr), 8000) + 'X')-1)
FROM ThatDatabase
It is almost what I want. It removes the regular characters x, but it does not remove the unicode character -. How can I remove this as well? And also, it seems rather ineffective to have two PatIndex functions for every row, is there a way to avoid this? (This will be used on a big database where the result of this Query will be used as keys).
EDIT: Updated as a new database sometimes contained additional -'s or . together with -.
DECLARE #T as table
(
dats nvarchar(10)
)
INSERT INTO #T VALUES
('111BWA30'),
('115-200-11')
('115-22.4-1')
('10.000.22')
('600F-FFF200')
I wasn't sure if you wanted the numbers before the - char as well, but if you do, here is one way to do it:
Create and populate sample table (Please save us this step in your future questions)
DECLARE #T as table
(
dats nvarchar(10)
)
INSERT INTO #T VALUES
('abcde-1234'),
('23-343')
The query:
SELECT dats,
case when patindex('%[^0-9]-[0-9]%', dats) > 0 then
right(dats, len(dats) - patindex('%-[0-9]%', dats))
else
stuff(dats, charindex('-', dats), 1, '')
end As NumbersOnly
FROM #T
Results:
dats NumbersOnly
abcde-1234 1234
23-343 23343
If you want the only the numbers to the right of the - char, it's simpler:
SELECT dats,
right(dats, len(dats) - patindex('%-[0-9]%', dats)) As RightNumbersOnly
FROM #T
Results:
dats RightNumbersOnly
abcde-1234 1234
23-343 343
If you know which characters you need to remove then use REPLACE function
DECLARE #T as table
(
dats nvarchar(100)
)
INSERT INTO #T
VALUES
('111BWA30'),
('115-200-11'),
('115-22.4-1'),
('10.000.22'),
('600F-FFF200')
SELECT REPLACE(REPLACE(dats, '.', ''), '-', '')
FROM #T
I am passing string values from my code like '12th Standard/Ordinary National Diploma,Higher National Diploma' to SQL query, but I am not getting any values and nothing showing any result.
My SQL query:
declare #qua varchar(250),#final varchar(250),#Qualification varchar(250)
set #Qualification= '12th Standard/Ordinary National Diploma,Higher National Diploma'
set #qua =replace(#Qualification,',',''',''')
set #final= ''''+#qua+''''
select * from mytablename in(#final)
Result: Data is not displaying
Thank you in advance.
Instead do it using a table variable like
declare #tbl table(qual varchar(250));
insert into #tbl
select '12th Standard/Ordinary National Diploma'
union
select 'Higher National Diploma';
select * from mytablename where somecolumn in(select qual from #tbl);
Despite trying to put quote marks in there, you're still only passing a single string to the IN. The string just contains embedded quotes and SQL Server is looking for that single long string.
You also don't seem to be comparing a column for the IN.
Your best bet is to pass in multiple string variables, but if that's not possible then you'll have to write a function that parses a single string into a resultset and use that. For example:
SELECT
Column1, -- Because we never use SELECT *
Column2
FROM
MyTableName
WHERE
qualification IN (SELECT qualification FROM dbo.fn_ParseString(#qualifications))
You can insert all your search criteria in one table and then can easily do a lookup on the main table, example below:
DECLARE #MyTable TABLE (Name VARCHAR(10), Qualification VARCHAR(50))
DECLARE #Search TABLE (Qualifications VARCHAR(50))
INSERT INTO #MyTable VALUES ('User1','12th Standard'), ('User2','Some Education'),
('User3','Ordinary National Diploma'), ('User4','Some Degree'),
('User5','Higher National Diploma')
INSERT INTO #Search VALUES ('12th Standard'),('Ordinary National Diploma'),('Higher National Diploma')
SELECT MT.*
FROM #MyTable MT
INNER JOIN (SELECT Qualifications FROM #Search) S ON S.Qualifications = MT.Qualification
As previous said, you are passing a string with commas, not comma separated values. It needs to be split up into separate values.
You can do this by passing the qualification string into XML which you can use to turn it into separate rows of data.
The IN parameter will then accept the data as separate values.
DECLARE #Qualifications as varchar(150) = '12th Standard/Ordinary National Diploma,Higher National Diploma'
Declare #Xml XML;
SET #Xml = N'<root><r>' + replace(#Qualifications, char(44),'</r><r>') + '</r></root>';
select *
from MyTableName
Where MyTableName.Qualification in
(select r.value('.','varchar(max)') as item
from #Xml.nodes('//root/r') as records(r))
Alternatively you can create a table-valued function that splits according to input like in your case its ',' and then INNER JOIN with the returnColumnname and that particular column that you want to filter
SELECT COLUMNS, . . . .
FROM MyTableName mtn
INNER JOIN dbo.FNASplitToTable(#qualifications, ',') csvTable
ON csvTable.returnColumnName = mtn.somecolumn
Table Valued function might be like:
CREATE FUNCTION dbo.FNASplitToTable (#string varchar(MAX), #splitType CHAR(1))
RETURNS #result TABLE(Value VARCHAR(100))
AS
BEGIN
DECLARE #x XML
SELECT #x = CAST('<A>' + REPLACE(#string, #splitType, '</A><A>') + '</A>' AS XML)
INSERT INTO #result
SELECT LTRIM(t.value('.', 'VARCHAR(100)')) AS inVal
FROM #x.nodes('/A') AS x(t)
RETURN
END
GO