select string between 3rd and 4th pipe delimiter - sql

I have a column that contains values such as
Column
Asset|Class1|Category1|Group1|Account1
Expense|Class23|Category23|Group23|Account23
I want to select the string between 3rd and 4th occurrence of my pipe delimiter, how can I achieve this?
I've tried the PARSENAME and charindex+stuff function, but they have limitations, like max 128 characters. Also our SQL server has limited regex support. Any ideas?
SELECT REVERSE(PARSENAME(REVERSE(replace(LTRIM(Column), '|', '.')), 3))
My select need to return:
Group1
Group23

Perhaps this will help
Example
Declare #YourTable table (ID int,[Column] varchar(max))
Insert Into #YourTable values
(1,'Asset|Class1|Category1|Group1|Account1')
,(2,'Expense|Class23|Category23|Group23|Account23')
Select ID
,SomeValue = convert(xml,'<x>' + replace([Column],'|','</x><x>')+'</x>').value('/x[3]','varchar(100)')
From #YourTable
Returns
ID SomeValue
1 Category1
2 Category23

You can also use STRING_SPLIT() if you have 2016+
CREATE TABLE T(
ID INT IDENTITY(1,1),
Str VARCHAR(45)
);
INSERT INTO T(Str) VALUES
('Asset|Class1|Category1|Group1|Account1'),
('Expense|Class23|Category23|Group23|Account23');
SELECT V Str
FROM (
SELECT Value V,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ID) RN
FROM T CROSS APPLY STRING_SPLIT(Str, '|')
) TT
WHERE RN = 3;
Returns:
Str
---------
Category1
Category23

Related

SQL Server stored procedure looping through a comma delimited cell

I am trying to figure out how to go about getting the values of a comma separated string that's present in one of my cells.
This is the query I current am trying to figure out in my stored procedure:
SELECT
uT.id,
uT.permissions
FROM
usersTbl AS uT
INNER JOIN
usersPermissions AS uP
/*Need to loop here I think?*/
WHERE
uT.active = 'true'
AND
uT.email = 'bbarker#thepriceisright.com'
The usersPermissions table looks like this:
And so a row in the usersTbl table looks like this for permissions:
1,3
I need to find a way to loop through that cell and get each number and place the name ****, in my returned results for the usersTbl.permissions.
So instead of returning this:
Name | id | permissions | age |
------------------------------------
Bbarker | 5987 | 1,3 | 87 |
It needs to returns this:
Name | id | permissions | age |
------------------------------------
Bbarker | 5987 | Read,Upload | 87 |
Really just replacing 1,3 with Read,Upload.
Any help would be great from a SQL GURU!
Reworked query
SELECT
*
FROM
usersTbl AS uT
INNER JOIN
usersPermissionsTbl AS uPT
ON
uPT.userId = uT.id
INNER JOIN
usersPermissions AS uP
ON
uPT.permissionId = uP.id
WHERE
uT.active='true'
AND
uT.email='bBarker#thepriceisright.com'
I agree with all of the comments... but strictly trying to do what you want, here's a way with a splitter function
declare #usersTbl table ([Name] varchar(64), id int, [permissions] varchar(64), age int)
insert into #usersTbl
values
('Bbarker',5987,'1,3',87)
declare #usersTblpermissions table (id int, [type] varchar(64))
insert into #usersTblpermissions
values
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')
;with cte as(
select
u.[Name]
,u.id as UID
,p.id
,p.type
,u.age
from #usersTbl u
cross apply dbo.DelimitedSplit8K([permissions],',') x
inner join #usersTblpermissions p on p.id = x.Item)
select distinct
[Name]
,UID
,age
,STUFF((
SELECT ',' + t2.type
FROM cte t2
WHERE t.UID = t2.UID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
from cte t
Jeff Moden Splitter
CREATE FUNCTION [dbo].[DelimitedSplit8K] (#pString VARCHAR(8000), #pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
/* "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
enough to cover VARCHAR(8000)*/
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
GO
First, you should read Is storing a delimited list in a database column really that bad?, where you will see a lot of reasons why the answer to this question is Absolutely yes!
Second, you should add a table for user permissions since this is clearly a many to many relationship.
Your tables might look something like this (pseudo code):
usersTbl
(
Id int primary key
-- other user related columns
)
usersPermissionsTbl
(
UserId int, -- Foreign key to usersTbl
PermissionId int, -- Foreign key to permissionsTbl
Primary key (UserId, PermissionId)
)
permissionsTbl
(
Id int primary key,
Name varchar(20)
)
Once you have your tables correct, it's quite easy to get a list of comma separated values from the permissions table.
Adapting scsimon's sample data script to a correct many to many relationship:
declare #users table ([Name] varchar(64), id int, age int)
insert into #users values
('Bbarker',5987,87)
declare #permissions table (id int, [type] varchar(64))
insert into #permissions values
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')
declare #usersPermissions as table (userId int, permissionId int)
insert into #usersPermissions values (5987, 1), (5987, 3)
Now the query looks like this:
SELECT u.Name,
u.Id,
STUFF(
(
SELECT ','+ [type]
FROM #permissions p
INNER JOIN #usersPermissions up ON p.id = up.permissionId
WHERE up.userId = u.Id
FOR XML PATH('')
)
, 1, 1, '') As Permissions,
u.Age
FROM #Users As u
And the results:
Name Id Permissions Age
Bbarker 5987 Read,Upload 87
You can see a live demo on rextester.
I concur with much of the advice being presented to you in the other responses. The structure you're starting with is not going to be fun to maintain and work with. However, your situation may mean you are stuck with it so maybe some of the tools below will help you.
You can parse the delimiter with charindex() as others demonstrated here- MSSQL - How to split a string using a comma as a separator
... and even better here (several functions are provided) - Split function equivalent in T-SQL?
If you still want to do it with raw inline SQL and are committed to a loop, then pair the string manipulation with a CURSOR. Cursors have their own controversies BTW. The code below will work if your permission syntax remains consistent, which it probably doesn't.
They used charindex(',',columnName) and fed the location into the left() and right() functions along with some additional string evaluation to pull values out. You should be able to piece those together with a cursor
Your query might look like this...
--creating my temp structure
declare #userPermissions table (id int, [type] varchar(16))
insert into #userPermissions (id, [type]) values (1, 'Read')
insert into #userPermissions (id, [type]) values (2, 'Write')
insert into #userPermissions (id, [type]) values (3, 'Upload')
insert into #userPermissions (id, [type]) values (4, 'Admin')
declare #usersTbl table ([Name] varchar(16), id int, [permissions] varchar(8), age int)
insert into #usersTbl ([Name], id, [permissions], age) values ('Bbarker', 5987, '1,3', 87)
insert into #usersTbl ([Name], id, [permissions], age) values ('Mmouse', 5988, '2,4', 88)
--example query
select
ut.[Name]
, (select [type] from #userPermissions where [id] = left(ut.[permissions], charindex(',', ut.[permissions])-1) )
+ ','
+ (select [type] from #userPermissions where [id] = right(ut.[permissions], len(ut.[permissions])-charindex(',', ut.[permissions])) )
from #usersTbl ut

Find rows which are in a row with comma separated values same table sql

i have a table which contains comma separated values some thing like
id locs
1 a,s,d,f
2 s,d,f,a
3 d,s,a,f
4 d,f,g,a
5 a,s,e
6 f,d
i need out put as 1,2,3,6 in sql server when i have taken comma separated string of id 1.
that means i have taken locs of id 1 and separated with comma, now i want all the ids which contains the separated values of id 1.
Note: I know i don't have to keep comma separated values in table but its happened.
Hope i was clear with my question.
declare #tb table (id int, locs varchar(50))
insert into #tb values(1, 'a,s,d,f'),
(2,'s,d,f,a'),
(3,'d,s,a,f'),
(4,'d,f,g,a'),
(5,'a,s,e'),
(6,'f,d')
declare #cta varchar(20)='s,d,f,a'
;with cte0(id,col2)
as
(
select id,t.c.value('.','varchar(max)') as col2 from (select id,x= cast('<t>'+replace(locs,',','</t><t>') +'</t>' as xml) from #tb) a cross apply x.nodes('/t') t(c)
)
select distinct id from cte0 where #cta like '%'+col2+'%' and id not in( select distinct id from cte0 where #cta not like '%'+col2+'%')
If I understand you correctly, you need to return the id value of all the rows that has at least one of the comma separated values from the locs column of the row you selected. Since this is a poor database design there can only be an ugly solution to this problem.
Start by creating a user defined function to split a comma separated values into a table. there are many ways to do it, this is the first that google found.
DECLARE #Values varchar(max)
SELECT #Values = Locs
FROM Table WHERE Id = #Id
SELECT Id
FROM Table INNER JOIN dbo.Split(#Values) SplitedString
ON( '%,'+ SplitedString.s+',%' LIKE ',' + Locs + ',')

SQL query to know skipped number

Hi im new to SQL query i only know simple query.
My question is it possible to SELECT skipped check number EX2001,EX2002,EX2004
select result will show EX2003.
thanks in advance, sorry for my english.
you can answer algorithm only, ill try to implement it to SQL.
example:
SELECT * FROM SETTLEMENT WHERE checkno not in (between ex2001 and ex2900)
is it possible like this? im using MS SQL 2008.
create table sequence(st varchar(50))
insert into sequence values('EX2001');
insert into sequence values('EX2002');
insert into sequence values('EX2004');
insert into sequence values('EX2005');
insert into sequence values('EX2008');
Assuming your original table name is sequence with only one field, you can modify as per your needs
Try below
DECLARE #all TABLE
(
st varchar(20)
)
declare #start int
declare #end int
declare #str varchar(20)
set #start=2000 //define starting point
set #end=2010 //define end point
while(#start<#end)
BEGIN
SET #start=#start+1
set #str='EX'+cast(#start as varchar(20))
INSERT INTO #all VALUES (''+#str+'')
END
SELECT * from #all
except
select * from sequence
output
st
EX2003
EX2006
EX2007
EX2009
EX2010
if you need hard coded values in query then it can be done like (as you did not posted any code so here is a simple query). you can use IN() or NOT IN() in sql
select * from table where check_number not in ('EX2001','EX2002','EX2004' );
Since SQL engine is not mentioned, this answer is only for Oracle 11G
Option 1: With hierarchical queries
See Fiddle here
CREATE TABLE TEST1 ( A VARCHAR2 ( 9 ) );
INSERT INTO
TEST1
VALUES
( 'EX2001' );
INSERT INTO
TEST1
VALUES
( 'EX2002' );
INSERT INTO
TEST1
VALUES
( 'EX2004' );
COMMIT;
WITH TEST2
AS (SELECT
TO_NUMBER(SUBSTR ( A,
3 ))
AS A
FROM
TEST1)
SELECT
MIN_A
- 1
+ LEVEL
FROM
(SELECT
MIN ( A ) MIN_A,
MAX ( A ) MAX_A
FROM
TEST2)
CONNECT BY
LEVEL <= MAX_A
- MIN_A
+ 1
MINUS
SELECT A FROM TEST2;
Option 2: With Oracle analytics function
See Fiddle here
WITH T
AS (SELECT
TO_NUMBER(SUBSTR ( A,
3 ))
AS SNO,
SYSDATE AS SDATE
FROM
TEST1)
SELECT
SDATE,
SNO
+ 1
FIRST_MISSING,
DECODE ( NEXT_SNO
- 1,
SNO
+ 1, TO_NUMBER ( NULL ),
NEXT_SNO
- 1 )
LAST_MISSING
FROM
(SELECT
SDATE,
SNO,
LAG ( SNO )
OVER ( PARTITION BY SDATE
ORDER BY SNO )
LAST_SNO,
LEAD ( SNO )
OVER ( PARTITION BY SDATE
ORDER BY SNO )
NEXT_SNO
FROM
T)
WHERE
NVL ( NEXT_SNO,
SNO
+ 1 ) <> SNO
+ 1;

SQL - Select like string

I have the following table:
create table #tbl
(
PartNumber varchar(20)
)
insert into #tbl values ('003A-I00-1')
insert into #tbl values ('003A-INT-1')
insert into #tbl values ('003A-I1')
insert into #tbl values ('003A-I2')
insert into #tbl values ('003A-I3')
I need to select the highest PartNumber where PartNumber is equal to 003A-I followed only by a number (or numbers). In other words, I need 003A-I3.
What I've tried:
select top 1 partnumber
from #tbl
where partnumber like '003A-I%' + '%[0-9]'
order by partnumber desc
But it doesn't work. It returns 003A-INT-1. I need 003A-I3.
I'm using MS SQL Server 2005.
select top 1 partnumber
from tbl
where partnumber like '003A-I[0-9]%'
order by partnumber desc
SQLFiddle
Update (in response to #Nicarus's comment:
SELECT top 1 partnumber
FROM tbl
WHERE partnumber LIKE '003A-I[0-9]%'
ORDER BY convert(varbinary(200), partnumber) DESC
If you want to get the numeric values, you can use the ISNUMERIC() function of the SQL
The code will be:
select * from #tbl
where isnumeric(replace(partnumber, '003A-I', '')) = 1
order by partnumber desc
REVISE:
ISNUMERIC() function returns Boolean value either true or false (1 or 0).
REPLACE() function replaces a certain pattern in an expression or string
For more information:
http://technet.microsoft.com/en-us/library/ms186862.aspx
http://technet.microsoft.com/en-us/library/ms186272.aspx
UPDATE (2):
You can use the following if you don't want to match a specific word, but as long as you know the text size which would be replaced
select top 1 * from #tbl
where isnumeric(substring(partnumber, 7, LEN(partnumber))) = 1
order by partnumber desc
The link on the SQLFiddle

Pivot String SQL

I am trying to Pivot this table whose name is #salida
IDJOB NAME DATE
1 Michael NULL
1 Aaron NULl
THe result which I want to obtain is
IDJOB DATE NAME1 NAME2
1 NULL Michael Aaron
My code is this
SELECT *
FROM #salida
PIVOT
(
MAX([Name]) FOR [Name] IN ([Name1],[Name2])
) PVT GROUP BY IdJob,Date,Name1,Name2 ;
SELECT * FROM #salida
The result which obtain is
IDJOB DATE NAME1 NAME2
1 NULL NULL NULL
#XabiIparra, see a mock up. you need to partition by the IdJob and then add the columns needed.
DECLARE #salida TABLE(idjob VARCHAR(100),[Name] VARCHAR(100),[DATE] DATE);
INSERT INTO #salida VALUES
(1,'Michael', NULL)
,(1,'Aaron', NULL)
,(2,'Banabas', NULL)
SELECT p.*
FROM
(
SELECT *
,'NAME'+CAST(ROW_NUMBER() OVER(PARTITION BY [idjob] ORDER BY NAME) AS varchar(100)) ColumnName
FROM #salida
)t
PIVOT
(
MAX([Name]) FOR ColumnName IN (NAME1,NAME2,NAME3,NAME4,NAME5 /*add as many as you need*/)
)p;
How about must using aggregation and min() and max()?
select idjob, date, min(name), max(name)
from #salida
group by idjob, date;
SQL tables represent unordered sets, so there is no ordering to the values (unless another column specifies the ordering). So, this is probably the simplest way to get two different values in the same row.