Calculate amount of text - sql

message
------------
01d,2s3,1wee (nvarchar)
01,32,32154, (nvarchar)
df,g,d, (nvarchar)
dd,12,2 (nvarchar)
I dont know how to achieve the result below and to take account to the ',' in the end of the cell. The data in the list can be changed daily.
Requested result
message Count
------------ -----
01d,2s3,1wee 3
01,32,32154, 3
df,g,d, 3
dd,12,2 3
// DWD

declare #table table(message varchar(20))
insert into #table values('01d,2s3,1wee');
insert into #table values('01,32,32154,');
insert into #table values('df,g,d,');
insert into #table values('dd,12,2');
SELECT message
,CASE WHEN SUBSTRING(REVERSE(message),1,1) = ',' THEN
LEN(message) - LEN(REPLACE(message, ',', '')) ELSE
( LEN(message) - LEN(REPLACE(message, ',', '')) +1 )END As Count
FROM #table

This approach checks if the final character is a ,, and then removes it if it is...
it then compares the length of the original message to the length of the message with all , removed to determine how many , existed originally.
select
len(message) - len(replace(message, ',', '')) as wordCount
from
(
select
case
when right(rtrim(message),1) = ','
then substring(rtrim(message),1,len(rtrim(message))-1)
else message
end as message
) trimmed

I think this is what you are looking for:
SELECT message
,[Count] = LEN(CASE WHEN RIGHT(message, 1) = ',' THEN message ELSE message + ',' END) - LEN(REPLACE(CASE WHEN RIGHT(message, 1) = ',' THEN message ELSE message + ',' END, ',' ,''))
FROM yourtablename

First, You need to get rid of the trail ",", and then count the length of the original string and substract the length of the string replacing "," with nothing. Something like this:
SELECT message, LEN(Newmessage) - LEN(REPLACE(Newmessage,',','')) + 1 [Count]
FROM (SELECT message, CASE WHEN RIGHT(message,1) = ',' THEN LEFT(message,LEN('01,32,32154,')-1) ELSE
message END AS Newmessage
FROM YourTable) A
This assumes that there is no trailing spaces on your columns, otherwise you should use RTRIM

SELECT message
, 1 + LEN(message) - REPLACE ( message , ',' , '' ) AS count
FROM my_table
The tick consists in counting the number of comas (",") in the column.
For this, it calculates the difference between the string and the same string without the coma.

Related

I want to remove the value after *.*, but it the value of the length is not definite

Thank you in advance.
I want to remove the values after ., but the length is not defined it keeps changing but it should not take the values after the second FULL STOP.
1)Example:
Input:- ROL0602.E.DCM.5264403 and COK0105.F.SKE and CLT005.02A.FCM.65721
output: ROL0602.E and COK0105.F and CLT005.02A
2) example :
Input: SKE-5700-00211-000
output: SKE-5700-00211
These are the two columns i want some help with.
I tried using the charindex but as the length keeps on changing i wasn't able to do it.
Try it like this:
DECLARE #tbl TABLE(YourValue VARCHAR(100));
INSERT INTO #tbl VALUES
('ROL0602.E.DCM.5264403'),('COK0105.F.SKE'),('CLT005.02A.FCM.65721'),('SKE-5700-00211-000');
SELECT CASE WHEN CHARINDEX('.',YourValue)>0 THEN LEFT(YourValue,CHARINDEX('.',YourValue,CHARINDEX('.',YourValue,1)+1)-1)
ELSE YourValue END
FROM #tbl AS tbl
The result
ROL0602.E
COK0105.F
CLT005.02A
SKE-5700-00211-000
Please provide a rule for the last example. Don't know how to cut this...
This command uses LEFT to take a string starting at the beginning. The lenght is found by searching for a dot, starting at the position 1 after the first dot.
UPDATE: A more generic solution
The following fill first split the string in its parts (easy to use with other separators too). This is finally re-concatenated depending on a rule you can define yourself:
DECLARE #tbl TABLE(YourValue VARCHAR(100));
INSERT INTO #tbl VALUES
('ROL0602.E.DCM.5264403'),('COK0105.F.SKE'),('CLT005.02A.FCM.65721'),('SKE-5700-00211-000');
WITH Parted AS
(
SELECT YourValue
,CAST('<x>' + REPLACE(REPLACE(tbl.YourValue,'-','.'),'.','</x><x>') + '</x>' AS XML) AS Casted
,CASE WHEN CHARINDEX('.',YourValue)>0 THEN '.' ELSE CASE WHEN CHARINDEX('-',YourValue)>0 THEN '-' ELSE '?' END END AS SeparatorChar
FROM #tbl AS tbl
)
,SingleParts AS
(
SELECT SeparatorChar
,YourValue
,ISNULL(Casted.value('/x[1]','nvarchar(max)'),'') AS Part1
,ISNULL(Casted.value('/x[2]','nvarchar(max)'),'') AS Part2
,ISNULL(Casted.value('/x[3]','nvarchar(max)'),'') AS Part3
,ISNULL(Casted.value('/x[4]','nvarchar(max)'),'') AS Part4
,ISNULL(Casted.value('/x[5]','nvarchar(max)'),'') AS Part5
FROM Parted
)
SELECT CASE SeparatorChar
WHEN '.' THEN Part1 + '.' + Part2
WHEN '-' THEN Part1 + '-' + Part2 + '-' + Part3
ELSE YourValue
END
FROM SingleParts
The result
ROL0602.E
COK0105.F
CLT005.02A
SKE-5700-00211
SHnugo's solution is excellent but will fail if there is only one dot (.) in the string. Here's a tweaked version that will handle that (note my comments).
DECLARE #tbl TABLE(YourValue VARCHAR(100));
INSERT INTO #tbl VALUES
('ROL0602.E.DCM.5264403'),('COK0105.F.SKE'),('CLT005.02A.FCM.65721'),('CLT099.02ACFVVV721'), ('SKE-5700-00211-000');
SELECT
CASE
--If there are two or more dots(.) then return everything up to the second dot:
WHEN LEN(YourValue) - LEN(REPLACE(YourValue,'.','')) > 1
THEN LEFT(YourValue,CHARINDEX('.',YourValue,CHARINDEX('.',YourValue,1)+1)-1)
ELSE YourValue -- if there are 1 or 0 dots(.) then return the entire value
END
FROM #tbl AS tbl;
You can try as follows:
SELECT REPLACE('ROL0602.E.DCM.5264403',
( '.' + REVERSE(SUBSTRING(REVERSE('ROL0602.E.DCM.5264403'), 0,
CHARINDEX('.',
REVERSE('ROL0602.E.DCM.5264403')))) ),
'')

Updating database column with string built based on value of another column

I have a table with a column called Days. The Days column stores a comma delimited string representing days of the week. For example the value 1,2 would represent Sunday, Monday. Instead of storing this information as a comma delimited string, I want to convert it to JSON and store it in a column called Frequency in the same table. For example, a record with the Days value of 1,2 should be updated to store the following in it's Frequency column:
'{"weekly":"interval":1,"Sunday":true,"Monday":true,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}'
I found a way to do this using a case statement assuming that there is only one digit in the Days column like so:
UPDATE SCH_ITM
SET
FREQUENCY =
CASE
WHEN SCH_ITM.DAYS = 1 THEN '{"weekly":{"interval":1,"Sunday":true,"Monday":false,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 2 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":true,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 3 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":true,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 4 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":false,"Wednesday":true,"Thursday":false,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 5 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":false,"Wednesday":false,"Thursday":true,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 6 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":true,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 7 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":true}}'
END
WHERE SCH_TYPE = 'W';
However I cannot seem to figure out an effecient way to handle converting a value such as 1,5 into the correct JSON representation. Obviously I could write out every possible permutation, but surely is a better way?
Okay this will give you what you have asked for
create table test (days varchar(20), frequency varchar(500))
insert into test(days) values('1'),('2'),('3'),('4'),('5'),('6'),('7'),('1,5')
update test set frequency = '{"weekly":{"interval":1,'
+ '"Sunday": ' + case when days like '%1%' then 'true' else 'false' end + ','
+ '"Monday": ' + case when days like '%2%' then 'true' else 'false' end + ','
+ '"Tuesday": ' + case when days like '%3%' then 'true' else 'false' end + ','
+ '"Wednesday": ' + case when days like '%4%' then 'true' else 'false' end + ','
+ '"Thursday": ' + case when days like '%5%' then 'true' else 'false' end + ','
+ '"Friday": ' + case when days like '%6%' then 'true' else 'false' end + ','
+ '"Saturday": ' + case when days like '%7%' then 'true' else 'false' end + '}}'
select * from test
Though of course e.g. Days = '1234' will produce the same as '1,2,3,4' - as will 'Bl4arg3le12' for that matter. If Days is a string, you can put '8' which is meaningless?
Really it sounds like you need an extra table or two:
If "MyTable" is the table with the Days column, add a Days table with the days of the week, then a MyTableDays table to link MyTable entries to days - for the 1,5 example, there would be two rows in MyTableDays
With the help of a parse function and an cross apply
;with cteDays As (Select ID,Name From (Values(1,'Sunday'),(2,'Monday'),(3,'Tuesday'),(4,'Wednesday'),(5,'Thursday'),(6,'Friday'),(7,'Saturday')) D(ID,Name))
Update YourTable Set Frequency = '{"weekly":"interval":1,'+String+'}}'
From YourTable A
Cross Apply (
Select String = Stuff((Select ','+String
From (
Select String='"'+Name+'":'+case when RetVal is null then 'false' else 'true' end
From [dbo].[udf-Str-Parse](A.Days,',') A
Right Join cteDays B on RetVal=ID) N
For XML Path ('')),1,1,'')
) B
Select * from YourTable
Updated Table
Days Frequency
1,2 {"weekly":"interval":1,"Sunday":true,"Monday":true,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}
1,2,3 {"weekly":"interval":1,"Sunday":true,"Monday":true,"Tuesday":true,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}
The UDF if needed
CREATE FUNCTION [dbo].[udf-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(#String,#Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')

split string in column

I have data that has come over from a hierarchical database, and it often has columns that contain data that SHOULD be in another table, if the original database had been relational.
The column's data is formatted in pairs, with LABEL\VALUE with a space as the delimiter, like this:
LABEL1\VALUE LABEL2\VALUE LABEL3\VALUE
There is seldom more than one pair in a record, but there as many as three. There are 24 different possible Labels. There are other columns in this table, including the ID. I have been able to convert this column into a sparse array without using a cursor, with columns for ID, LABEL1, LABEL2, etc....
But this is not ideal for using in another query. My other option it to use a cursor, loop through the entire table once and write to a temp table, but I can't see to get it to work the way I want. I have been able to do it in just a few minutes in VB.NET, using a couple of nested loops, but can't manage to do it in T-SQL even using cursors. Problem is, that I would have to remember to run this program every time before I want to use the table it creates. Not ideal.
So, I read a row, split out the pairs from 'LABEL1\VALUE LABEL2\VALUE LABEL3\VALUE' into an array, then split them out again, then write the rows
ID, LABEL1, VALUE
ID, LABEL2, VALUE
ID, LABEL3, VALUE
etc...
I realize that 'splitting' the strings here is the hard part for SQL to do, but it just seems a lot more difficult that it needs to be. What am I missing?
Assuming that the data label contains no . characters, you can use a simple function for this:
CREATE FUNCTION [dbo].[SplitGriswold]
(
#List NVARCHAR(MAX),
#Delim1 NCHAR(1),
#Delim2 NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
SELECT
Val1 = PARSENAME(Value,2),
Val2 = PARSENAME(Value,1)
FROM
(
SELECT REPLACE(Value, #Delim2, '.') FROM
(
SELECT LTRIM(RTRIM(SUBSTRING(#List, [Number],
CHARINDEX(#Delim1, #List + #Delim1, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(#List)
AND SUBSTRING(#Delim1 + #List, [Number], LEN(#Delim1)) = #Delim1
) AS y(Value)
) AS z(Value)
);
GO
Sample usage:
DECLARE #x TABLE(ID INT, string VARCHAR(255));
INSERT #x VALUES
(1, 'LABEL1\VALUE LABEL2\VALUE LABEL3\VALUE'),
(2, 'LABEL1\VALUE2 LABEL2\VALUE2');
SELECT x.ID, t.val1, t.val2
FROM #x AS x CROSS APPLY
dbo.SplitGriswold(REPLACE(x.string, ' ', N'ŏ'), N'ŏ', '\') AS t;
(I used a Unicode character unlikely to appear in data above, only because a space can be problematic for things like length checks. If this character is likely to appear, choose a different one.)
Results:
ID val1 val2
-- -------- --------
1 LABEL1 VALUE
1 LABEL2 VALUE
1 LABEL3 VALUE
2 LABEL1 VALUE2
2 LABEL2 VALUE2
If your data might have ., then you can just make the query a little more complex, without changing the function, by adding yet another character to the mix that is unlikely or impossible to be in the data:
DECLARE #x TABLE(ID INT, string VARCHAR(255));
INSERT #x VALUES
(1, 'LABEL1\VALUE.A LABEL2\VALUE.B LABEL3\VALUE.C'),
(2, 'LABEL1\VALUE2.A LABEL2.1\VALUE2.B');
SELECT x.ID, val1 = REPLACE(t.val1, N'ű', '.'), val2 = REPLACE(t.val2, N'ű', '.')
FROM #x AS x CROSS APPLY
dbo.SplitGriswold(REPLACE(REPLACE(x.string, ' ', 'ŏ'), '.', N'ű'), 'ŏ', '\') AS t;
Results:
ID val1 val2
-- -------- --------
1 LABEL1 VALUE.A
1 LABEL2 VALUE.B
1 LABEL3 VALUE.C
2 LABEL1 VALUE2.A
2 LABEL2.1 VALUE2.B
With only three values, you can manage to do this by brute force:
select (case when rest like '% %'
then left(rest, charindex(' ', rest) - 1)
else rest
end) as val2,
(case when rest like '% %'
then substring(col, charindex(' ', col) + 1, 1000)
end) as val3
from (select (case when col like '% %'
then left(col, charindex(' ', col) - 1)
else col
end) as val1,
(case when col like '% %'
then substring(col, charindex(' ', col) + 1, 1000)
end) as rest
from t
) t
Using the SQL split string function given at referenced SQL tutorial, you can split the label-value pairs as following
SELECT
id, max(label) as label, max(value) as value
FROM (
SELECT
s.id,
label = case when t.id = 1 then t.val else NULL end,
value = case when t.id = 2 then t.val else NULL end
FROM dbo.Split(N'LABEL1\VALUE1 LABEL2\VALUE2 LABEL3\VALUE3', ' ') s
CROSS APPLY dbo.Split(s.val, '\') t
) t
group by id
You can see that the split string function is called twice, first for splitting pairs from others. Then the second split function joined to previous one using CROSS APPLY splits labels from pairs

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.

SQL, remove appearances of a comma at end of the comma seperated list

How can we remove last comma from a comma seperated list in SQL query?
I have done a sample where i get a comma seperated string as output. But i found that a comma is comming after the string ends. I have tried to trim it of but was not able to do it.Can any one help me in getting it off?
my resulting string looks like this
eg:- some text, some more text, even more, yet more,
DECLARE #my_string nvarchar(128)
SET #my_string = N'Some Text, Some More Text, Even More, Yet More,'
select SUBSTRING(#my_string, 1, LEN(#my_string) - 1)
Should you need further assistance, you should provide further information.
Update MyTable
Set MyField = Case
When Right(MyField,1) = ',' Then Substring( MyField, 1, Len(MyField) - 1 )
Else MyField
End
Btw, another alternative:
Update MyTable
Set MyField = Substring( MyField, 1, Len(MyField) - 1 )
Where Value Like '%,'
Test script:
Declare #TestValues Table ( Value nvarchar(100) not null )
Insert #TestValues(Value) Values( 'XYZZY' )
Insert #TestValues(Value) Values( 'PLUGH' )
Insert #TestValues(Value) Values( 'SpiffyValueWithComma,' )
Select Case
When Right(Value,1) = ',' Then Substring( Value, 1, Len(Value) - 1 )
Else Value
End
From #TestValues
Results:
XYZZY
PLUGH
SpiffyValueWithComma
Update
One possibiity is that your trailing value isn't a comma but rather a comma and a space. If that's the case, then you need to modify your query like so:
Update MyTable
Set MyField = Substring( MyField, 1, Len(MyField) - 1 )
Where Value Like '%,'
Or Value Like '%, '