Fix CHARINDEX query issues - sql-server

Hello all I am having my query as follows where I am checking whether the value is having NULL or empty
SELECT CASE WHEN LSTR = '' THEN 0 ELSE LSTR END AS left_string, CASE WHEN RSTR = '' THEN 0 ELSE RSTR END AS right_string FROM table
cross apply (
SELECT
CAST(LEFT(colsetting, CHARINDEX('/', colsetting) -1) AS INT) LSTR
, CAST(SUBSTRING(colsetting, CHARINDEX('/', colsetting) +1, 200) AS INT) RSTR
) ca1
WHERE
CHARINDEX('/', colsetting) > 0
Initially I am having this but as I am getting some conversion error I have written the above one
DECLARE #setting nvarchar(100)='80/ '
SELECT CAST(CAST(SUBSTRING(isnull(#setting,0), 0, CHARINDEX('/',isnull(#setting,0))) AS DECIMAL(3, 0)) AS INT)
SELECT CAST(CAST(SUBSTRING(isnull(#setting,0), CHARINDEX('/',isnull(#setting,0),0) + 1, CHARINDEX('/',isnull(#setting,0),0)) AS DECIMAL(3, 0)) AS INT)
My setting values can be 80/80 or 80/ or /80 when I execute the script what I need is I would like to display the values separately either by first query or by second one. Second one is giving conversion issue, first one is working fine but I would like to know is there any better approach
SQL fiddle http://sqlfiddle.com/#!18/45e87/1
With both queries http://sqlfiddle.com/#!18/45e87/2

Please try this (I used original table name from your sql fiddle instead):
select
[left_string] = convert(int, left([productName], charindex('/', [productName], 0) - 1))
,[right_string] = convert(int, right([productName], charindex('/', reverse([productName]), 0) - 1))
from [ForgeRock] where charindex('/', [productName]) > 0;

I actually think your code is fine, but since I love a good cte, I will throw this one out here. You could try and compare:
WITH CTE AS
(
SELECT CAST(LEFT(Productname, CHARINDEX('/', Productname)-1) AS INT) AS Lstr,
CAST(SUBSTRING(Productname, CHARINDEX('/', Productname)+1, 200) AS INT) AS Rstr
FROM Forgerock
WHERE CHARINDEX('/', Productname) > 0
)
SELECT CASE WHEN Lstr = '' THEN 0 ELSE Lstr
END AS Left_String,
CASE WHEN Rstr = '' THEN 0 ELSE Rstr
END AS right_string
FROM CTE;

Related

How to insert a character into a string every 3 characters from the right

I have a string made up of numbers. The length of the string ranges anywhere from 1 character to 9 characters.
I want to insert a dash (-) every three characters from the right. This will only be relevant for strings with upwards of three characters.
This will just be a select statement, as I do not want to actually update the string itself.
For example,
8 should return 8
476 should return 476
4767 should return 4-767
45907392 should return 45-907-392
845907392 should return 845-907-392
This should work:
select
*,
replace(format(n,'#,#'),',','-')
from (values (8),(476),(4767),(45907392),(845907392)) x (n)
Change , to . if it's thousand separator in your system, or provide a culture as third parameter in FORMAT function so it always works the same.
Since SQL Server 2012 you can use the FORMAT function as described in the other answers. In case you want to format a string value like 'abcde' too you can use only string functions like this:
DECLARE #str VARCHAR(100) = '845907392';
-- 845-907-392
SELECT REVERSE(SUBSTRING(REVERSE(#str), 7, 3))
+ CASE WHEN LEN(#str)>6 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 4, 3))
+ CASE WHEN LEN(#str)>3 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 1, 3))
You can also create a function:
CREATE FUNCTION dbo.GetFormatTripleDash (#str varchar(255))
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #retStr VARCHAR(255) = REVERSE(SUBSTRING(REVERSE(#str), 7, 3))
+ CASE WHEN LEN(#str)>6 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 4, 3))
+ CASE WHEN LEN(#str)>3 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 1, 3))
RETURN(#retStr)
END
You can use this funtion like this:
-- 845-907-392
SELECT dbo.GetFormatTripleDash('845907392')
A more flexible solution using the function. Now you can use a much longer string value and you can define the part size separated by - character.
CREATE FUNCTION dbo.GetFormatDash (#str varchar(255), #partSize INT = 3)
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #startSize INT = 0;
DECLARE #retStr VARCHAR(255) = '';
WHILE #startSize < LEN(#str)
BEGIN
SET #retStr = REVERSE(SUBSTRING(REVERSE(#str), #startSize + 1, #partSize)) + CASE WHEN #startSize > 0 THEN '-' ELSE '' END + #retStr;
SET #startSize = #startSize + #partSize;
END
RETURN(#retStr)
END
You can use this improved function like this:
-- 12-345-678-901-234-567-890
SELECT dbo.GetFormatDash('12345678901234567890', DEFAULT)
SELECT dbo.GetFormatDash('12345678901234567890', 3)
-- 12345-67890-12345-67890
SELECT dbo.GetFormatDash('12345678901234567890', 5)
demo on dbfiddle.uk
Its a bit gross but it works! Try i
t and let me know if you agree
SELECT
CASE
WHEN LEN(yourColumn)>6 THEN format(CAST(YourColumn AS NUMERIC), '###-###-###')
WHEN LEN(YourColumn)>3 THEN format(CAST(YourColumn AS NUMERIC), '###-###')
ELSE YourColumn
END
Here is another way to do it, albeit more typing
SELECT
CASE
WHEN LEN(yourColumn)>6 THEN SUBSTRING(YourColumn, 1, 3) +'-'+ SUBSTRING(YourColumn, 4, 3)
WHEN LEN(YourColumn)>3 THEN REVERSE(SUBSTRING(REVERSE(YourColumn), 1, 3) +'-'+ SUBSTRING(REVERSE(YourColumn), 4, 3))
ELSE YourColumn
END
Try this. I'm sure it can also be extended to allow for any number of characters with a bit of effort
declare #input nvarchar(100) = '845907392'
declare #separator char(1) = '-'
--option1 - CTE
;with dash1 as (
select isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input)) as v
)
, dash2 as(
select isnull(stuff(v, 1+7, 0, #separator), v) as v from dash1
)
select reverse(v) from dash2
--option2 - Non CTE
select reverse(isnull(stuff(isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input)), 1+7, 0, #separator), isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input))))
Try this using format() function.
Select Replace(format(8, '#,##0'), ',', '-')
Select Replace(format(476, '#,##0'), ',', '-')
Select Replace(format(45907392, '#,##0'), ',', '-')
Select Replace(format(845907392, '#,##0'), ',', '-')
Live db<>fiddle demo.
A solution with a recursive CTE:
with cte as (
select col, len(col) - 3 pos from tablename
union all
select
cast(left(col, pos) + '-' + right(col, len(col) - pos) as varchar(100)),
pos - 3
from cte
where pos > 0
)
select col from cte
where pos <= 0
See the demo.
Results:
> | col |
> | :---------- |
> | 8 |
> | 476 |
> | 845-907-392 |
> | 45-907-392 |
> | 4-767 |

SQL string before and after certain characters

SELECT NAME
FROM SERVERS
returns:
SDACR.hello.com
SDACR
SDACR\AIR
SDACR.hello.com\WATER
I need the SELECT query for below result:
SDACR
SDACR
SDACR\AIR
SDACR\WATER
Kindly help ! I tried using LEFT and RIGHT functions as below, but not able to get combined output correctly:
SELECT
LEFT(Name, CHARINDEX('.', Name) - 1)
FROM
SERVERS
SELECT
RIGHT(Name, LEN(Name) - CHARINDEX('\', Name))
FROM
SERVERS
It looks like you're just trying to REPLACE a substring of characters in your column. You should try this:
SELECT REPLACE(Name,'.hello.com','') AS ReplacementName
FROM SERVERS
In tsql, you can concatenate values with CONCAT(), or you can simply add strings together with +.
SELECT LEFT(Name, CHARINDEX('.',Name)-1) + RIGHT(Name,LEN(Name)-CHARINDEX('\',Name)) from SERVERS
Also, be careful with doing arithmetic with CHARINDEX(). A value without a '.' or a '\' will return a NULL and you will get an error.
You can use LEFT for this to select everything up to the first period (dot) and add on everything after the last \
declare #servers table ([NAME] varchar(64))
insert into #servers
values
('SDACR.hello.com '),
('SDACR'),
('SDACR\AIR'),
('SDACR.hello.com\WATER')
select
left([NAME],case when charindex('.',[NAME]) = 0 then len([NAME]) else charindex('.',[NAME]) -1 end) +
case when charindex('\',left([NAME],case when charindex('.',[NAME]) = 0 then len([NAME]) else charindex('.',[NAME]) -1 end)) = 0 then right([NAME],charindex('\',reverse([NAME]))) else '' end
from #servers
Throwing my hat in.... Showing how to use Values and APPLY for cleaner code.
-- sample data in an easily consumable format
declare #yourdata table (txt varchar(100));
insert #yourdata values
('SDACR.hello.com'),
('SDACR'),
('SDACR\AIR'),
('SDACR.hello.com\WATER');
-- solution
select
txt,
newTxt =
case
when loc.dot = 0 then txt
when loc.dot > 0 and loc.slash = 0 then substring(txt, 1, loc.dot-1)
else substring(txt, 1, loc.dot-1) + substring(txt, loc.slash, 100)
end
from #yourdata
cross apply (values (charindex('.',txt), (charindex('\',txt)))) loc(dot,slash);
Results
txt newTxt
------------------------------ --------------------
SDACR.hello.com SDACR
SDACR SDACR
SDACR\AIR SDACR\AIR
SDACR.hello.com\WATER SDACR\WATER

Print Substring before and after slash '/' from a string

I've a task to print substring from a string based on the occurence of slash '/' position in a string .Below i'm trying to show you one sample example how i want to get .
Declare #My_string = 'abc/def gh /ijk l/m/no p/qr
From the above string i want to print the substring value based on the position of slash occourence.
For Example: Sometimes i was asked to print the substring after the 2nd occourence of slah then i've to display ijk l and Sometimes i was asked to print the substring after the 3rd occourence then m should display if it is 1st then def gh vice versa .There is no specific occourence position it may vary based on need .How can i achieve it .Any help will be very thankful
Note: I want to achieve without using function .i know that it can be done by using split function .Is that possible to get without using it
One way to do it without using a function is to use a recursive cte.
This is not the way I would recommend splitting a string but since you insist on not using a function it's a reasonable alternative.
If you change your mind about splitting string function, you should read Aaron Bertrand's Split strings the right way – or the next best way and choose a split string function that would be easy to modify to return the item number as well.
Sample data:
DECLARE #My_string varchar(100) = 'abc/def gh /ijk l/m/no p/qr',
#SlashIndex int = 3
The CTE:
;WITH CTE AS
(
SELECT LEFT(#My_String, CHARINDEX('/', #My_String)-1) As Value,
0 As SlashIndex,
RIGHT(#My_String, LEN(#My_String) - CHARINDEX('/', #My_String)) As String
WHERE CHARINDEX('/', #My_String) > 0
OR LEN (#My_String) > 0
UNION ALL
SELECT LEFT(String, CASE WHEN CHARINDEX('/', String) > 0 THEN CHARINDEX('/', String) ELSE LEN(String) +1 END -1),
SlashIndex + 1,
RIGHT(String, LEN(String) - CASE WHEN CHARINDEX('/', String) > 0 THEN CHARINDEX('/', String) ELSE LEN(String) END)
FROM CTE
WHERE CHARINDEX('/', String) > 0
OR LEN(String) > 0
)
The Query:
SELECT Value
FROM CTE
WHERE SlashIndex = #SlashIndex
Result: m
You can see a live demo on rextester.
A simple way to get the 3rd position via XML
Example
Declare #My_string varchar(max)= 'abc/def gh /ijk l/m/no p/qr'
Select Pos3 = convert(xml,'<x>'+replace(#My_String,'/','</x><x>')+'</x>').value('/x[3]','varchar(100)')
Returns
Pos3
ijk l
created below function which will take 2 input first - your position and second will be your string and return the required output -
create function dbo.GetSubsting ( #StatPoint int , #Input_string varchar(1000) )
returns varchar(1000)
as
begin
Declare #len int , #idx int =1 , #section int = 1 , #output varchar(1000) = ''
DECLARE #tab table ( idx int identity(1,1 ), Val char(1) , section int )
select #len = len(#Input_string)
while #idx <= #len
begin
insert into #tab
select substring( #Input_string , #idx ,1) , #section
if substring( #Input_string , #idx ,1) = '/'
set #section = #section + 1
set #idx = #idx + 1
end
select #output = #output + Val from #tab where section = #StatPoint
select #output = replace(#output , '/' , '')
RETURN #output
end
go
select dbo.GetSubsting(3, 'abc/def gh /ijk l/m/no p/qr')
--OUTPUT
-- ijk l

SQL Server: Returning Results of Group By Based on Substring of Column

For more context on this question please see here.
I have a query which uses GROUP BY with a substring
use thedatabase;
declare #fromDate datetime = '2016-02-01 00:00:00.000';
declare #toDate datetime = '2016-02-02 23:59:59.999';
declare #source varchar(15) = 'server001';
DECLARE #countForType bigint;
DECLARE #totalForType decimal(30,8);
DECLARE #country varchar(10);
SELECT #countForType = count(*),
#totalForType = SUM(typeTable.amount),
#country =
case
when (charindex('[', typeTable.source) > 0 and charindex(']', typeTable.source) > 0)
then substring(typeTable.source, charindex('[', typeTable.source) +1, (charindex(']', typeTable.source) - 1) - charindex('[', typeTable.source))
else null
end
FROM
theTypeTable typeTable (nolock)
WHERE
typeTable.startDate > #fromDate
AND typeTable.startDate < #toDate
AND typeTable.source like #source
GROUP BY
case
when (charindex('[', typeTable.source) > 0 and charindex(']', typeTable.source) > 0)
then substring(typeTable.source, charindex('[', typeTable.source) +1, (charindex(']', typeTable.source) - 1) - charindex('[', typeTable.source))
else null
end
I'd like to be able to get this into a report format, either by looping through and print out values with PRINT or place the it all in a result set. Either would be fine.
From researching, there are two things that I could do here to compile a report:
Use a cursor and loop through the results, printing the values. I'm not sure how to go about doing this.
Get rid of the scalar variables and have this query return a result set. I've done this below. Below is the re-written query. However, it just returns a single row.
Returns a single row:
Select count(*) as countForType,
SUM(typeTable.amount) as totalForType,
case
when ( charindex('[', typeTable.source) > 0 and
charindex(']', typeTable.source) > 0)
then substring( typeTable.source,
charindex('[', typeTable.source) +1,
(charindex(']', typeTable.source) - 1) - charindex('[', typeTable.source))
else null
end
as country
FROM theTypeTable typeTable (nolock)
WHERE typeTable.startDate > #fromDate
AND typeTable.startDate < #toDate
AND typeTable.source like #source
GROUP BY
case
when ( charindex('[', typeTable.source) > 0 and
charindex(']', typeTable.source) > 0)
then substring( typeTable.source,
charindex('[', typeTable.source) +1,
(charindex(']', typeTable.source) - 1) - charindex('[', typeTable.source))
else null
end
Which returns something like:
countForType totalForType country
=========================================
590 82983909 NULL
Ideally, I'd like a report, either in a result set or printing the output, that has the following information:
countForType: 104
totalForType: 110000.00000000
country: en-US
countForType: 55
totalForType: 95000.00000000
country: de-CH
countForType: 25
totalForType: 5000.00000000
country: tr-TR
countForType: 30
totalForType: 10000.00000000
I would say you can use a bit scripting, like
SELECT 'countForType: ' & count (*) + '
totalForType: ' & SUM(typeTable.amount) as totalForType
FROM
....
Here after the last apostrophe in the line, I pressed enter, that will make the line change part of the query.
Execute the query in Text mode, by pressing Ctrl+T first and hope you will get the result.

find from a column having csv values in sql

I have a column (data) in database having values like
id name data
1 jatin 1,11,15
2 harsh 3,5,11
what can I do if I want to get the names of persons having 11 in data column??
thanks
On MS SQL Server you cannot use the FIND_IN_SET function, and would have to do something like this instead:
SELECT * FROM MyTable
WHERE ',' + Data + ',' LIKE '%,11,%'
I've added the commas in front and in the end of the Data column, to ensure that the LIKE operator works, even if "11" is at the beginning or the end of the string.
You can use this function:
CREATE FUNCTION CSV2Table (#var VARCHAR(max))
RETURNS TABLE
AS
RETURN (
-- Recursive CTE
WITH CSV(pop, variable) AS (
-- Base case
SELECT CASE
WHEN charindex(',', #var) > 0
THEN substring(#var, 1, charindex(',', #var) - 1)
ELSE #var
END,
CASE
WHEN charindex(',', #var) > 0
THEN substring(#var, charindex(',', #var) + 1, len(#var) - charindex(',', #var) + 1)
ELSE ''
END
UNION ALL
-- Recursive
SELECT CASE
WHEN charindex(',', CSV.variable) > 0
THEN substring(CSV.variable, 1, charindex(',', CSV.variable) - 1)
ELSE CSV.variable
END,
CASE
WHEN charindex(',', CSV.variable) > 0
THEN substring(CSV.variable, charindex(',', CSV.variable) + 1, len(CSV.variable) - charindex(',', CSV.variable) + 1)
ELSE ''
END
FROM CSV
WHERE len(CSV.variable) > 0
)
SELECT pop
FROM CSV
)
GO
and do something like:
SELECT * FROM <TABLE> WHERE EXISTS(SELECT TOP 1 1 FROM dbo.CSV2Table(data) WHERE POP = '11')

Resources