sql split after certain lenght of charcters to new row - sql

I have a record in one of the column and it looks like below:
132007700013213860001321264000
I want to split without using any delimiter based on char length of 10. After every 10 characters i need the records in a new line. Like below
1320077000
1321386000
1321264000

If the string is fixed width you could use 3 SUBSTRING functions and CROSS APPLY to 'unpivot' the 3 columns into 1 column, called 'new_col'.
declare #txtTable table(txt char(30) not null)
insert #txtTable(txt) values
('132007700013213860001321264000'),
('999999999999999999999999999999');
select u.*
from #txtTable t
cross apply (values (substring(t.txt, 1, 10),
substring(t.txt, 11, 10),
substring(t.txt, 21, 10))) v(c1, c2, c3)
cross apply (values (v.c1),(v.c2),(v.c3)) u(new_col);
new_col
1320077000
1321386000
1321264000
9999999999
9999999999
9999999999

You can use a recursive CTE:
with cte as (
select left(col, 10) as x10, convert(varchar(max), stuff(col, 1, 10, '')) as rest
from t
union all
select left(rest, 10), stuff(rest, 1, 10, '')
from cte
where rest <> ''
)
select x10
from cte;
Here is a db<>fiddle.

All credit goes to Gordon Linoff. I just added casting to make it work.
Somehow the DB Fiddle sample worked, but in SSMS it was an error.
SQL
DECLARE #tbl TABLE (col VARCHAR(30));
INSERT #tbl (col) VALUES
('132007700013213860001321264000'),
('999999999999999999999999999999');
WITH cte as
(
SELECT CAST(LEFT(col, 10) AS VARCHAR(10)) as x10, CAST(stuff(col, 1, 10, '') AS VARCHAR(MAX)) as rest
FROM #tbl
UNION ALL
SELECT CAST(LEFT(rest, 10) AS VARCHAR(10)), STUFF(rest, 1, 10, '')
FROM cte
WHERE rest <> ''
)
SELECT x10
FROM cte;

Related

Add character in front and at the end of each character

In SQL I want to add 0 in front and , at the end of each character.
Example: A30F1 -> 0A,03,00,0F,01
I don't want to use cursor if possible.
Thanks!
EIDT:
I apologize for not asking the most appropriate question at the beginning.
In short, I have a table and for each value in the column name I have to convert it to the desired format. For example, we have a #Temp table:
CREATE TABLE #Temp (id INT, name VARCHAR(25))
INSERT INTO #Temp VALUES (1, 'A30F1'), (2, 'B51R9'), (3, 'L1721')
SELECT * FROM #Temp
One method would be to use a Tally to split the string into it's individual characters, and then use concatenation to add the 0 to the start, and STRING_AGG to comma delimit the results:
DECLARE #YourValue varchar(5) = 'A30F1';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (LEN(#YourValue))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --Up to 100 characters, add more cross joins for more characters
SELECT STRING_AGG(CONCAT('0',SS.C),',') WITHIN GROUP (ORDER BY T.I) AS NewString
FROM (VALUES(#YourValue))V(YourValue)
CROSS JOIN Tally T
CROSS APPLY (VALUES(SUBSTRING(V.YourValue,T.I,1)))SS(C);
It appears this is meant to be against a table, not a single value. This needs, however, very few changes to work against a table:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (SELECT MAX(LEN(YourColumn)) FROM dbo.YourTable)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --Up to 100 characters, add more cross joins for more characters
SELECT STRING_AGG(CONCAT('0',SS.C),',') WITHIN GROUP (ORDER BY T.I) AS NewString
FROM dbo.YourTable YT
JOIN Tally T ON LEN(YT.YourColumn) >= T.I
CROSS APPLY (VALUES(SUBSTRING(YT.YourColumn,T.I,1)))SS(C)
GROUP BY YT.YourColumn;
db<>fiddle
I solved the simplest possible with a few variables, WHILE and SUBSTRING
DECLARE #var VARCHAR(20) = 'A30F1', #i INT = 1, #res NVARCHAR(20)
WHILE (#i <= LEN(#var))
BEGIN
SET #res = #res + '0' + SUBSTRING(#var, #i, 1) + ','
SET #i = #i + 1
END
SELECT LEFT(#res, LEN(#res) - 1) output
Check demo on DB<>FIDDLE.
Original answer:
A recursive CTE and a STRING_AGG() call is also an option (SQL Server 2017+ is needed):
DECLARE #text varchar(max) = 'A30F1';
WITH rCTE AS
(
SELECT 1 AS CharacterPosition, SUBSTRING(#text, 1, 1) AS Character
UNION ALL
SELECT CharacterPosition + 1, SUBSTRING(#text, CharacterPosition + 1, 1)
FROM rCTE
WHERE CharacterPosition < LEN(#text)
)
SELECT STRING_AGG('0' + Character, ',') WITHIN GROUP (ORDER BY CharacterPosition)
FROM rCTE
OPTION (MAXRECURSION 0);
Update:
You need a different statement, if the names are stored in a table, again using recursion and STRING_AGG():
Table:
CREATE TABLE #Temp (id INT, name VARCHAR(25))
INSERT INTO #Temp VALUES (1, 'A30F1'), (2, 'B51R9'), (3, 'L1721')
Statement:
; WITH rCTE AS (
SELECT
t.id AS id,
LEFT(t.name, 1) AS Character,
STUFF(t.name, 1, 1, '') AS CharactersRemaining,
1 AS CharacterPosition
FROM #Temp t
UNION ALL
SELECT
r.id,
LEFT(r.CharactersRemaining, 1),
STUFF(r.CharactersRemaining, 1, 1, ''),
CharacterPosition + 1
FROM rCTE r
WHERE LEN(r.CharactersRemaining) > 0
)
SELECT
id,
STRING_AGG('0' + Character, ',') WITHIN GROUP (ORDER BY CharacterPosition) AS name
FROM rCTE
GROUP BY id
OPTION (MAXRECURSION 0);
Result:
id name
1 0A,03,00,0F,01
2 0B,05,01,0R,09
3 0L,01,07,02,01
If you are only applying this to English alphabet characters and digits as in your example you could do this.
CREATE TABLE #Temp (id INT, name VARCHAR(25))
INSERT INTO #Temp VALUES (1, 'A30F1'), (2, 'B51R9'), (3, 'L1721'), (4, 'A')
SELECT SUBSTRING(REPLACE(
0x00 + CAST(CAST(name AS NVARCHAR(25)) AS BINARY(50)), CHAR(0), '0,')
, 3
, LEN(name) * 3 - 1)
FROM #Temp
returns
0A,03,00,0F,01
0B,05,01,0R,09
0L,01,07,02,01
0A
This takes advantage of the fact that the binary representation of the nvarchar and varchar is the same for this limited character set except for padding out with 0x00
'A30F1' -> 0x4133304631
N'A30F1' -> 0x41003300300046003100

Some numbers are getting truncated. I would like to pull all numbers

I'm trying to parse only numbers from a string. My code must be pretty close, but something is off here, because several numbers in the last string are being truncated, although the first two strings seem fine.
Here is my code.
Drop Table SampleData
Create table SampleData
(id int, factor varchar(100))
insert into #source_Policy values (1 ,'AAA 1.058 (Protection Class)')
insert into #source_Policy values (2, 'BBB0.565 (Construction) ')
insert into #source_Policy values ( 3, 'CCCCC 1.04890616 (Building Limit Rel')
Select *
From SampleData
;with processTable as (
select id, factor, num
from SampleData
cross apply (
select (select C + ''
from (select N, substring(factor, N, 1) C from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) Num(N) where N<=datalength(factor)) t
where PATINDEX('%[0-9.]%',C)> 0
order by N
for xml path(''))
) p0 (num)
)
SELECT id, factor, num
FROM processTable
This is the result that I get.
In the num column, instead of 1.04, I would like to see the full precision, so: 1.04890616
I would think something like this:
select s.*, v2.numstr
from sampledata s cross apply
(values (stuff(factor, 1, patindex('%[0-9]%', factor) - 1, ''))) v(str) cross apply
(values (left(v.str, patindex('%[^0-9.]%', v.str + 'x') - 1))) v2(numstr);
Here is a SQL Fiddle.

SQL Query to parse numbers from name

The DBMS in this case is SQL Server 2012.
I need a SQL query that will grab just the numbers from a device name. I've got devices that follow a naming scheme that SHOULD look like this:
XXXnnnnn
or
XXXnnnnn-XX
Where X is a letter and n is a number which should be left padded with 0's where appropriate. However, not all of the names are properly padded in this way.
So, imagine you have a column that looks something like this:
Name
----
XXX01234
XXX222
XXX0390-A2
XXX00965-A1
I need an SQL query that will return results from this example column as follows.
Number
------
01234
00222
00390
00965
Anyone have any thoughts? I've tried things like casting the name first as a float and then as an int, but to be honest, I'm just not skilled enough with SQL yet to find the solution.
Any help is greatly appreciated!
SQL Server does not have great string parsing functions. For your particular example, I think a case statement might be the simplest approach:
select (case when number like '___[0-9][0-9][0-9][0-9][0-9]%'
then substring(number, 4, 5)
when number like '___[0-9][0-9][0-9][0-9]%'
then '0' + substring(number, 4, 4)
when number like '___[0-9][0-9][0-9]%'
then '00' + substring(number, 4)
when number like '___[0-9][0-9]%'
then '000' + substring(number, 4, 2)
when number like '___[0-9][0-9]%'
then '0000' + substring(number, 4, 1)
else '00000'
end) as EmbeddedNumber
This might work :
SELECT RIGHT('00000'
+ SUBSTRING(Col, 1, ISNULL(NULLIF((PATINDEX('%-%', Col)), 0) - 1, LEN(Col))), 5)
FROM (SELECT REPLACE(YourColumn, 'XXX', '') Col
FROM YourTable)t
SQLFIDDLE
This will work even when XXX can be of different len:
DECLARE #t TABLE ( n NVARCHAR(50) )
INSERT INTO #t
VALUES ( 'XXXXXXX01234' ),
( 'XX222' ),
( 'X0390-A2' ),
( 'XXXXXXX00965-A1' )
SELECT REPLICATE('0', 5 - LEN(n)) + n AS n
FROM ( SELECT SUBSTRING(n, PATINDEX('%[0-9]%', n),
CHARINDEX('-', n + '-') - PATINDEX('%[0-9]%', n)) AS n
FROM #t
) t
Output:
n
01234
00222
00390
00965
If the first 3 chars are always needed to be removed, then you can do something like that (will work if the characters will start only after '-' sign):
DECLARE #a AS TABLE ( a VARCHAR(100) );
INSERT INTO #a
VALUES
( 'XXX01234' ),
( 'XXX222' ),
( 'XXX0390-A2' ),
( 'XXX00965-A1' );
SELECT RIGHT('00000' + SUBSTRING(a, 4, CHARINDEX('-',a+'-')-4),5)
FROM #a
-- OUTPUT
01234
00222
00390
00965
Another option (will extract numbers after first 3 characters):
SELECT
RIGHT('00000' + LEFT(REPLACE(a, LEFT(a, 3), ''),
COALESCE(NULLIF(PATINDEX('%[^0-9]%',
REPLACE(a, LEFT(a, 3), '')),
0) - 1,
LEN(REPLACE(a, LEFT(a, 3), '')))), 5)
FROM
#a;
-- OUTPUT
01234
00222
00390
00965

Selecting between quotes (") in SQL Server 2012

I have a table holding IDs in one column and a string in the second column like below.
COLUMN01 COLUMN02
----------------------------------------------------------------------------------
1 abc"11444,12,13"efg"14,15"hij"16,17,18,19"opqr
2 ahsdhg"21,22,23"ghshds"24,25"fgh"26,27,28,28"shgshsg
3 xvd"3142,32,33"hty"34,35"okli"36,37,38,39"adfd
Now I want to have the following result
COLUMN01 COLUMN02
-----------------------------------------------------------
1 11444,12,13,14,15,16,17,18,19
2 21,22,23,24,25,26,27,28,28
3 3142,32,33,34,35,36,37,38,39
How can I do that?
Thanks so much
Here is one way (maybe not the best, but it seems to work). I am NOT a SQL guru...
First, create this SQL Function. It came from: Extract numbers from a text in SQL Server
create function [dbo].[GetNumbersFromText](#String varchar(2000))
returns table as return
(
with C as
(
select cast(substring(S.Value, S1.Pos, S2.L) as int) as Number,
stuff(s.Value, 1, S1.Pos + S2.L, '') as Value
from (select #String+' ') as S(Value)
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
union all
select cast(substring(S.Value, S1.Pos, S2.L) as int),
stuff(S.Value, 1, S1.Pos + S2.L, '')
from C as S
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
where patindex('%[0-9]%', S.Value) > 0
)
select Number
from C
)
Then, you can do something like this to get the results you were asking for. Note that I broke the query up into 3 parts for clarity. And, obviously, you don't need to declare the table variable and insert data into it.
DECLARE #tbl
TABLE (
COLUMN01 int,
COLUMN02 varchar(max)
)
INSERT INTO #tbl VALUES (1, 'abc"11444,12,13"efg"14,15"hij"16,17,18,19"opqr')
INSERT INTO #tbl VALUES (2, 'ahsdhg"21,22,23"ghshds"24,25"fgh"26,27,28,28"shgshsg')
INSERT INTO #tbl VALUES (3, 'xvd"3142,32,33"hty"34,35"okli"36,37,38,39"adfd')
SELECT COLUMN01, SUBSTRING(COLUMN02, 2, LEN(COLUMN02) - 1) as COLUMN02 FROM
(
SELECT COLUMN01, REPLACE(COLUMN02, ' ', '') as COLUMN02 FROM
(
SELECT COLUMN01, (select ',' + number as 'data()' from dbo.GetNumbersFromText(Column02) for xml path('')) as COLUMN02 FROM #tbl
) t
) tt
GO
output:
COLUMN01 COLUMN02
1 11444,12,13,14,15,16,17,18,19
2 21,22,23,24,25,26,27,28,28
3 3142,32,33,34,35,36,37,38,39
I know you want to do it using SQL. But ones I had nearly the same problem and getting this data to a string using a php or another language, than parsing is a way to do it. For example, you can use this kind of code after receiving the data into a string.
function clean($string) {
$string = str_replace(' ', '-', $string); // Replaces all spaces with hyphens.
$string = preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars.
return preg_replace('/-+/', '-', $string); // Replaces multiple hyphens with single one.
}
For more information you might want to look at this post that I retrieved the function: Remove all special characters from a string
As I said this is an easy way to do it, I hope this could help.

sql query complex

I have table where in a table called test which have 4 fields.one field named as listing, I have 1,2,3,4,5,6 multiple values separated by comma, I need to check whether in that table and in that particular field an id say 4 is there or not.. by a sql query.
You database design is wrong, that's why you have problems querying the data. You should have the values in a separate table, so that teach value is in it's own field. Then it would be easy to find the records:
select t.testId
from test t
inner join listing l on l.testId = t.testId
where l.id = 4
Now you have to use some ugly string comparison to find the records:
select testId
from test
where ','+listing+',' like '%,4,%'
You can try
SELECT *
FROM YourTable
WHERE REPLACE(Col, ' ', '') LIKE '4,%' --Starts with
OR REPLACE(Col, ' ', '') LIKE '%,4' --Ends with
OR REPLACE(Col, ' ', '') LIKE '%,4,%' --Contains
OR REPLACE(Col, ' ', '') = '4' --Equals
Just as a matter of interest, have a look at this
DECLARE #delimiter NVARCHAR(5),
#Val INT
SELECT #Val = 40
SELECT #delimiter = ','
DECLARE #YourTable TABLE(
ID INT,
Vals VARCHAR(50)
)
INSERT INTO #YourTable (ID,Vals) SELECT 1, '1,2,3,4,5,6,7,8'
DECLARE #TempTable TABLE(
ID INT,
Vals XML
)
INSERT INTO #TempTable
SELECT ID,
CAST('<d>' + REPLACE(Vals, #delimiter, '</d><d>') + '</d>' AS XML)
FROM #YourTable
SELECT *
FROM #TempTable tt
WHERE EXISTS(
SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM tt.Vals.nodes('/d') T(split)
WHERE T.split.value('.', 'nvarchar(max)') = #Val
)
The common approach is to parse the list into a table variable or table-valued function, then either join against the table, or use an EXISTS sub-query.
There are lots of examples on how to do this:
http://www.bing.com/search?setmkt=en-US&q=SQL+parse+list+into+table
You could use an instring function in the where clause and in the select clause:
Oracle:
select substr(column, instr(column, '1', 1), 1)
where instr(column, '1', 1) > 0
works if you want a single value. Alternatively you can use a combination of case or decode statements to create a single column for each possible value:
select
decode(instr(column, '1', 1), 0, substr(column, instr(column, '1', 1), 1), null) c1,
decode(instr(column, '2', 1), 0, substr(column, instr(column, '2', 1), 1), null) c2,
decode(instr(column, '3', 1), 0, substr(column, instr(column, '3', 1), 1), null) c3
The beauty of this approach for such a poorly normalised set of data is you can save this as a view and then run SQL on that, so if you save the above you could use:
select c1, c2 from view where c1 is not null or c2 is not null
NB. In other dbms you might have to use different syntax, possibly the case rather decode statement
If you need to find 4 and only 4 (ie not 14 or 24 or 40 etc) you should use
SELECT * FROM foo WHERE col LIKE '%, 4,%'
or
SELECT * FROM foo WHERE col LIKE '%,4,%'
if there are no spaces between the commas and numbers
How about this?
Select * From Foo Where Col like '%4%'