/ / Contagem total de sequências separadas por vírgula por variável por linha - sql-server, sql-server-2008

Contagem total de sequências separadas por vírgulas por variável por linha - sql-server, sql-server-2008

Este é o esquema:

User_ID Page_ID Timestamp
1   48,51,94    7/26/2017 8:30
2   42,11,84    7/26/2017 9:40
3   4,16,24 7/26/2017 16:20
4   7,2,94  7/27/2017 8:00
1   48,22,94    7/27/2017 13:50
2   42,11   7/27/2017 14:00
3   4,24    7/27/2017 18:15

O código abaixo fornece a contagem agregada de IDs de página executados por usuário (não exclusivo de propósito):

SELECT User_ID, sum(len(Page_ID) - len(replace(Page_ID, ",", "")) +1) as TotalPageCount
FROM DBTABLE
group by User_ID

Saída:

User_ID TotalPageCount
1   6
2   5
3   5
4   3

No entanto, pretendo adicionar um (separado por vírgula)coluna com contagem de páginas por ID de página por ID de usuário. ie uma coluna como ID 1 do boletim informativo: contagem, ID 2 do boletim informativo: contagem etc. (essencialmente um dicionário). Pode ser um formato diferente, mas precisa ser descritivo no nível do ID da página, com sua respectiva contagem.

Algo assim:

User_ID PageIDCount TotalPageCount
1   48:2, 51:1, 94:2, 22:1, 6
2   42:2, 11:2, 84:1, 5
3   4:2, 16:1, 24:2, 5
4   7:1, 2:1, 94:1, 3

Sua ajuda é muito apreciada!


Editar:

De acordo com a incrível solução de SeanLange, você pode alterar a definição para MyCTE para a abaixo, a fim de evitar o uso de quaisquer funções:

select user_id, page_id, page_count = count(*)
FROM (
SELECT user_id, Split.a.value(".", "NVARCHAR(max)") AS page_id FROM
( SELECT user_id, CAST ("<M>" + REPLACE(page_id, ",", "</M><M>") + "</M>" AS XML) page_id
FROM #temp
) AS A
CROSS APPLY page_id.nodes ("/M") AS Split(a)
) x
group by user_id, page_id

Respostas:

1 para resposta № 1

Uau, isso é um pesadelo. Você vai precisar de um divisor de cordas para começar. Meu favorito pessoal é este. http://www.sqlservercentral.com/articles/Tally+Table/72993/ Há várias outras excelentes opções aqui. https://sqlperformance.com/2012/07/t-sql-queries/split-strings

Começando com seus dados, você precisará fazer algo assim.

declare @Something table
(
User_ID int
, Page_ID varchar(100)
, MyDate datetime
)

insert @Something
select 1, "48,51,94", "7/26/2017 8:30" union all
select 2, "42,11,84", "7/26/2017 9:40" union all
select 3, "4,16,24", "7/26/2017 16:20" union all
select 4, "7,2,94", "7/27/2017 8:00" union all
select 1, "48,22,94", "7/27/2017 13:50" union all
select 2, "42,11", "7/27/2017 14:00" union all
select 3, "4,24", "7/27/2017 18:15"

select User_ID
, Page_ID = x.Item
, count(*)
from @Something s
cross apply dbo.DelimitedSplit8K(s.Page_ID, ",") x
group by User_ID
, x.Item
order by User_ID
, x.Item

Isso obtém os dados com as contagens que você deseja. A partir daí, você terá que empurrar isso de volta para a estrutura desnormalizada que deseja. Você pode fazer isso com FOR XML. Aqui está um artigo que explica como fazer essa parte disso. Simulando a função MySQL group_concat no Microsoft SQL Server 2005?

-----EDITAR-----

OK, aqui está a solução completa de trabalho. Obviamente, você está trabalhando duro para tentar resolver isso. Estou usando a função DelimitedSplit8K aqui, então não precisei alinhar XML como sua solução estava fazendo.

with MyCTE as
(
select User_ID
, Page_ID = x.Item
, PageCount = count(*)
from @Something s
cross apply dbo.DelimitedSplit8K(s.Page_ID, ",") x
group by User_ID
, x.Item
)
, GroupedPageViews as
(
select c.User_ID
, sum(c.PageCount) as TotalPageCount
, PageViews = STUFF((select ", " + convert(varchar(4), c2.Page_ID) + ":" + convert(varchar(4), c2.PageCount)
from MyCTE c2
where c.User_ID = c2.User_ID
order by c2.Page_ID
for xml path("")), 1, 1, "")
from MyCTE c
group by c.User_ID
)

select gpv.User_ID
, gpv.PageViews
, gpv.TotalPageCount
from GroupedPageViews gpv
join MyCTE c on c.User_ID = gpv.User_ID
group by gpv.PageViews
, gpv.User_ID
, gpv.TotalPageCount
order by gpv.User_ID

Isso retornará seus dados assim.

User_ID PageViews               TotalPageCount
1       22:1, 48:2, 51:1, 94:2  6
2       11:2, 42:2, 84:1        5
3       16:1, 24:2, 4:2         5
4       2:1, 7:1, 94:1          3

0 para resposta № 2

Aqui está

SELECT DISTINCT User_Id
, (
SELECT CAST(t.Value AS VARCHAR) + ":" + CAST(COUNT(t.value) AS VARCHAR) + ", "
FROM TBL_46160346_DBTABLE ii
CROSS APPLY (
SELECT *
FROM fn_ParseText2Table(Page_ID, ",")
) t
WHERE pp.User_Id = ii.User_Id
GROUP BY User_Id
, VALUE
ORDER BY User_Id
FOR XML PATH("")
) PageIDCount
, (
SELECT  COUNT(*)
FROM TBL_46160346_DBTABLE ii
CROSS APPLY (
SELECT *
FROM fn_ParseText2Table(Page_ID, ",")
) t
WHERE pp.User_Id = ii.User_Id
GROUP BY User_Id
) TotalPageCount
FROM TBL_46160346_DBTABLE pp

Função fn_ParseText2Table

ALTER FUNCTION [dbo].[fn_ParseText2Table] (
@p_SourceText VARCHAR(8000), @p_Delimeter VARCHAR(10) = "," --default comma
)
RETURNS @retTable TABLE (Value BIGINT)
AS
BEGIN
DECLARE @w_Continue INT, @w_StartPos INT, @w_Length INT, @w_Delimeter_pos INT, @w_tmp_txt VARCHAR(48), @w_Delimeter_Len TINYINT

IF LEN(@p_SourceText) = 0
BEGIN
SET @w_Continue = 0 -- force early exit
END
ELSE
BEGIN
-- parse the original @p_SourceText array into a temp table
SET @w_Continue = 1
SET @w_StartPos = 1
SET @p_SourceText = RTRIM(LTRIM(@p_SourceText))
SET @w_Length = DATALENGTH(RTRIM(LTRIM(@p_SourceText)))
SET @w_Delimeter_Len = LEN(@p_Delimeter)
END

WHILE @w_Continue = 1
BEGIN
SET @w_Delimeter_pos = CHARINDEX(@p_Delimeter, SUBSTRING(@p_SourceText, @w_StartPos, @w_Length - @w_StartPos + @w_Delimeter_Len))

IF @w_Delimeter_pos > 0 -- delimeter(s) found, get the value
BEGIN
SET @w_tmp_txt = LTRIM(RTRIM(SUBSTRING(@p_SourceText, @w_StartPos, @w_Delimeter_pos - 1)))
SET @w_StartPos = @w_Delimeter_pos + @w_StartPos + @w_Delimeter_Len - 1
END
ELSE -- No more delimeters, get last value
BEGIN
SET @w_tmp_txt = LTRIM(RTRIM(SUBSTRING(@p_SourceText, @w_StartPos, @w_Length - @w_StartPos + @w_Delimeter_Len)))

SELECT @w_Continue = 0
END

INSERT INTO @retTable
VALUES (@w_tmp_txt)
END

RETURN
END