Group records per day

Asked

Viewed 1,818 times

2

I have the following table

ID  |   Início   |   Fim      |   Horas
333 | 01/01/2017 | 03/01/2017 |    5
333 | 02/01/2017 | 05/01/2017 |    1
333 | 05/01/2017 | 07/01/2017 |    3
333 | 01/01/2017 | 07/01/2017 |    6

I need a result like this

ID  | 01 | 02 | 03 | 04 | 05 | 06 | 07 |...| 31
333 | 11 | 12 | 12 | 07 | 10 | 09 | 09 | 0 | 0

I’m doing the sum of hours by grouping the days of Beginning and End. Another result that for my need would still be this

ID  | Dia | Horas
333 | 01  |  11
333 | 02  |  12
333 | 03  |  12
333 | 04  |  07
333 | 05  |  10
333 | 06  |  09
333 | 07  |  09
333 | ... |  0
333 | 31  |  0
  • "I am doing the sum of Hours by grouping by Start and End days": the values of tables 2 and 3 do not coincide with this statement. For example, for this rule day 2 would have only 1 hour but in tables 2 and 3 the value displayed is 12.

  • The interval can start in one month and end in another?

  • Yes, it can. Or even years.

5 answers

1


I ended up opting for the following solution

Dice

DECLARE @INI SMALLDATETIME = '20170101'
DECLARE @FIM SMALLDATETIME = '20170131'
DECLARE @TAB TABLE(
    ID INT
    ,INICIO SMALLDATETIME
    ,FIM SMALLDATETIME
    ,HORAS FLOAT
)

INSERT INTO @TAB
SELECT 333, '20170101', '20170103', 5
UNION ALL
SELECT 333, '20170102', '20170105', 1
UNION ALL
SELECT 333, '20170105', '20170107', 3
UNION ALL
SELECT 333, '20170101', '20170107', 6

Logic

;WITH sample AS (
SELECT INICIO AS dt, ID, HORAS, FIM
FROM @TAB
WHERE   (
            INICIO BETWEEN @INI AND @FIM
            OR FIM BETWEEN @INI AND @FIM
            OR (INICIO <= @INI AND FIM >= @FIM)
        )
    UNION ALL
    SELECT DATEADD(dd, 1, dt), S.ID, S.HORAS, FIM
    FROM sample s 
    WHERE DATEADD(dd, 1, dt) <= FIM
)

SELECT dt, SUM(HORAS) FROM sample
WHERE DT BETWEEN @INI AND @FIM
GROUP BY DT
ORDER BY DT
OPTION (MAXRECURSION 0);

It won’t display until the 31st but I can configure it on the client side (graphical component)

  • As I commented in the answer posted, the trick is to transform the "date range" into "individual dates". From there, it is "papaya with sugar", right? Doubt: the ID column should not be included in the GROUP BY clause?

  • In my case no need since id will always be the same.

-1

-1

You can use the SUM and the DAY and the MONTH to catch only the day for each month.

SELECT
    ID,
    DAY (INICIO) AS DIA,
    MONTH (INICIO) AS MES,
    SUM(HORAS) AS HORAS
FROM
    TABELA
GROUP BY
    DAY (INICIO),
    MONTH (INICIO)
ORDER BY
    DAY (INICIO),
    MONTH (INICIO)
  • Someone passed and denied all the answers except one. Which leads me to think of only one possibility. Now, this person might want to tell me what’s wrong with my answer.

-1

-2

Each entry table row contains the ID of an object, a date range, and the number of recorded hours. It is also observed that there are overlapping dates in the intervals; that is, the intervals overlap.

From each row of the input table it is necessary to generate several rows, one for each date of the range defined by the Start/End columns. For example, for the first row of the input table,

333 | 01/01/2017 | 03/01/2017 | 5

the following lines are generated:

inserir a descrição da imagem aqui

After this transformation from "date range" to "dates", calculating the sum of the hours for each ID/day is a simple process.


To assemble the third table, a shape is

-- código #1 v2
-- informe o mês e ano
declare @Mês int, @Ano int;
set @Mês= 1;
set @Ano= 2017;

-- calcula o período de emissão
declare @DataInicial date, @DataFinal date;
set @DataInicial= Convert(date, '1/' + Cast(@Mês as varchar) +
                                 '/' + Cast(@Ano as char(4)), 103);
set @DataFinal= DateAdd(day, -1, DateAdd(month, +1, @DataInicial));

--
SELECT H.ID, Day(I.dt) as Dia, sum(H.Horas) as Horas
  from tbHoras as H
       cross apply dbo.GeraIntervalo (H.Início, H.Fim) as I
  where H.Fim >= @DataInicial
        or H.Início <= @DataFinal       
  group by H.ID, I.dt
  having I.dt between @DataInicial and @DataFinal
  order by H.ID, Dia;
go

inserir a descrição da imagem aqui


To obtain the layout of the second table, the usual technique is the pivoteamento.

-- código #2 v2
-- informe o mês e ano
declare @Mês int, @Ano int;
set @Mês= 1;
set @Ano= 2017;

-- calcula o período de emissão
declare @DataInicial date, @DataFinal date;
set @DataInicial= Convert(date, '1/' + Cast(@Mês as varchar) +
                                 '/' + Cast(@Ano as char(4)), 103);
set @DataFinal= DateAdd(day, -1, DateAdd(month, +1, @DataInicial));

--
with T3 as (
SELECT H.ID, I.dt, H.Horas
  from tbHoras as H
       cross apply dbo.GeraIntervalo (H.Início, H.Fim) as I
  where H.Fim >= @DataInicial
        or H.Início <= @DataFinal
),
T3_Mês as (
SELECT ID, day(dt) as Dia, Horas 
  from T3
  where dt between @DataInicial and @DataFinal
)  
SELECT ID, [01], [02], [03], [04], [05], [06], [07], [08], [09], [10],
       [11], [12], [13], [14], [15], [16], [17], [18], [19], [20],
       [21], [22], [23], [24], [25], [26], [27], [28], [29], [30], [31]
  from T3_Mês
       pivot (sum(Horas) for Dia in ([01],[02],[03],[04],[05],[06],
                  [07],[08],[09],[10],[11],[12],[13],[14],[15],[16],
                  [17],[18],[19],[20],[21],[22],[23],[24],[25],[26],
                  [27],[28],[29],[30],[31])) as P
go

inserir a descrição da imagem aqui


The following functions have been used::

-- código #3
IF OBJECT_ID('dbo.GetNums') IS NOT NULL DROP FUNCTION dbo.GetNums;
GO
CREATE FUNCTION dbo.GetNums(@n AS BIGINT) RETURNS TABLE
AS
RETURN
  WITH
  L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
  L1   AS(SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
  L2   AS(SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
  L3   AS(SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
  L4   AS(SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
  L5   AS(SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
  Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5)
  SELECT TOP (@n) n FROM Nums ORDER BY n;
GO

Source: Packing Date Intervals

and

-- código #4
CREATE FUNCTION GeraIntervalo (@D1 date, @D2 date)
returns table as
return
with Datas as (
SELECT DateAdd(day, n-1, @D1) as dt
  from dbo.GetNums(DateDiff(day, @D1, @D2) + 1) as Nums
)
SELECT dt
  from Datas;
go 

Code to generate test data.

-- código #5
CREATE TABLE tbHoras (ID int, Início date, Fim date, Horas tinyint);

INSERT into tbHoras values
  (333, '1/1/2017', '3/1/2017', 5),
  (333, '2/1/2017', '5/1/2017', 1),
  (333, '5/1/2017', '7/1/2017', 3),
  (333, '1/1/2017', '7/1/2017', 6),
  (337, '30/12/2016', '2/1/2017', 3),
  (337, '1/1/2017', '3/1/2017', 2),
  (337, '30/1/2017', '2/2/2017', 4);
go
  • Columns with accentuation are not indicated.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.