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 № 1Uau, 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