Possible to use select in an update clause - sql

I am trying to update a table using a select like so. It does not work. Is this the correct method or do I have to put the result of the select into a temp table and update the table from that?
Update WaterRevPropInfo
Set StreetDir = Direction
where exists (SELECT StreetNum,
ISNULL
( LTRIM
( RIGHT
( RTRIM(StreetNum),
LEN
( StreetNum
) +
1 -
( PATINDEX --Identifies first instance of a numeric char
( '%[0-9]%',
StreetNum
) +
PATINDEX --Identifies first instance of a non-numeric char
( '%[^0-9]%',
SUBSTRING --that follows the first numeric char
( StreetNum,
PATINDEX
( '%[0-9]%',
StreetNum
),
LEN(StreetNum)
) + ' '
)
) +
1
)
),
' '
) AS 'Direction')
FROM WaterRevPropInfo

The exists will give you TRUE if WaterRevPropInfo has at least 1 row, regardless of what you put in the select. I think you need to do something like this:
UPDATE WaterRevPropInfo
SET StreetDir = ISNULL(LTRIM(RIGHT(RTRIM(StreetNum), LEN(StreetNum) + 1 - (PATINDEX --Identifies first instance of a numeric char
('%[0-9]%', StreetNum) + PATINDEX --Identifies first instance of a non-numeric char
('%[^0-9]%', SUBSTRING --that follows the first numeric char
(StreetNum, PATINDEX ('%[0-9]%', StreetNum), LEN(StreetNum)) + ' ' ) ) + 1 ) ), StreetDir)
It will assign all your logic to StreetDir, unless it's NULL, in which case it will keep its value (will reassign itself).

Related

SQL : extract next character from string where multiple separators exist

Azure MSSQL Database
I have a column that contains values stored per transaction. The string can contain up to 7 values, separated by a '-'.
I need to be able to extract the value that is stored after the 3rd '-'. The issue is that the length of this column (and the characters that come before the 3rd '-') can vary.
For example:
DIM VALUE
1. NHL--WA-S-MOSG-SER-
2. VDS----HAST-SER-
3. ---D---SER
Row 1 needs to return 'S'
Row 2 needs to return '-'
Row 3 needs to return 'D'
This is by no means an optimal solution, but it works in SQL Server. 😊
TempTable added for testing purposes. Maybe it gives you a hint as of where to start.
Edit: added reference for string_split function (works from SQL Server 2016 up).
CREATE TABLE #tempStrings (
VAL VARCHAR(30)
);
INSERT INTO #tempStrings VALUES ('NHL--WA-S-MOSG-SER-');
INSERT INTO #tempStrings VALUES ('VDS----HAST-SER-');
INSERT INTO #tempStrings VALUES ('---D---SER');
INSERT INTO #tempStrings VALUES ('A-V-D-C--SER');
SELECT
t.VAL,
CASE t.PART WHEN '' THEN '-' ELSE t.PART END AS PART
FROM
(SELECT
t.VAL,
ROW_NUMBER() OVER (PARTITION BY VAL ORDER BY (SELECT NULL)) AS IX,
value AS PART
FROM #tempStrings t
CROSS APPLY string_split(VAL, '-')) t
WHERE t.IX = 4; --DASH COUNT + 1
DROP TABLE #tempStrings;
Output is...
VAL PART
---D---SER D
A-V-D-C--SER C
NHL--WA-S-MOSG-SER- S
VDS----HAST-SER- -
If you always want the fourth element then using CHARINDEX is relatively straightforward:
DROP TABLE IF EXISTS #tmp;
CREATE TABLE #tmp (
rowId INT IDENTITY PRIMARY KEY,
xval VARCHAR(30) NOT NULL
);
INSERT INTO #tmp
VALUES
( 'NHL--WA-S-MOSG-SER-' ),
( 'VDS----HAST-SER-' ),
( '---D---SER' ),
( 'A-V-D-C--SER' );
;WITH cte AS
( -- Work out the position of the 3rd dash
SELECT
rowId,
xval,
CHARINDEX( '-', xval, CHARINDEX( '-', xval, CHARINDEX( '-', xval ) + 1 ) + 1 ) + 1 xstart
FROM #tmp t
), cte2 AS
( -- Work out the length for the substring function
SELECT rowId, xval, xstart, CHARINDEX( '-', xval, xstart) - (xstart) AS xlen
FROM cte
)
SELECT rowId, ISNULL( NULLIF( SUBSTRING( xval, xstart, xlen ), '' ), '-' ) xpart
FROM cte2
I also did a volume test at 1 million rows and this was by far the fastest method compared with STRING_SPLIT, OPENJSON, recursive CTE (the worst at high volume). As a downside this method is less extensible, say you want the second or fifth items for example.

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

How do i get sub string from string in between two symbol

I want to get sub string
in between two operators like = and ,
I tried something like CHARINDEX and LEFT to get the value but i got output in terms of
CN=Khushwant Khatri
but my output should be only Khushwant Khatri
SELECT left([String_value],CHARINDEX(',',([String_value]),0)-1) from trim_string
My string look like
CN=Khushwant Khatri,OU=TestMig,DC=valjha,DC=vedantaresource,DC=local
CN=Raghav Tare,OU=EXECUTIVE,OU=EXUDR,DC=HZL01,DC=vedantaresource,DC=local
CN=D K Chodankar,OU=Users,OU=AD LotusSync,DC=SGL01,DC=vedantaresource,DC=local
as you can see string has variable length
i want only CN value my output should look like
Khushwant Khatri
Raghav Tare
D K Chodankar
You may try this. First you need to find the position of your first =, since name will start from there, then need to find the length of your name which is separated by ,. So we find the index of next , and substract it from the length of string till =. Remaining string is your name as expected.
I am considering that your first value 'CN=' may vary for some condition.
declare #str varchar(max) = 'CN=Khushwant Khatri,OU=TestMig,DC=valjha,DC=vedantaresource,DC=local'
select substring( #str, charindex('=',#str)+1, (charindex(',',#str) - (charindex('=',#str) +1) ))
As per given table structure details please find the below code snippet.
Create table trim_string ( string_value nvarchar(max) )
Insert into trim_string ( string_value )
values ( 'CN=Khushwant Khatri,OU=TestMig,DC=valjha,DC=vedantaresource,DC=local' )
, ( 'CN=Raghav Tare,OU=EXECUTIVE,OU=EXUDR,DC=HZL01,DC=vedantaresource,DC=local' )
, ( 'CN=D K Chodankar,OU=Users,OU=AD LotusSync,DC=SGL01,DC=vedantaresource,DC=local' )
----- this is your query section
; with cte as (
select string_value , substring( string_value, charindex('=',string_value)+1, (charindex(',',string_value) - (charindex('=',string_value) +1) )) newvalue
from trim_string )
---- you may store them in temptable
select * into #temp from cte
----or
---- for selection
select * from cte
----or
----- use to update
update cte set string_value = newvalue
select * from trim_string
As per the comments discussion please try this.
; with cte as (
select string_value , substring( string_value, charindex('=',string_value)+1, (charindex(',',string_value) - (charindex('=',string_value) +1) )) newvalue
from trim_string )
select * from cte
This should give you all of the strings record of your table, with Name is fetched in new column as newvalue.
Use SUBSTRING() as
SELECT SUBSTRING(S, 4, CHARINDEX(',', S)-4),
S
FROM
(
VALUES
('CN=Khushwant Khatri,OU=TestMig,DC=valjha,DC=vedantaresource,DC=local'),
('CN=Raghav Tare,OU=EXECUTIVE,OU=EXUDR,DC=HZL01,DC=vedantaresource,DC=local'),
('CN=D K Chodankar,OU=Users,OU=AD LotusSync,DC=SGL01,DC=vedantaresource,DC=local')
) T(S);
Try this:
select substring(#t,charindex('=',#t)+1,charindex(',',#t)-4)
I am not exactly sure what you want. Alse working with CHARINDEX is a bit unclear.
But I was thinking that maybe this piece of query can help you to find what you want in your string better.
DECLARE #string VARCHAR(MAX) = 'CN=Khushwant Khatri,OU=TestMig,DC=valjha,DC=vedantaresource,DC=local,
,CN=Raghav Tare,OU=EXECUTIVE,OU=EXUDR,DC=HZL01,DC=vedantaresource,DC=local
,CN=D K Chodankar,OU=Users,OU=AD LotusSync,DC=SGL01,DC=vedantaresource,DC=local'
SELECT *
FROM (
SELECT -- this part is based on what your question looks it want.
CASE WHEN LEN(X.value)>2 THEN X.value END AS Value
FROM
(
SELECT value
FROM string_split(REPLACE(#string,',','='),'=') -- might help more than CHARINDEX
) X
) Y
WHERE Y.Value IS NOT NULL

Query SQL with similar values

I have to make a query to a base using as a comparison a string like this 12345678, but the value to compare is this way12.345.678, if I do the following query it does not return anything.
SELECT * FROM TABLA WHERE CAMPO = '12345678'
Where CAMPO would have the value of (12.345.678), if I replace = with a like, it does not return the data either
SELECT * FROM TABLA WHERE CAMPO like '12345678%'
SELECT * FROM TABLA WHERE CAMPO like '%12345678'
SELECT * FROM TABLA WHERE CAMPO like '%12345678%'
None of the 3 previous consultations works for me, how can I make this query?
The value can be of either 7, 8 or 9 numbers and the. It has to be every 3 from the end to the beginning
Use REPLACE() function to replace all the dots '.' as
SELECT *
FROM(
VALUES ('12.345.678'),
('23.456.789')
) T(CAMPO)
WHERE REPLACE(CAMPO, '.', '') = '12345678';
Your query should be
SELECT * FROM TABLA WHERE REPLACE(CAMPO, '.', '') = '12345678';
You can compare the string without the dots to a REPLACE(StringWithDots, '.','')
I recommend you to convert the number to numeric
So you can use < and > operators and all functions that require you to have a number...
the best way to achieve this is to make sure you remove any unecessary dots and convert the commas to dots. like this
CONVERT(NUMERIC(10, 2),
REPLACE(
REPLACE('7.000,45', '.', ''),
',', '.'
)
)
I hope this will help you out.
A SARGABLE solution would be to write a function that takes your target value ('12345678') and inserts the separators ('.') every third character from right to left. The result ('12.345.678') can then be used in a where clause and benefit from an index on CAMPO.
The following code demonstrates an approach without creating a user-defined function (UDF). Instead, a recursive common table expression (CTE) is used to process the input string three characters at a time to build the dotted target string. The result is used in a query against a sample table.
To see the results from the recursive CTE replace the final select statement with the commented select immediately above it.
-- Sample data.
declare #Samples as Table ( SampleId Int Identity, DottedDigits VarChar(20) );
insert into #Samples ( DottedDigits ) values
( '1' ), ( '12' ), ( '123' ), ( '1.234' ), ( '12.345' ),
( '123.456' ), ( '1.234.567' ), ( '12.345.678' ), ( '123.456.789' );
select * from #Samples;
-- Query the data.
declare #Target as VarChar(15) = '12345678';
with
Target as (
-- Get the first group of up to three characters from the tail of the string ...
select
Cast( Right( #Target, 3 ) as VarChar(20) ) as TargetString,
Cast( Left( #Target, case when Len( #Target ) > 3 then Len( #Target ) - 3 else 0 end ) as VarChar(20) ) as Remainder
union all
-- ... and concatenate the next group with a dot in between.
select
Cast( Right( Remainder, 3 ) + '.' + TargetString as VarChar(20) ),
Cast( Left( Remainder, case when Len( Remainder ) > 3 then Len( Remainder ) - 3 else 0 end ) as VarChar(20) )
from Target
where Remainder != ''
)
-- To see the intermediate results replace the final select with the line commented out below:
--select TargetString from Target;
select SampleId, DottedDigits
from #Samples
where DottedDigits = ( select TargetString from Target where Remainder = '' );
An alternative approach would be to add a indexed computed column to the table that contains Replace( CAMPO, '.', '' ).
If the table containing IDs like 12.345.678 is big (contains many records), I would add a computed field that removes the dots (and if this ID does never contain any alphanumeric characters other than dots and has no significant leading zeros then also cast it in an INT or BIGINT) and persist it and lay an index over it. That way you loose a little time when inserting the record but are querying it with maximum speed and therefore saving processor power.

SQL Join Invalid length parameter passed to the LEFT or SUBSTRING function

My database table looks like this:
ID Value
-------------
1 a1¦aa¦a2
1 b1¦aa¦b2
1 b3¦tt¦b3
2 a2¦aa¦z1
2 b2¦tt¦z2
2 b3¦tt¦z3
I'm trying to split each value up with ¦ as the break point which works:
Select
SUBSTRING(MT.Value, 1, CHARINDEX ( '¦' ,MT.Value) - 1) [part1],
SUBSTRING(MT.Value, CHARINDEX ( '¦' ,MT.Value) + 1, 2) [part2],
RIGHT(MT.Value,CHARINDEX('¦',REVERSE(MT.Value),0)-1) [part3]
from
mytable MT
where
MT.id = 1
But then I try to include a join to match up the records with id2 to id1 where the third part matches, SQL Server throws an error:
Invalid length parameter passed to the LEFT or SUBSTRING function.
Code:
Select
SUBSTRING(MT.Value, 1, CHARINDEX ( '¦' ,MT.Value) - 1) [part1],
SUBSTRING(MT.Value, CHARINDEX ( '¦' ,MT.Value) + 1, 2) [part2],
RIGHT(MT.Value, CHARINDEX('¦', REVERSE(MT.Value), 0) - 1) [part3],
mt1.value
Join
mytable mt1 on mt1.ID = 2
and RIGHT(mt.Value, CHARINDEX('¦', REVERSE(mt.Value), 0) - 1) = SUBSTRING(mt1.Value, 1, CHARINDEX ( '¦' , mt1.Value) - 1)
where
MT.id = 1
from
mytable MT
Has anyone got any idea how else I can do it or where I'm going wrong?
You are doing a CHARINDEX for '¦' on the "Value" column then subtracting 1. This mostly likely means there is at least 1 record in which the '¦' does not exist in the string. So your CHARINDEX would = zero (0) then you subtract 1 is a negative number. You cannot use a negative number in a substring or left function.
One solution is casing your statement:
[Part1] =
CASE
WHEN CHARINDEX ( '¦' ,MT.Value) = 0 THEN MT.Value
ELSE SUBSTRING(MT.Value, 1, CHARINDEX ( '¦' ,MT.Value)-1)
END
As you have a working select statement why not wrap that in a common table expression (CTE). Now you can reuse your returned columns.
To make it easier for everyone to share your sample data I've moved it into variable.
DECLARE #Sample TABLE
(
ID INT,
[Value] VARCHAR(8)
)
;
INSERT INTO #Sample
(
ID,
[Value]
)
VALUES
(1,'a1¦aa¦a2'),
(1,'b1¦aa¦b2'),
(1,'b3¦tt¦b3'),
(2,'a2¦aa¦z1'),
(2,'b2¦tt¦z2'),
(2,'b3¦tt¦z3')
;
I'm not entire sure I understood the requirement. So I better explain what I have done. This query returns all ID 1s that share a Part1 with an ID 2.
WITH CTE AS
(
/* Amended OP original query.
* WHERE clause dropped and
* table name replaced with var.
*/
SELECT
Id,
SUBSTRING(MT.Value, 1, CHARINDEX ( '¦' ,MT.Value)-1) AS [part1],
SUBSTRING(MT.Value, CHARINDEX ( '¦' ,MT.Value)+1, 2) AS [part2],
RIGHT(MT.Value,CHARINDEX('¦',REVERSE(MT.Value),0)-1) AS [part3]
FROM
#Sample MT
)
SELECT
*
FROM
CTE AS cte1
INNER JOIN CTE AS cte2 ON cte2.part1 = cte1.part1
WHERE
cte1.ID = 1
AND cte2.ID = 2
;