0

我的一张表中有这些数据:

YEAR    MONTH     Cost
-------------------------
2018      1       25000
2018      2       32000
2018      3        9865

我想重复行,例如如果 Month=1,那么我希望这一行有 31 次,如果 Month=2,那么这一行有 28 次(实际上是根据每个月的天数)

我怎样才能做到这一点?

太感谢了

4

6 回答 6

1

使用日历表,它将为您解决这个和许多未来的日期问题。

此解决方案生成一个具有递归 CTE 的解决方案。

DECLARE @StartDate DATE = '2018-01-01'
DECLARE @EndDate DATE = '2020-01-01'

;WITH GeneratedCalendar AS
(
    SELECT
        GeneratedDate = @StartDate,
        Month = MONTH(@StartDate),
        Year = YEAR(@StartDate)
    UNION ALL
    SELECT
        GeneratedDate = DATEADD(DAY, 1, G.GeneratedDate),
        Month = MONTH(DATEADD(DAY, 1, G.GeneratedDate)),
        Year = YEAR(DATEADD(DAY, 1, G.GeneratedDate))
    FROM
        GeneratedCalendar AS G
    WHERE
        G.GeneratedDate < @EndDate
)
SELECT
    T.YEAR,
    T.MONTH,
    T.Cost,
    G.GeneratedDate
FROM
    YourTable AS T
    INNER JOIN GeneratedCalendar AS G ON
        T.YEAR = G.Year AND
        T.MONTH = G.Month
ORDER BY
    T.YEAR,
    T.MONTH
OPTION
    (MAXRECURSION 30000)
于 2018-06-27T10:49:33.213 回答
0

您可以使用递归 CTE:

with cte as (
      select year, month, datefromparts(year, month, 1) as dte, cost
      from t
      union all
      select year, month, dateadd(day, 1, dte), cost
      from t
      where day(dateadd(day, 1, dte)) <> 1
     )
select *
from cte;

这包括每行的日期。

于 2018-06-27T10:49:14.690 回答
0

您应该有一个日历表,您可以在其中 LEFT JOIN 您的表,或者您可以使用数字表/计数表构建一个。

这是一篇关于这个概念的优秀文章。

一种方法如下所示 -

select year, month,date=t.d cost from 
your table cross apply(
select
d=dateadd(d,r ,cast(cast(month as varchar(2))+ '-01-'+cast(year as varchar(4)) as date))
from
(
select top 31
r=row_number() over( order by (select null))-1
from
sys.objects s1 cross join sys.objects s2)t
where month(dateadd(d,r ,cast(cast(month as varchar(2))+ '-01-'+cast(year as varchar(4)) as date)))=month
)t
于 2018-06-27T10:52:50.257 回答
0

这使用带有 CROSS APPLY 的内联表值函数:

create function YearsMonths(@year int, @month int)
returns table
as
return
    Select @year [Year], @month [Month]
    from    master.dbo.spt_values t2 
    where t2.type = 'P' AND t2.number < CASE WHEN @month IN (1, 3, 5, 7, 8, 10, 12) THEN 31
                WHEN @month IN (4, 6, 9, 11) THEN 30
                ELSE CASE WHEN (YEAR(@year) % 4    = 0 AND
                                YEAR(@year) % 100 != 0) OR
                               (YEAR(@year) % 400  = 0)
                          THEN 29
                          ELSE 28
                     END
           END
GO
create table yearmonthcost ([Year] int, [Month] int, [Cost] int)
insert into yearmonthcost values
  (2018, 1, 25000)
, (2018, 2, 32000)
, (2018, 3, 9865)

select * 
from yearmonthcost ymc
cross apply dbo.YearsMonths(ymc.[Year], ymc.[Month])
于 2018-06-27T11:14:58.550 回答
0

递归 CTE 是你的朋友,假设你的表名是T2

with A as (
    select T.[year], T.[month], datefromparts(T.[year], T.[month], 1) AS D, T.[cost]
    from T2 T
    union all
    select T.[year], T.[month], dateadd(day, 1, A.D) AS D, T.[cost]
    from T2 T INNER JOIN A ON T.[year]=A.[year] AND T.[month]=A.[month] AND T.[cost]=A.[cost]
    where MONTH(dateadd(day, 1, A.D)) = MONTH(A.D)
)
SELECT * FROM A
ORDER BY A.[year], A.[month], A.D
于 2018-06-27T11:34:47.300 回答
0

还有一个:

DECLARE @tbl TABLE(y INT, m INT, Cost INT)
INSERT INTO @tbl VALUES
 (2018,1,25000)
,(2018,2,32000)
,(2018,3,9865);

--tally-CTE 将立即生成 31 个运行数字 --
此 CTETOP与计算的计数一起使用以检索合适的数字计数

WITH Tally AS(SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
                                  ,(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)
                                  ,(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31)) t(nr))
SELECT *
FROM @tbl t
CROSS APPLY(SELECT CONVERT(DATETIME,CONCAT(t.y,REPLACE(STR(t.m,2),' ','0'),'01'),126)) D(FirstOfMonth)
CROSS APPLY(SELECT TOP (DAY(DATEADD(MONTH,1,D.FirstOfMonth)-1)) nr FROM Tally ORDER BY nr) AS multiplier;

第一个交叉应用将计算您本月的第一天(“20180101”),而计算TOP将增加一个月并返回一天。这是本月的最后一天。

您没有指定您的 SQL-Serer 版本。使用(v2012+)(v2012+)更容易EOMONTHDATEFROMPARTS

使用 SQL-Server 2012 更新

用 v2012+ 试试这个

WITH Tally AS(SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
                                  ,(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)
                                  ,(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31)) t(nr))
SELECT *
FROM @tbl t
CROSS APPLY(SELECT TOP (DAY(EOMONTH(DATEFROMPARTS(t.y,t.m,1)))) nr FROM Tally ORDER BY nr) AS multiplier
于 2018-06-27T12:30:37.223 回答