根据列

时间:2018-06-27 10:42:58

标签: sql sql-server tsql

我在其中一个表中有此数据:

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

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

我该怎么做?

非常感谢您

6 个答案:

答案 0 :(得分: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)

答案 1 :(得分: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;

这包括每一行的日期。

答案 2 :(得分:0)

您应该有一个日历表,您可以在其中插入表,也可以使用数字表/计数表建立一个日历表。

在这里 an excellent article 可以了解这个概念。

下面展示了一种实现此目的的方法-

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

答案 3 :(得分: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])

答案 4 :(得分: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

答案 5 :(得分:0)

还有一个:

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

-提示CTE会立即生成31个运行编号
-此CTE与TOP一起使用,并具有计算出的计数来检索数字的拟合计数

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版本。这将是much easier with EOMONTH (v2012+)DATEFROMPARTS (v2012+)

使用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