SQL SERVER 2014 - Split String & Convert to INT - sql

I have a string
1,2,3|5
After Split with | I have
1,2,3, 5
Now I want to convert 1,2,3 to INT. How can I achieve this?
Following is my query
Declare #tmpReferContentRule = "1,2,3|5"
SELECT items
FROM splitbystring(#tmpReferContentRule,'|')
WHERE id = 1
I want to use it in
SELECT name
FROM tmptable
WHERE id in(SELECT items FROM splitbystring(#tmpReferContentRule,'|'))
id is type of integer
The above query throws an error
1,2,3 could not be converted to int.
Please suggest a solution.

You can try this -
Declare #tmpReferContentRule varchar(20) = '1,2,3|5'
;with cte
AS
(
SELECT items as subitem from dbo.splitbystring(#tmpReferContentRule,'|')
)
SELECT name FROM tmptable WHERE id IN
(
SELECT t.items FROM cte
CROSS APPLY (SELECT * from dbo.splitbystring (subitem,',')) t
)

Try this methods
select name from tmptable as t1 inner join (#tmpReferContentRule,'|') as t2 on
','+t2.items+',' like '%,'+cast(t1.id as varchar(10)+',%'
where t2.id=1

You can convert that string into table with the help of xml:
DECLARE #string nvarchar(10) = '1,2,3|5',
#xml xml
SELECT #xml = CAST('<r><a>' + REPLACE(REPLACE(#string,',','</a><a>'),'|','</a><b>') +'</b></r>' as xml)
SELECT t.v.value('.','int') as id
FROM #xml.nodes('/r/a') as t(v)
Output:
id
1
2
3

I see you have been already using a split string function
I have a similar split string sql function, I used it as follows twice in your script
Declare #tmpReferContentRule varchar(max) = '1,2,3|5'
SELECT *
FROM Table1 t
inner join (
select
s2.val
from dbo.Split(#tmpReferContentRule,'|') s1
cross apply dbo.Split(s1.val,',') s2
WHERE
s1.id = 1
) s on t.id = s.val

Related

Simple query that should be working is not

I have a simple query:
declare #manual varchar(80) = '''Discount'',''Misc Charges'''
select *
from #Final
where charge_type in (#manual)
Now I've gone as far as verifying my declared variable is setup correctly by using the PRINT command as follows: PRINT '''Discount'',''Misc Charges''' and it in fact returns as I would expect: 'Discount','Misc Charges'.
However, when I run this query, I get no results.
If I instead simply use:
select *
from #Final
where charge_type in ('Discount','Misc Charges')
Then no problem, I get my results. I'm sure I'll kick myself once I get the answer, but as of right now, this is just not making sense. No errors, it's just not giving me my columns without any rows as if there's no data. What am I missing?
Because
IN ('''Discount'',''Misc Charges''')
is the same as
= '''Discount'',''Misc Charges'''
In other words, that is one single string that contains a bunch of escaped string delimiters, not a comma-separated list of individual string values. Which is why you can do this without SQL Server barfing:
PRINT '''Discount'',''Misc Charges''';
What you want is:
declare #manual varchar(80) = 'Discount,Misc Charges';
select f.*
from #Final AS f
INNER JOIN STRING_SPLIT(#manual, ',') AS s
ON f.charge_type = s.value;
However that can fail on compatibility_level < 130, in which case:
declare #manual varchar(80) = 'Discount,Misc Charges';
select f.*
from #Final AS f
INNER JOIN
OPENJSON('["' + REPLACE(#manual, ',', '","') + '"]') AS s
ON f.charge_type = s.value;
In the latter case you can make the query itself a little nicer by using slightly different jacked-up strings in the variable declaration:
declare #manual varchar(80) = '["Discount","Misc Charges"]';
select f.*
from #Final AS f
INNER JOIN
OPENJSON(#manual) AS s ON f.charge_type = s.value;
Or if you are on an older version and you really are hand-crafting these strings inline, you can use a table variable or CTE like #SMor suggested.
Table variable:
DECLARE #d table(str varchar(32));
INSERT #d VALUES('Discount'),('Misc Charges');
SELECT f.*
from #Final AS f
INNER JOIN #d AS d
ON f.charge_type = d.str;
CTE:
;WITH cte AS
(
SELECT str = 'Discount'
UNION ALL
SELECT str = 'Misc Charges'
)
SELECT f.*
from #Final AS f
INNER JOIN cte
ON f.charge_type = cte.str;
If you'll have more values at some point, it tips to writing a table constructor instead of multiple UNION ALLs, e.g.
;WITH cte AS
(
SELECT str FROM
(
VALUES('Discount','Misc Charges')
) AS s(str)
)
SELECT f.*
from #Final AS f
INNER JOIN cte
ON f.charge_type = cte.str;
You can use just use your list of values as comma seperated string & then use STRING_SPLIT.
declare #manual varchar(80) = 'Discount,Misc Charges'
select *from #Final
where charge_type in (SELECT * from STRING_SPLIT(#manual,',))
Here is to to do in SQL Server 2016 onwards.
SQL
DECLARE #manual VARCHAR(80) = 'Discount,Misc Charges';
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, charge_type VARCHAR(30));
INSERT INTO #tbl (charge_type) VALUES
('Discount'),
('No Discount'),
('Misc Charges');
SELECT *
FROM #tbl
WHERE charge_type in (SELECT value FROM STRING_SPLIT(#manual, ','))

SQL Server : searching value doesn't have entry in table

I have a table which has the following values:
ID | Name
---------------
1 | Anavaras
2 | Lamurep
I need a query which outputs the value which doesn't have entry in the table.
For e.g:
If my where clause contains id in('1','2','3','4'), should produce output has
3 |
4 |
for the above entries in the table.
You would put this into a "derived table" and use left join or a similar construct:
select v.id
from (values(1), (2), (3), (4)) v(id) left join
t
on t.id = v.id
where t.id is null;
Something like this:
"SELECT id FROM table WHERE name IS NULL"
I'd assume?
First you need to split your in to a table. Sample split function is here:
CREATE FUNCTION [dbo].[split]
(
#str varchar(max),
#sep char
)
RETURNS
#ids TABLE
(
id varchar(20)
)
AS
BEGIN
declare #pos int,#id varchar(20)
while len(#str)>0
begin
select #pos = charindex(#sep,#str + #sep)
select #id = LEFT(#str,#pos),#str = SUBSTRING(#str,#pos+1,10000000)
insert #ids(id) values(#id)
end
RETURN
END
Then you can use this function.
select id from dbo.split('1,2,3,4,5',',') ids
left join myTable t on t.id=ids.id
where t.id is null
-- if table ID is varchar then '''1'',''2'',''3'''

How to compare two sub queries in one sql statement

I have a table tbl_Country, which contains columns called ID and Name. The Name column has multiple country names separated by comma, I want the id when I pass multiple country names to compare with Name column values. I am splitting the country names using a function - the sample query looks like this:
#country varchar(50)
SELECT *
FROM tbl_Country
WHERE (SELECT *
FROM Function(#Country)) IN (SELECT *
FROM Function(Name))
tbl_country
ID Name
1 'IN,US,UK,SL,NZ'
2 'IN,PK,SA'
3 'CH,JP'
parameter #country ='IN,SA'
i have to get
ID
1
2
NOTE: The Function will split the string into a datatable
Try this
SELECT * FROM tbl_Country C
LEFT JOIN tbl_Country C1 ON C1.Name=C.Country
Try this:
SELECT *
FROM tbl_Country C
WHERE ',' + #country + ',' LIKE '%,' + C.Name + ',%';
Basically, by specifying multiple values in a single column, you are violating the 1st NF. Therefore, the following might not be a good approach but provides the solution that you are looking for:
declare #country varchar(50)= 'IN,SA'
declare #counterend int
declare #counterstart int =1
declare #singleCountry varchar(10)
set #counterend = (select COUNT(*) from fnSplitStringList(#country))
create table #temp10(
id int
,name varchar(50))
while #counterstart<= #counterend
begin
;with cte as (
select stringliteral country
, ROW_NUMBER() over (order by stringliteral) countryseq
from fnSplitStringList(#country))
select #singleCountry = (select country FROM cte where countryseq=#counterstart)
insert into #temp10(id, name)
select * from tbl_country t1
where not exists (select id from #temp10 t2 where t1.id=t2.id)
and name like '%' + #singleCountry +'%'
set #counterstart= #counterstart+1
end
select * from #temp10
begin drop table #temp10 end
How it works: It splits the passed string and ranks it. Afterwards, it loops through all the records for every single Value(country) produced and inserts them into temptable.
try this,
select a.id FROM tbl_Country a inner join
(SELECT country FROM dbo.Function(#Country)) b on a.name=b.country

T-SQL - multiple comma-separated values on a single line

I have table (T-SQL) with 2 attributes - Code and Range.
Code Range
-------------------- ----------
5000_RANGE 5001..5003
5001 NULL
5002 NULL
5003 NULL
5802 NULL
5802_RANGE 5802..5804
5803 NULL
5804 NULL
6401 NULL
I'm trying to write a simple query to get the Code values with '_RANGE' postfix and the Code values (separated by commas) specified by Range attribute on a single line.
Code Range
-------------------- --------------
5000_RANGE 5001,5002,5003
5802_RANGE 5802,5803,5804
What is the best solution? Maybe somehow by using XML Path()?
You can get the list using a self-join:
select range.code, c.code
from (select code, range
from t
where code like '%RANGE'
) range left outer join
(select t.*
from t
where code not like '%RANGE'
) c
on c.code between left(range.range, 4) and right(range.range, 4)
Getting them into a comma separate list depends on the database. Here is the method in MySQL:
select range.code, group_concat(c.code)
from (select code, range
from t
where code like '%RANGE'
) range left outer join
(select t.*
from t
where code not like '%RANGE'
) c
on c.code between left(range.range, 4) and right(range.range, 4)
group by range.code
Try this. Create a function like below:
ALTER FUNCTION GetRangeText
(
#Value VARCHAR(50)
) RETURNS VARCHAR(100)
AS
BEGIN
DECLARE #Start AS INT
DECLARE #End AS INT
DECLARE #RangeText AS VARCHAR(200)
SET #RangeText = ''
SET #Start = CAST(SUBSTRING(#Value, 0, CHARINDEX('...', #Value)) AS INT)
SET #End = CAST(SUBSTRING(#Value, CHARINDEX('...', #Value) + 3, LEN(#Value)) AS INT)
WHILE #Start <= #End
BEGIN
SET #RangeText = #RangeText + CAST(#Start AS VARCHAR(100)) + ','
SET #Start = #Start + 1
END
RETURN #RangeText
END
Consume the function in the SELECT query like below
SELECT Code, dbo.GetRangeText(Range) FROM Table1 WHERE Code LIKE '%_RANGE'
This will give the excepted output.
You didn't specify which DBMS you are using. If you are using MySQL you could use a query like this:
SELECT
Code,
GROUP_CONCAT(SUBSTRING_INDEX(rng, '..', 1)+numbers.digit) ranges
FROM
(SELECT 0 digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL
SELECT 4 UNION ALL SELECT 4 UNION ALL SELECT 5) numbers
INNER JOIN
codes
ON SUBSTRING_INDEX(rng, '..', -1)-SUBSTRING_INDEX(rng, '..', 1)>=numbers.digit
WHERE
rng like '%..%'
GROUP BY
Code
Which gives exatly the result you need.
Please see fiddle here. It can be improved to support larger ranges.
Something like
SELECT Code, Range FROM code_table WHERE Code LIKE "%_RANGE"
Not sure about presenting the range how you like.

T-Sql count string sequences over multiple rows

How can I find subsets of data over multiple rows in sql?
I want to count the number of occurrences of a string (or number) before another string is found and then count the number of times this string occurs before another one is found.
All these strings can be in random order.
This is what I want to achieve:
I have one table with one column (columnx) with data like this:
A
A
B
C
A
B
B
The result I want from the query should be like this:
2 A
1 B
1 C
1 A
2 B
Is this even possible in sql or would it be easier just to write a little C# app to do this?
Since, as per your comment, you can add a column that will unambiguously define the order in which the columnx values go, you can try the following query (provided the SQL product you are using supports CTEs and ranking functions):
WITH marked AS (
SELECT
columnx,
sortcolumn,
grp = ROW_NUMBER() OVER ( ORDER BY sortcolumn)
- ROW_NUMBER() OVER (PARTITION BY columnx ORDER BY sortcolumn)
FROM data
)
SELECT
columnx,
COUNT(*)
FROM marked
GROUP BY
columnx,
grp
ORDER BY
MIN(sortcolumn)
;
You can see the method in work on SQL Fiddle.
If sortcolumn is an auto-increment integer column that is guaranteed to have no gaps, you can replace the first ROW_NUMBER() expression with just sortcolumn. But, I guess, that cannot be guaranteed in general. Besides, you might indeed want to sort on a timestamp instead of an integer.
I dont think you can do it with a single select.
You can use AdventureWorks cursor:
create table my_Strings
(
my_string varchar(50)
)
insert into my_strings values('A'),('A'),('B'),('C'),('A'),('B'),('B') -- this method will only work on SQL Server 2008
--select my_String from my_strings
declare #temp_result table(
string varchar(50),
nr int)
declare #myString varchar(50)
declare #myLastString varchar(50)
declare #nr int
set #myLastString='A' --set this with the value of your FIRST string on the table
set #nr=0
DECLARE string_cursor CURSOR
FOR
SELECT my_string as aux_column FROM my_strings
OPEN string_cursor
FETCH NEXT FROM string_cursor into #myString
WHILE ##FETCH_STATUS = 0 BEGIN
if (#myString = #myLastString) begin
set #nr=#nr+1
set #myLastString=#myString
end else begin
insert into #temp_result values (#myLastString, #nr)
set #myLastString=#myString
set #nr=1
end
FETCH NEXT FROM string_cursor into #myString
END
insert into #temp_result values (#myLastString, #nr)
CLOSE string_cursor;
DEALLOCATE string_cursor;
select * from #temp_result
Result:
A 2
B 1
C 1
A 1
B 2
Try this :
;with sample as (
select 'A' as columnx
union all
select 'A'
union all
select 'B'
union all
select 'C'
union all
select 'A'
union all
select 'B'
union all
select 'B'
), data
as (
select columnx,
Row_Number() over(order by (select 0)) id
from sample
) , CTE as (
select * ,
Row_Number() over(order by (select 0)) rno from data
) , result as (
SELECT d.*
, ( SELECT MAX(ID)
FROM CTE c
WHERE NOT EXISTS (SELECT * FROM CTE
WHERE rno = c.rno-1 and columnx = c.columnx)
AND c.ID <= d.ID) AS g
FROM data d
)
SELECT columnx,
COUNT(1) cnt
FROM result
GROUP BY columnx,
g
Result :
columnx cnt
A 2
B 1
C 1
A 1
B 2