Группа SQL по части строки

У меня есть следующие данные в моей таблице:

URL         TIME    DATE
--------------------------------------
/x          11      2013-08-01
/x          11      2013-08-01
/pl/        11      2013-08-01
/pl/        11      2013-08-03
/pl/XXX/    11      2013-08-01
/pl/XXX/    11      2013-08-04
/pl/XXX/1   11      2013-08-01
/pl/XXX/2   11      2013-08-01
/pl/YYY/    11      2013-08-01
/pl/YYY/1   11      2013-08-01
/pl/YYY/2   11      2013-08-04
/pl/YYY/3   11      2013-08-04

Есть ли способ группировать по URL-адресу до третьего слэша (/) в SQL Server? К сожалению, существует запись, содержащая меньше трех.

Ответ 1

Один трюк для подсчета количества косой черты в строке:

len(url) - len(replace(url,'/',''))

Затем вы можете использовать charindex три раза, чтобы найти позицию третьей косой черты:

select  BeforeThirdSlash
,       max([date])
from    (
        select  case 
                when len(url) - len(replace(url,'/','')) < 3 then url
                else substring(url, 1, charindex('/', url, charindex('/', 
                         url, charindex('/', url)+1)+1)-1)
                end as BeforeThirdSlash
        ,       *
        from    @t
        ) as SubQueryAlias
group by
        BeforeThirdSlash

Живой пример в SQL Fiddle.

Ответ 2

Простое выражение, позволяющее захватить подстроку до третьего символа '/', выглядит следующим образом:

case
    when patindex('%/%/%/%', url) = 0 then url
    else left(url,charindex('/',url,charindex('/',url,charindex('/',url)+1)+1))
end

patindex проверяет наличие по крайней мере трех косых черт; left извлекает подстроку до и включая третью.

С этим выражением в руке запись group by проста:

SELECT
    url3, max(tm), max(dt)
FROM (
    SELECT
        CASE
            WHEN patindex('%/%/%/%', url) = 0 THEN url
            ELSE left(url,charindex('/',url,charindex('/',url,charindex('/',url)+1)+1))
        END AS url3
    ,   tm
    ,   dt
    FROM test
) x
GROUP BY url3

Демо на SqlFiddle.

Ответ 3

Вы можете найти положение каждой позиции / и обрезать до MAX(position) - предполагая, что третий / является последним / в соответствии с данными.

DECLARE @tbl TABLE ( u VARCHAR(255), t INT, d DATE)

INSERT INTO @tbl (u, t, d) VALUES
('/x',          11,      '2013-08-01'),
('/x',          11,      '2013-08-01'),
('/pl/',        11,      '2013-08-01'),
('/pl/',        11,      '2013-08-03'),
('/pl/XXX/',    11,      '2013-08-01'),
('/pl/XXX/',    11,      '2013-08-04'),
('/pl/XXX/1',   11,      '2013-08-01'),
('/pl/XXX/2',   11,      '2013-08-01'),
('/pl/YYY/',    11,      '2013-08-01'),
('/pl/YYY/1',   11,      '2013-08-01'),
('/pl/YYY/2',   11,      '2013-08-04'),
('/pl/YYY/3',   11,      '2013-08-04')

;WITH split AS (
    SELECT u, 1 s, CHARINDEX('/', u) p
    FROM @tbl
    UNION ALL
    SELECT u, p + 1, CHARINDEX('/', u, p + 1)
    FROM split
)

SELECT LEFT(t.u, split.i), MAX(t.t), MAX(t.d)
FROM @tbl t
JOIN (
    SELECT u, MAX(p) i 
    FROM split
    GROUP BY u
) split ON split.u = t.u
GROUP BY LEFT(t.u, split.i)

С небольшой настройкой на cte вы можете управлять вхождениями

DECLARE @n INT = 3    -- 'nth occurence'

;WITH split AS (
    SELECT u, CHARINDEX('/', u) i, 1 r
    FROM (
        SELECT DISTINCT u
        FROM @tbl
    ) t
    WHERE CHARINDEX('/', u) > 0
    UNION ALL
    SELECT u, CHARINDEX('/', u, i + 1), r + 1
    FROM split
    WHERE r < @n
    AND CHARINDEX('/', u, i + 1) > 0
)

SELECT LEFT(t.u, split.i) u, MAX(t.t) t , MAX(t.d) d
FROM @tbl t
JOIN split ON split.u = t.u
GROUP BY LEFT(t.u, split.i)

демонстрационная версия sql