find from a column having csv values in sql - sql-server

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')

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 |

Split With Cross Apply function in SQL Server

I need to split a variable as, exp
declare #testString varchar(100)
set #testString = ' Agency=100|Org=2112|RepOrg=2112|SubOrg= |Fund=0137|Approp=6755|Object= |SubObject= |Activity= |Function= |Job= |ReportingCat= '
select
y.items
from
dbo.Split(#testString, '|') x
cross apply
dbo.Split(x.items, '=') y
Leads to error :
Msg 102, Level 15, State 1, Line 7
Incorrect syntax near '.'.
Not sure where I'm going wrong.
May be you need something like this:-
DECLARE #testString VARCHAR(100)
SET #testString =
' Agency=100|Org=2112|RepOrg=2112|SubOrg= |Fund=0137|Approp=6755|Object= |SubObject= |Activity= |Function= |Job= |ReportingCat= '
SELECT X.VALUE AS ACTUALVALUE,
SUBSTRING(
X.VALUE,
1,
CASE
WHEN CHARINDEX('=', X.VALUE) = 0 THEN LEN(X.VALUE)
ELSE CHARINDEX('=', X.VALUE) -1
END
) AS FIELD,
SUBSTRING(X.VALUE, CHARINDEX('=', X.VALUE) + 1, 10) AS VALUE
FROM string_split(#testString, '|') x
I have used the same function which you have used dbo.split. To get the output (Agency in one column and code in another), you can make use of substring along with char index which will help you to split into two columns.
Few changes I made to your script:
Changed the length from 100 to 250 as it was truncating the string, and
removed another cross apply as it was creating duplicates.
declare #testString varchar(250)
set #testString = 'Agency=100|Org=2112|RepOrg=2112|SubOrg=
|Fund=0137|Approp=6755|Object= |SubObject= |Activity= |Function= |Job= |ReportingCat='
select substring( (x.items),1,
case when CHARINDEX('=', x.items) = 0 then LEN(x.items)
else CHARINDEX('=', x.items) -1 end ) Agency ,
substring( (x.items),
case when CHARINDEX('=', x.items) = 0 then LEN(x.items)
else CHARINDEX('=', x.items) +1 end,len(x.items) -
case when CHARINDEX('=', x.items) = 0 then LEN(x.items)
else CHARINDEX('=', x.items)-1 end) as Code from dbo.split
(#testString, '|') x
It ran without error, and that function is here as Ben mentioned.
https://social.msdn.microsoft.com/Forums/en-US/bb2b2421-6587-4956-aff0-a7df9c91a84a/what-is-dbosplit?forum=transactsql
Output which I get:
Agency Code
Agency 100
Org 2112
RepOrg 2112
SubOrg
Fund 0137
Approp 6755
Object
SubObject
Activity
Function
Job
ReportingCat

Fix CHARINDEX query issues

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;

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.

Invalid length parameter passed to the LEFT or SUBSTRING function in row concatenation

i'm getting "Invalid length parameter passed to the LEFT or SUBSTRING function" error on this function, it takes numbers from strings like "Abracadabra {34}" or doesn't take if string has no "{}".
I have a "CASE", but it seems like it doesn't work properly.
Certainly it works perfectly with typical case - "Abracadabra {34}", but because of complicated condition in query i can't check this function with another cases.
Maybe you could find mistake, just by taking a look.
Please help me.
CREATE FUNCTION func (
#nidop int
)
RETURNS varchar
BEGIN
DECLARE #sreturn varchar(1000);
SET #sreturn = '';
SELECT
#sreturn = CASE CHARINDEX('{', wp.description)
WHEN 0 THEN #sreturn
ELSE
#sreturn + SUBSTRING(wp.description, CHARINDEX('{', wp.description) + 1,
CHARINDEX('}', wp.description) - CHARINDEX('{', wp.description) - 1) + ','
END
FROM
(/*some select*/) ttt, RB_WORKPLACE wp
WHERE wp.classified = ttt.ap
ORDER BY CASE WHEN CHARINDEX('{', wp.[description]) > 0 THEN
CONVERT(INT,
SUBSTRING(wp.[description],
CHARINDEX('{', wp.[description]) + 1,
CHARINDEX('}', wp.[description]) - CHARINDEX('{', wp.[description]) - 1)
)
ELSE 0
END;
SET #sreturn = SUBSTRING(#sreturn, 1, LEN(#sreturn) - 1)
RETURN #sreturn;
END;
I will try to give more information about issue. In this function i'm trying to migrate another function from Oracle:
create or replace function func(nidop in number) return varchar2 as
sreturn varchar2(1000);
begin
select listagg(to_number(substr(wp.description, instr(wp.description, '{') + 1,
instr(wp.description, '}') - instr(wp.description, '{') - 1)), ',') within group(order by to_number(substr(wp.description, instr(wp.description, '{') + 1, instr(wp.description, '}') - instr(wp.description, '{') - 1)))
into sreturn
from (/*some select*/) ttt, workplace wp
where wp.classified = ttt.ap;
return sreturn;
exception
when others then
return null;
end func;
Temporaly I changed this not-mine query, to "xml path('')" like query, thanks all for help. Only one item was because of returning varchar instead of varchar(50) for example.(In my case)
The error is coming from your ORDER BY clause. You do not have it wrapped in a condition (CASE or IIF) like you have in the SELECT. Change your ORDER BY to be the following:
ORDER BY CASE WHEN CHARINDEX('{', wp.[description]) > 0 THEN
CONVERT(INT,
SUBSTRING(wp.[description],
CHARINDEX('{', wp.[description]) + 1,
CHARINDEX('}', wp.[description]) - CHARINDEX('{', wp.[description]) - 1)
)
ELSE 0
END;
Also, it is not a good idea to specify the return type as simply VARCHAR without a length. The default width of a VARCHAR / NVARCHAR is either 1 or 30, depending on the context in which it is being stated. Since your local variable is declared as VARCHAR(1000), you should use the same 1000 for the declaration of the output type.

Resources