0

I wanted to extract all the details till the second /(forward slash)from my table in SQL Server. Any ideas?

website
AA.AA/AB/123
www.google.com/en/abcd/
yahoo.com/us/dev
gmail.com

ouput
website
AA.AA/AB
www.google.com/en
yahoo.com/us
gmail.com
  • Its not clear what you are asking for? I do not se any pipes in your data? Is this data in rows? Or in one single column? Please provide table structure and what you have tried with code examples – Brad Jan 15 '21 at 18:27
  • Corrected the error. It's forward slash. what i tried `ISNULL(SUBSTRING(url, 0, NULLIF(CHARINDEX('|', reverse(url)), 0)), url)`. This is the closest that i could get. –  Jan 15 '21 at 18:30
  • REVERSE isn't going to help you here, since there may be 5 forward slashes, or 17, or 33. Unless your data can only ever have two, the location of the last one is irrelevant. What _version_ of SQL Server are you using? – Aaron Bertrand Jan 15 '21 at 18:41

4 Answers4

1

Perhaps this will suit your needs:

DECLARE @Table TABLE (Col1 NVARCHAR(100))
INSERT @Table VALUES
('website'),
('AA.AA/AB/123'),
('www.google.com/en/abcd/'),
('yahoo.com/us/dev'),
('gmail.com')



SELECT 
COALESCE(
    NULLIF(
        SUBSTRING(Col1,1,CHARINDEX('/',Col1,CHARINDEX('/',Col1)+1))
        ,'')
   ,Col1    
    ) AS Col1
FROM @Table
BJones
  • 2,450
  • 2
  • 17
  • 25
1

If you are using SQL Server 2017 or 2019, you can use STRING_AGG() to reassemble the output from STRING_SPLIT():

SELECT STRING_AGG(x.value, '/')
FROM dbo.table_name CROSS APPLY 
(
  SELECT value, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
  FROM STRING_SPLIT(Col1, '/') AS ss
) AS x(value, rn)
WHERE x.rn <= 2
GROUP BY Col1;

You might say:

"But Aaron, the output of STRING_SPLIT() isn't guaranteed to be in order; in fact the documentation warns about that."

This is true; the documentation does say that. But in current versions the output is extremely unlikely to be in anything but left-to-right order. I still suggest you be wary of relying on this, since it could break at any time (I warn about this in more detail here).

If you are on an older version, or don't trust it, you can use a table-valued function that preserves the order of the input string, for example from this answer:

CREATE FUNCTION [dbo].[SplitString]
(
    @List NVARCHAR(MAX),
    @Delim VARCHAR(255)
)
RETURNS TABLE
AS
    RETURN ( SELECT [Value], idx = RANK() OVER (ORDER BY n) FROM 
      ( 
        SELECT n = Number, 
          [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
          CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
        FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
          FROM sys.all_objects) AS x
          WHERE Number <= LEN(@List)
          AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim
      ) AS y
    );

With that function in place, you can then do the following, and now feel safer about relying on order (at the cost of a more expensive query):

;WITH src AS 
(
  SELECT Col1, idx, Value  
  FROM dbo.table_name CROSS APPLY dbo.SplitString(Col1, '/')
)
SELECT STUFF((SELECT '/' + Value
  FROM src
  WHERE src.idx <= 2 AND Col1 = t.Col1
  ORDER BY idx
  FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'), 1, 1, '')   
FROM dbo.table_name AS t
GROUP BY Col1;
Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
  • Oooh looking forward! So we can safely `row_number` the output? I always knew the wouldn't have coded it differently, but obvs they can change the code, and parallelism may screw that up also. – Charlieface Jan 16 '21 at 21:18
  • @Charlieface Well, Microsoft certainly isn't going to guarantee that anything you do to the output _after_ it's returned will be deterministic. After all, what would you put in your `ORDER BY` to determine the row number? `(SELECT NULL)` is a common one but it is definitely not deterministic. I _think_ that will work if you do it the way I've described here, but can't be absolutely certain. – Aaron Bertrand Jan 17 '21 at 17:20
  • The idea behind that was that it is just a TVF that outputs rows. `ORDER BY (SELECT NULL)` says "order it by whatever is easiest", and quite obviously, the engine has absolutely no other reason to order it differently (in a serial plan) as each row is pulled through it gets a number, simple. Yes, there is no guarantee, but there is no guarantee also on quite a lot of things in the optimizer (for example the optimizer could pick an obviously inefficient plan), we rely not so much on.anecdotal results, but on what we know based on logic, documentation, deep-diving into the source code etc. – Charlieface Jan 17 '21 at 17:42
  • This is different from the `SELECT TOP 100 PERCENT...ORDER BY...` in a view. There, the defintion didn't actually make sense anyway, and the optimizer was perfectly reasonable in eliding the ordering. Again, had it not elided it (and *guaranteed* not to do so, which it didn't), I still wouldn't *guarantee* that the rows would be ordered in a serial plan, but the optimizer would have had no reason to do otherwise, so it could have been relied upon. – Charlieface Jan 17 '21 at 17:46
  • @Charlieface just a couple of things - yes, parallelism (and batch mode, and inlining, and who knows what else) could _potentially_ have an impact here. Carrying around the disclaimer of "as long as you're not doing x, y, and z" suffers the same problem as relying on order by in a view, ordering from a clustered index, etc. – Aaron Bertrand Jan 17 '21 at 18:22
  • @Charlieface The difference between this and other execution plan behaviors is the latter cases don't change the results (and there was never an ordering promise there anyway). We have to be careful to not overly promote a promise of behavior here, because relying on that behavior and then having it break is potentially more harmful to the consumer of the results than differences _only_ noticeable in performance. There are of course ways to guarantee order of a split string without using this specific function, but those approaches of course add significant cost to the queries. – Aaron Bertrand Jan 17 '21 at 18:25
  • So what are MS saying? Who cares what order they come out the function, anything can happen after that?? – Charlieface Jan 17 '21 at 18:26
  • @Charlieface Yes. If you add `ORDER BY value` do you think SQL Server should ignore that and maintain the order that the function puts out? Why? – Aaron Bertrand Jan 18 '21 at 15:43
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227496/discussion-between-charlieface-and-aaron-bertrand). – Charlieface Jan 18 '21 at 15:53
  • So what were those two deleted comments in chat? Getting curious now – Charlieface Jan 22 '21 at 01:57
0

I find cross apply handy for these situations

select case when str like '%/%' then left(str, i2-1) else str end as str
from t
cross apply (select charindex( '/', str ) as i1) t2           --position of first slash
cross apply (select charindex( '/', str, (i1 + 1)) as i2 ) t3 --position of second slash
Radagast
  • 5,102
  • 3
  • 12
  • 27
0

Below is the simple query you can try. In the below query please replace 'colName' with your column name and Table_1 with your table name.

SELECT  LEFT([colName], charindex('/', [colName], charindex('/', [colName])+1)-1) AS [AfterSecondPipe]
FROM    [Table_1]
RAM G
  • 50
  • 1
  • 1
  • 13