How can I add a character into a specified position into string in SQL SERVER? - sql

I have a varchar field like:
195500
122222200
I need to change these values to:
1955.00
1222222.00

try this
Declare #s varchar(50) = '1234567812333445'
Select Stuff(#s, Len(#s)-1, 0, '.')
--> 12345678123334.45
fiddle demo

Query:
SELECT col,
LEFT(col,len(col)-2) + '.' + RIGHT(col,2) as newcol
FROM Table1
Result:
| COL | NEWCOL |
|-----------|------------|
| 195500 | 1955.00 |
| 122222200 | 1222222.00 |

If you want to add a '.' before the last two digits of your values you can do:
SELECT substring(code,0,len(code)-1)+'.'+substring(code,len(code)-1,len(code))
FROM table1;
sqlfiddle demo

Please try:
select
Col,
REVERSE(STUFF(REVERSE(Col), 1, 2, LEFT(REVERSE(Col), 2)+'.'))
from YourTable
SQL Fiddle Demo

CREATE TABLE #T ( Value VARCHAR(20) )
INSERT INTO #T ( Value ) VALUES ( 195500 ), ( 122222200)
SELECT
Value
, NewValue = CONVERT(DECIMAL(17,2),CONVERT(DECIMAL,Value) / 100)
FROM #T
| Value | NewValue |
|-----------|------------|
| 195500 | 1955.00 |
| 122222200 | 1222222.00 |

Please Try :
select reverse(stuff(reverse(columnName),3,0,'.') ) from yourTable

Ran into something similar and came up with this, could probably change this to a function/SP to make it reusable. Scenario faced was inserting a specified character at different positions within the string, for a certain number of times.
/*
--- passed in string or column, N'C4CB6B22250B'
--- desired output, C4:CB:6B:22:25:0D
--- Test values
--- declare #strToChange varchar(50) = N'C4:CB:6B:22:25:0B'
--- declare #strToChange varchar(50) = N'C4CB6B22250B'
*/
declare #strToChange varchar(50) = N'C4CB6B22250B'
IF(SELECT LEN(#strToChange) - LEN(REPLACE(#strToChange, ':', ''))) > 0
BEGIN
---returns count of specified char
SELECT LEN(#strToChange) - LEN(REPLACE(#strToChange, ':', ''))
END
ELSE
BEGIN
declare #charCount int = 5; --# of times to insert the char
declare #shiftPosition int = 2; -- inital insertion shift
While(#charCount > 0)
begin
SET #strToChange = LEFT(#strToChange,len(#strToChange)- #shiftPosition) + ':' + RIGHT(#strToChange,#shiftPosition)
SET #charCount = #charCount - 1 --decrement charCount for each added char
SET #shiftPosition = #shiftPosition + 3 ---increment shift position by 3 for the new char and the chars that were already there
end
SELECT #strToChange
END

Please see the following code. You can choose the symbols and index in variable.
declare #index int,#sym varchar(10)
set #sym='#'
set #index=2
select left(195500,#index) +''+#sym+''+right(195500,len(195500)-#index)

declare #a varchar(10) = 'aaa'
select concat(#a,'.00')

Related

Get row that had multi string in name SQL server

I'm using SQL server 2014 I try to get row that have multi-value in name like:
declare #value ='m n'
So the value that return should have 'm' and 'n' in any position
I tried to use
Select * from Table where contains(name,N'"*m*" and "*n*"')
But the value return only if it started by 'm' and 'n'
I had also tried:
select * from Table where name like '%m%n%'
the value return only if name contains 'm' then 'n' not 'n' then 'm'.
Note: I'm getting value from parameter so I don't known how many char or what position so I'm using replace on #value to get what I want.
Since you want to use that variable.
Then best split the letters or words.
Having the STRING_SPLIT function would be great for this.
But there are more ways to split strings.
The example below does it with a WHILE loop.
CREATE TABLE [Table]
( id INT IDENTITY(101,1) PRIMARY KEY,
name NVARCHAR(100) NOT NULL
);
GO
✓
INSERT INTO [Table] (name) VALUES
('a munchkin'),
('never ever sever mah lever'),
('saintess'),
('them mammaries');
GO
4 rows affected
DECLARE #value NVARCHAR(1000);
SET #value =' m n ';
DECLARE #values TABLE (
value NVARCHAR(42)
);
DECLARE #words NVARCHAR(1000);
DECLARE #word NVARCHAR(42);
SET #words = RTRIM(LTRIM(#value))+' ';
WHILE CHARINDEX(' ', #words) > 0
BEGIN
SET #word = SUBSTRING(#words,0,CHARINDEX(' ',#words))
SET #words = LTRIM(SUBSTRING(#words,CHARINDEX(' ',#words)+1,LEN(#words)))
IF #word != '' INSERT INTO #values (value) VALUES (#word);
END;
DECLARE #TotalValues INT;
SET #TotalValues = (select count(distinct value) from #values);
--
-- use the table variable to query the table
--
SELECT *
FROM [Table] t
WHERE EXISTS
(
SELECT 1
FROM #values v
WHERE t.name LIKE '%'+v.value+'%'
HAVING COUNT(DISTINCT v.value) = #TotalValues
);
GO
id | name
--: | :-------------------------
101 | a munchkin
102 | never ever sever mah lever
db<>fiddle here
You have to separate your conditions like this
select * from Table where name like '%m%' and name like '%n%'

SQL Script to find Count the number of :username Occurrences of a String

I have a table that stores information whenever user make changes to the DB. I want to extract how many times the user make changes on the date on the application. The info is normally stored for each user in one row for example :
2019-06-15randomname1:YES I DID IT 2019-06-14randomname2:HHHHHHH JJJJJJ 2019-06-14Urandomnamexxxxxx: COMMENT OF PEOPLE
What I want is to search :username to detect how many times the user has changed. In this instance. The answer suppose to be 3. How can I do it
DECLARE #logEntry VARCHAR(4000);
SET #logEntry = ':' + (SELECT PERSON_NAME FROM P_PERSON WHERE PERSON = logged_person)
SELECT
id
,value
,COUNT = (LEN(value) - LEN(REPLACE(value, #logEntry , '')))/LEN(#logEntry)
FROM table
Will I use regular expression because for this particular example the answer will be 3 since we have 3.
I have decided to use :username I am having problem with Subquery returned more than 1 value :
If I understand, you want to count the occurrence of a date in a string
DECLARE #D VARCHAR(10) = '2019-01-01';
SELECT *, LEN(V) - (LEN(REPLACE(V, #D, '')) * 10) Occurrence
FROM (VALUES('A2019-01-01B2019-01-01C2019-01-01D2019-01-01E2019-01-01F2019-01-01'))T(V);
Returns:
+--------------------------------------------------------------------+------------+
| V | Occurrence |
+--------------------------------------------------------------------+------------+
| A2019-01-01B2019-01-01C2019-01-01D2019-01-01E2019-01-01F2019-01-01 | 6 |
+--------------------------------------------------------------------+------------+
Note that this will works only when the string doesn't contains a white spaces.
If you have a white spaces, then you need to remove them first as
DECLARE #D VARCHAR(10) = '2019-01-01';
SELECT *, LEN(REPLACE(V, ' ', '')) - (LEN(REPLACE(REPLACE(V, ' ', ''), #D, '')) * 10) Occurrence
FROM (VALUES('A 2019-01-01 B 2019-01-01 C 2019-01-01 D 2019-01-01 E 2019-01-01 F 2019-01-01'))T(V);
You just changed your question, to search by a user name, but since the ':' is fixed, and if you have 2016+ version you can do as
DECLARE #D VARCHAR(10) = 'UserName1';
SELECT *,
(SELECT COUNT(1) FROM STRING_SPLIT(V, ':') WHERE Value LIKE CONCAT('%', #D, '%'))
FROM (VALUES
('2019-06-15UserName1:YES I DID IT 2019-06-14UserName2:HHHHHHH JJJJJJ 2019-06-14UserName1: COMMENT OF PEOPLE')
) T(V);
Finally, I'll recommend to re-think of that design, which is the real issue here, and read more about normalization.
UPDATE:
Here is how to count the user name with joining the two tables
SELECT *,
(
SELECT COUNT(1)
FROM STRING_SPLIT(Col, ':')
WHERE Value LIKE CONCAT('%', UserName)
) Cnt
FROM Users U JOIN Data D
ON D.Col LIKE CONCAT('%', U.UserName, '%');
Returns:
+----------+----------------------------------------------+-----+
| UserName | Col | Cnt |
+----------+----------------------------------------------+-----+
| User1 | 2019-01-01User1:YES 2019-01-02User2:No | 1 |
| User2 | 2019-01-01User1:YES 2019-01-02User2:No | 1 |
| User1 | 2019-01-01User1:YES I 2019-01-02User1:No Way | 2 |
+----------+----------------------------------------------+-----+
See how it's working on live demo
First, you have a lousy data model and processing. You should not be just adding substrings to a string. You should be adding new rows to a table. And, you should not be encoding information in a string. You should be using columns for that.
My strongest suggestion is that you fix your data model and processing.
That said, you might be stuck with this situation. THe simplest solution is just to look for
SELECT id, value,
(LEN(REPLACE(value, 'XXXXXXXXXXXXX:', 'XXXXXXXXXXXXX:1') -
LEN(value)
) as Num_Times
FROM Table;
Of course, this assumes that 'XXXXXXXXXXXXX:' doesn't actually occur in the message. If that is a possibility, see my original comment on the data structure.
The following will do as you ask, but you seriously need to reconsider how you store your data. What if instead of someone commenting "I did it", they entered "I did it on 2019-01-01"?
-- DateCount
-- Return number of occurances of ####-##-## where # is a digit
create function dbo.DateCount(#s nvarchar(max))
returns int as
begin
declare #k int = 0 -- #k holds the count so far
declare #i int = 1 -- index into string, start at first character
while #i < len(#s)-9 -- keep checking until we get to the end
begin
if substring(#s,#i,10) like '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
set #k = #k + 1 -- increment count if these 10 characters match
set #i = #i + 1 -- check the next character
end
return #k -- return the count
end
go
select dbo.DateCount( '2019-06-15randomname1:YES I DID IT 2019-06-14random'
+ 'name2:HHHHHHH JJJJJJ 2019-06-14Urandomnamexxxxxx: '
+ 'COMMENT OF PEOPLE' )
-- Result is 3
If you're keen on using a set-based solution instead of a while loop, you can try this:
create function dbo.DateCount(#s nvarchar(max))
returns int as
begin
declare #k int;
with A as ( select 1 as I
union all
select I+1 as I from A where I<=len(#s)-9 )
select #k=count(*) from A
where substring(#S,I,10) like '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
option (maxrecursion 0)
return #k
end
But, in my performance tests, I find that the set-based solution takes 50% longer.

String Split Ignore Last delimiter if no data

I am string splitting some values that are comma delimited into rows.
However some values have an extra comma on the end.
Example
Userid | Value
1 | A,B,C,D,
2 | F,H
Code
select value
from string_split('A,B,C,D,',',')
Current Output
UserId | Value
1 | A
1 | B
1 | C
1 | D
1 |
Is there any way to make the string split function ignore the final comma if no data follows it?
Desired Output
UserId | Value
1 | A
1 | B
1 | C
1 | D
Using MSSQL
Just add "WHERE" sentence like this:
select value
from string_split('A,B,C,D,',',')
WHERE value <> ''
STRING_SPLIT Function doesn't support for lower version of sql server so first create a function to split the given string and join the function with your select query.Here is below sample for your expected result.
Created User defined Function
CREATE FUNCTION [dbo].[Udf_StringSplit]
(
#Userid INT,
#Value VARCHAR(1000)
)
RETURNS #Result TABLE(
Userid INT,
Value VARCHAR(10)
)
AS BEGIN
DECLARE #Data AS TABLE
(
Userid INT,
Value VARCHAR(100)
)
INSERT INTO #Data(Userid,Value)
SELECT #Userid, #Value
INSERT INTO #Result(Userid,Value)
SELECT Userid,
Split.a.value('.','nvarchar(1000)') AS Value
FROM
(
SELECT Userid,
CAST('<S>'+REPLACE(#Value,',','</S><S>')+'</S>' AS XML) Value
FROM #Data
) AS A
CROSS APPLY Value.nodes('S') AS Split(a)
WHERE Userid=#Userid AND Split.a.value('.','nvarchar(1000)') <>''
RETURN
END
GO
Sample data table
DECLARE #Data AS TABLE(Userid INT , Value VARCHAR(100))
INSERT INTO #Data
SELECT 1,'A,B,C,D,' UNION ALL
SELECT 2,'F,H'
Sql script to get the expected result
SELECT d.Userid,
f.Value
FROM #Data d
CROSS APPLY [dbo].[Udf_StringSplit] (d.Userid,d.Value) AS f
WHERE d.Userid=1
GO
Result
Userid Value
------------
1 A
1 B
1 C
1 D

T-SQL: Efficient way to add up column values

Now I'm sure this has been asked and superbly been answered on here. However, I am unable to find the answer since it touches many keywords.
I basically want to replace a table of the form:
Type amount param note
7 2 str1 NULL
42 12 str2 NULL
128 7 str3 samplenote
42 12 NULL NULL
101 4 str4 NULL
42 12 NULL NULL
7 1 str1 samplenote
128 2 str5 NULL
with a table like:
Type amount param note
7 3 str1 combined
42 36 NULL combined
128 9 NULL combined
101 4 str4 combined
In words, I seek to sum up the amount parameter based on its type while declaring param = NULL for all "unclear" fields. (param should be NULL when the param values of combined Types have more than one different content; else, param should have the original content.)
With my python background, I tackled this task with a for loop approach, iterating through the types, adding a new row for every type with summed up amount and note = 'combined', to then delete the remaining rows (see below). There has to be a more efficient way with some JOIN statement I'm sure. But how would that look like?
FYI, this is the solution I am working on (not functioning yet!):
/*** dbo.sourcetable holds all possible Type values ***/
CREATE PROCEDURE [sumup]
AS
BEGIN
DECLARE #i int = (SELECT TOP (1) Type FROM [dbo].[sourcetable] ORDER BY Type)
DECLARE #MaxType int = (SELECT TOP (1) Type FROM [dbo].[sourcetable] ORDER BY Type DESC)
DECLARE #sum int
BEGIN TRY
WHILE #i <= #MaxType
BEGIN
IF EXISTS (SELECT * FROM [dbo].[worktable] WHERE Type = #i)
BEGIN
SET #sum = (SELECT SUM(amount) FROM [dbo].[worktable] WHERE Type = #i)
BEGIN
WITH cte AS (SELECT * FROM [dbo].[worktable] WHERE Type = #i)
INSERT INTO [dbo].[worktable]
([Type]
,[amount]
,[param]
,[note]
SELECT
cte.Type
,#sum
,cte.param
,'combined'
FROM cte
END
DELETE FROM [dbo].[worktable] WHERE Type = #i AND ISNULL([note],'') <> 'combined'
END
SET #i = #i + 1
END
END TRY
BEGIN CATCH
-- some errorlogging code
END CATCH
END
GO
This can be achieved with a single select statement.
If you require your combined flag to only apply to where more than one row has been combined, add another case expression checking the result of either a count(1) for rows combined or count(distinct param) for unique param values combined:
declare #t as table(type int, amount int, param varchar(15), note varchar(15));
insert into #t values (7,2,'str1',NULL),(42,12,'str2',NULL),(128,7,'str3','samplenote'),(42,12,NULL,NULL),(101,4,'str4',NULL),(42,12,NULL,NULL),(7,1,'str1','samplenote'),(128,2,'str5',NULL);
select type
,sum(amount) as amount
,case when count(distinct isnull(param,'')) = 1
then max(param)
else null
end as param
,'combined' as note
from #t
group by type
order by type;
Output:
+------+--------+-------+----------+
| type | amount | param | note |
+------+--------+-------+----------+
| 7 | 3 | str1 | combined |
| 42 | 36 | NULL | combined |
| 101 | 4 | str4 | combined |
| 128 | 9 | NULL | combined |
+------+--------+-------+----------+
I am doing this way from keyboard, but this may work or be close to what you want
Select type , amount , iif( dc=1,p,null) param, 'combined' note
from
(
Select type, sum(amount) amount,
count(distinct Param) dc,max(Param) p
From ....
Group by type
) x
Here is a possible solution:
declare #tbl as table (
type int
,amount int
,param varchar(15)
,note varchar(15)
)
insert into #tbl values (7,2,'str1',NULL)
insert into #tbl values (42,12,'str2',NULL)
insert into #tbl values (128,7,'str3','samplenote')
insert into #tbl values (42,12,NULL,NULL)
insert into #tbl values (101,4,'str4',NULL)
insert into #tbl values (42,12,NULL,NULL)
insert into #tbl values (7,1,'str1','samplenote')
insert into #tbl values (128,2,'str5',NULL)
;WITH CTE AS (
SELECT
type
,SUM(AMOUNT) AS amount
,COUNT(DISTINCT ISNULL(param, 'dummy value')) AS ParamNo
,MAX(Param) AS Param
FROM #tbl
GROUP BY type
) SELECT
type
,amount
,CASE WHEN ParamNo = 1 THEN Param ELSE NULL END AS Param
,'combined' AS note
FROM CTE
This should work:
Select Type, sum(amount) as amount, count(distinct param)
, case when count(distinct param) = 1 then max(param) end as param,
'Combined' as note
From
mytable
Group By Type

SQL parse delimited string

I can't find a good way to do this and because of some restrictions it has to be a coded without the use of variables but I can call a function. Anyway, I need to return a result set from a Select query and one of the rows is a pipe delimited string.
So, it will return something like:
id| Name | Message |
---------------------
1 | Some Name | 'Here is my | delimited | message'
And I need it to be
id| Name | Message1 | Message2 | Message3
------------------------------------------------------
1 | Some Name | 'Here is my' | 'delimited' | 'message'
I was thinking of something like Parsename('','|',1), where you can pass in the delimiter instead of it always being a period but I don't know the best way to accomplish that.
EDIT: I've tried variations of this but of course it is very confusing. There could be 4 or more |
SELECT Field1,
Field2 AS Originalvalue,
--1
SUBSTRING(Field2,0,CHARINDEX('|',Field2)) AS Msg1,
--2
LEFT(SUBSTRING(Field2,CHARINDEX('|',Field2)+1 ,LEN(Field2)),CHARINDEX('|',SUBSTRING(Field2,CHARINDEX('|',Field2)+1 ,LEN(Field2)))-1)
AS ExtractedValue
FROM Table1 T1 JOIN Table2 T2
ON T1.Id = T2.Id
WHERE T1.Id = 12
You can write a sql function as follows.
create Function dbo.fn_Parsename(#Message Varchar(1000), #delimiter char(1), #index int )
Returns Varchar(1000)
As
Begin
Declare
#curIndex int = 0,
#pos int = 1,
#prevPos int = 0,
#result varchar(1000)
while #pos > 0
Begin
set #pos = CHARINDEX(#delimiter, #Message, #prevPos);
if(#pos > 0)
begin-- get the chars between the prev position to next delimiter pos
set #result = SUBSTRING(#message, #prevPos, #pos-#prevPos)
end
else
begin--get last delim message
set #result = SUBSTRING(#message, #prevPos, LEN(#message))
end
if(#index = #curIndex)
begin
return #result
end
set #prevPos = #pos + 1
set #curIndex = #curIndex + 1;
end
return ''--not found
End
And you can call it as below:
select dbo.fn_Parsename('Here is my | delimited | message','|', 0)
select dbo.fn_Parsename('Here is my | delimited | message','|', 1)
select dbo.fn_Parsename('Here is my | delimited | message','|', 2)