parsing using split_to_multimap without throwing an exception - sql

I want to parse out the values which are assigned to the variables without throwing exception:
e.g I want this to return 3 (which it does)
select try_cast(array_join(element_at(split_to_multimap('a=3,b=5', ',', '='), 'a'), '') as int) as a
I want this to return NULL (unfortunately it throws an exception)
select try_cast(array_join(element_at(split_to_multimap('a=3Xb=5', ',', '='), 'a'), '') as int) as a

You can use try:
Evaluate an expression and handle certain types of errors by returning NULL.
select try(
cast(
array_join(
element_at(
split_to_multimap('a=3Xb=5', ',', '='), 'a'), '') as int)) as a

Related

SQL: select the last values before a space in a string

I have a set of strings like this:
CAP BCP0018 36
MFP ACZZ1BD 265
LZP FEI-12 3
I need to extract only the last values from the right and before the space, like:
36
265
3
how will the select statement look like? I tried using the below statement, but it did not work.
select CHARINDEX(myField, ' ', -1)
FROM myTable;
Perhaps the simplest method in SQL Server is:
select t.*, v.value
from t cross apply
(select top (1) value
from string_split(t.col, ' ')
where t.col like concat('% ', val)
) v;
This is perhaps not the most performant method. You probably would use:
select right(t.col, charindex(' ', reverse(t.col)) - 1)
Note: If there are no spaces, then to prevent an error:
select right(t.col, charindex(' ', reverse(t.col) + ' ') - 1)
Since you have mentioned CHARINDEX() in question, I am assuming you are using SQL Server.
Try below
declare #table table(col varchar(100))
insert into #table values('CAP BCP0018 36')
insert into #table values('MFP ACZZ1BD 265')
insert into #table values('LZP FE-12 3')
SELECT REVERSE(LEFT(REVERSE(col),CHARINDEX(' ',REVERSE(col)) - 1)) FROM #table
Functions used
CHARINDEX ( expressionToFind , expressionToSearch ) : returns position of FIRST occurence of an expression inside another expression.
LEFT ( character_expression , integer_expression ) : Returns the left part of a character string with the specified number of characters.
REVERSE ( string_expression ) : Returns the reverse order of a string value

Selecting between quotes (") in SQL Server 2012

I have a table holding IDs in one column and a string in the second column like below.
COLUMN01 COLUMN02
----------------------------------------------------------------------------------
1 abc"11444,12,13"efg"14,15"hij"16,17,18,19"opqr
2 ahsdhg"21,22,23"ghshds"24,25"fgh"26,27,28,28"shgshsg
3 xvd"3142,32,33"hty"34,35"okli"36,37,38,39"adfd
Now I want to have the following result
COLUMN01 COLUMN02
-----------------------------------------------------------
1 11444,12,13,14,15,16,17,18,19
2 21,22,23,24,25,26,27,28,28
3 3142,32,33,34,35,36,37,38,39
How can I do that?
Thanks so much
Here is one way (maybe not the best, but it seems to work). I am NOT a SQL guru...
First, create this SQL Function. It came from: Extract numbers from a text in SQL Server
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
)
Then, you can do something like this to get the results you were asking for. Note that I broke the query up into 3 parts for clarity. And, obviously, you don't need to declare the table variable and insert data into it.
DECLARE #tbl
TABLE (
COLUMN01 int,
COLUMN02 varchar(max)
)
INSERT INTO #tbl VALUES (1, 'abc"11444,12,13"efg"14,15"hij"16,17,18,19"opqr')
INSERT INTO #tbl VALUES (2, 'ahsdhg"21,22,23"ghshds"24,25"fgh"26,27,28,28"shgshsg')
INSERT INTO #tbl VALUES (3, 'xvd"3142,32,33"hty"34,35"okli"36,37,38,39"adfd')
SELECT COLUMN01, SUBSTRING(COLUMN02, 2, LEN(COLUMN02) - 1) as COLUMN02 FROM
(
SELECT COLUMN01, REPLACE(COLUMN02, ' ', '') as COLUMN02 FROM
(
SELECT COLUMN01, (select ',' + number as 'data()' from dbo.GetNumbersFromText(Column02) for xml path('')) as COLUMN02 FROM #tbl
) t
) tt
GO
output:
COLUMN01 COLUMN02
1 11444,12,13,14,15,16,17,18,19
2 21,22,23,24,25,26,27,28,28
3 3142,32,33,34,35,36,37,38,39
I know you want to do it using SQL. But ones I had nearly the same problem and getting this data to a string using a php or another language, than parsing is a way to do it. For example, you can use this kind of code after receiving the data into a string.
function clean($string) {
$string = str_replace(' ', '-', $string); // Replaces all spaces with hyphens.
$string = preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars.
return preg_replace('/-+/', '-', $string); // Replaces multiple hyphens with single one.
}
For more information you might want to look at this post that I retrieved the function: Remove all special characters from a string
As I said this is an easy way to do it, I hope this could help.

Subquery results in comma separated format

I am trying to write a sub-query, that stores all the results in a single column separated by a comma. My code looks something like this
SELECT column1,
column2,
CourseRequests=(SELECT INNERCourseRequests =
COALESCE(CASE
WHEN innercourserequests
= '' THEN
crse_name
ELSE innercourserequests
+ ',' +
crse_name
END, '')
FROM tor_studentcrserequest SCR
WHERE SCR.stud_pk = MS.tt_stud_pk
AND SCR.delt_flag = 0),
column4
FROM tbl_mainstudent MS
When I try to execute the stored procedure, I get an error saying Invalid column name 'INNERCourseRequests'.
What is the correct way to do this?
TSR is a reference to table from the outer column
EDIT: I changed it to:
CourseRequests=(SELECT INNERCourseRequests =
COALESCE(case when #INNERCourseRequests='' THEN CRSE_NAME ELSE
#INNERCourseRequests+','+CRSE_NAME end,'')
However, now I"m getting an error saying subquery returned more than 1 result which is expected.
You can use FOR XML along with a few REPLACEs as shown here:
SELECT column1,
column2,
CourseRequests=COALESCE(
REPLACE(REPLACE(REPLACE((
SELECT crse_name
FROM (
SELECT 1, 22, 'first', 0
UNION ALL SELECT 2, 22, 'second', 1
UNION ALL SELECT 3, 22, 'third', 0
UNION ALL SELECT 4, 555, 'first', 1
) SCR (id, stud_pk, crse_name, delt_flag)
WHERE SCR.stud_pk = MS.tt_stud_pk
AND SCR.delt_flag = 0
FOR XML PATH('')
),'</crse_name><crse_name>', ','),
'</crse_name>', ''), -- remove end tag
'<crse_name>', ''), -- remove beginning tag
''), -- optional COALESCE to ensure no NULLs
column4
FROM (
SELECT 1, 'a', 'b', '2014-01-01'
UNION ALL SELECT 22, 'd', 'e', '2014-02-02'
) MS (tt_stud_pk, column1, column2, column4)
Output:
column1 column2 CourseRequests column4
a b 2014-01-01
d e first,third 2014-02-02
Explanation:
The FOR XML PATH('') flattens the result of the sub-query to be:
<crse_name>first</crse_name><crse_name>third</crse_name>
The first REPLACE converts just the end-tag/beginning-tag combinations that are only found between values (i.e. where the commas go)
The second REPLACE removes the ending tag (can't be done before the first REPLACE)
The third REPLACE removes the beginning tag (can't be done before the first REPLACE)
Note:
There might be a slightly more elegant way to do the XML stuff so you don't need all of the REPLACEs, but not sure and this does work.
I'm pretty sure you can't do this with a single query, and I'm not entirely certain the tactic I've
come up with is a legitimate tactic--meaning, if it is undocumented, a future version of SQL might
not support this. With that said:
Start with the following:
DECLARE #List varchar(max)
SELECT #List = isnull(#List + ', ', '') + InnerCourseRequests
from tor_studentcrserequest
where stud_pk = <TestValue>
and delt_flag = 0
PRINT #List
This will generate a comma-delimited list of all InnerCourseRequests from the tor_studentcrserequest table for a single stud_pk.
Next, turn it into a function:
DROP FUNCTION phkTest
GO
CREATE FUNCTION phkTest (#stud_pk int) -- Change datatype, if not int
RETURNS varchar(max)
AS
BEGIN
DECLARE #List varchar(max)
SELECT #List = isnull(#List + ', ', '') + InnerCourseRequests
from tor_studentcrserequest
where stud_pk = #stud_pk
and delt_flag = 0
RETURN #List
END
GO
(Add a second parameter for delt_flag, if that might vary somehow)
And add that to a query:
SELECT distinct tt_stud_pk, dbo.phkTest(stud_pk)
from tbl_mainstudent
(I wrote all this using one of my tables, then cut-and-paste your table/columns in, so there may be some syntax issues to deal with.)
There may be ways to improve performance for big tables (OUTER APPLY, select distinct before calling the function, and so forth), and it's entirly likely that this might best be done via procedural code by whatever's querying the data in the first place.

Convert to valid decimal data type

I have a column [Cash] nvarchar(50) that has data that will later be converted to decimal(9,3) during an import process,some of the data is consistent with normal looking numeric values such as 134.630,-80.662 and 324.372. Occasionally I have data with multiple dots for the numeric values such as 1.324.372 and -2.134.630. Is there a way of removing this extra dot.
declare #yourtable table(cash varchar(20))
insert #yourtable values('1.324.372')
insert #yourtable values('-2.134.630')
insert #yourtable values('1.234.567.89')
Old Code:
select reverse(replace(replace(stuff(reverse(cash), charindex(
'.', reverse(cash)), 1, ','), '.', ''), ',', '.'))
from #yourtable
Slightly upgraded code(result is the same):
select reverse(stuff(reverse(replace(cash, '.', '')),
charindex('.', reverse(cash)), 1, '.'))
from #yourtable
Result:
1324.372
-2134.630
1234567.89
You could;
select case when len(cash) - len(replace(cash, '.', '')) > 1 then
reverse(stuff(reverse(cash), charindex('.', reverse(cash)), 1, ''))
else
cash
end
from T
Create a view that includes a calculated field with the proper value.
That way you can still see the varchar value and the corresponding decimal value.
Something like this:
select
[Cash],
cast(replace([Cash],'.','') as decimal) as [CashDecimal]
from ....

Invalid length parameter passed to the LEFT or SUBSTRING function in Sql Server

While trying to execute the below query
Declare #t table (id int, string varchar(1000))
INSERT INTO #t (id, string)
SELECT 1, 'zxzzxx,ppppbppp,trtrtr,tyyt,hgghh,fefew,rewr,rwerer'
;WITH test (id, lft, rght, idx)
AS
(
SELECT t.id
,LEFT(t.string, CHARINDEX(', ', t.string) - 1)
,SUBSTRING(t.string, CHARINDEX(', ', t.string) + 2, DATALENGTH(t.string))
,0
FROM #t t
UNION ALL
SELECT c.id
,CASE WHEN CHARINDEX(', ', c.rght) = 0 THEN c.rght ELSE LEFT(c.rght, CHARINDEX(', ', c.rght) - 1) END
,CASE WHEN CHARINDEX(', ', c.rght) > 0 THEN SUBSTRING(c.rght, CHARINDEX(', ', c.rght) + 2, DATALENGTH(c.rght))
ELSE '' END
,idx + 1
FROM test c
WHERE DATALENGTH(c.rght) > 0
)
select id, lft from test
I am getting the below error
Msg 537, Level 16, State 2, Line 8
Invalid length parameter passed to the LEFT or SUBSTRING function.
but the same works for SELECT 1, 'the, quick, brown, fox, jumped, over, the, lazy, dog'
Please help
It seems to be a space missing between your words.
You are currently looking for charindex of ', ' not ','.
And the string does not have any match of ', '.
This Error message happens usually when you do these steps;
When you use Substring,
left, right functions.
When you use CharIndex (used in
the field, the selected word or word
search, or the length of a character
to be inadequate)
The return value is returned to each server in the query expression, the result of the transaction 0 (zero) returns, if error returns -1
This will not result in errors for the server to return a value of -1 or are compared objects.