如何在给定开始日期的固定布局中确定结果集中WeekDay列的日期?

时间:2014-05-27 13:18:44

标签: sql sql-server date

我的结果集始终以这种格式显示:

LocationId Wed Thu Fri Sat Sun Mon Tue

生成这些列的函数的输入接受开始日期,并生成7天的数量报告。因此,如果我输入'2014-02-01',则列将始终按该顺序出现,即使该特定日期是星期六(日期“环绕”)。

我需要每列的日期,以便计算另一个值(称为“费用”),该值基于每个位置的开始日期和结束日期。例如,对于日期'2014-01-01'到'2014-02-03',位置21可能具有与其关联的值50,但是从'2014-02-04'它具有值53。值在当天列中指的是销售。因此,如果有一个值(甚至为0),则表示SalesPerson存在且他应该收到AppearanceFee。其中一个困难是计算该人在特定日期应该收到的费用,因为报告没有生成日期。您拥有的唯一信息是开始日期。

例如

LocationId | Value | StartDate  | EndDate
-----------+-------+------------+-----------
21         | 50    | 2014-01-01 | 2014-02-03
21         | 53    | 2014-02-04 | null

要模拟一条记录,可以使用此查询:

declare @startdate datetime
select @startdate = '2014-02-01'

select *
, 0 as Fee -- How do I calculate this value?
from
(
    select 21 as LocationId
    , 30 as Wed
    , 33 as Thu
    , 36 as Fri
    , NULL as Sat
    , NULL as Sun
    , 19 as Mon
    , 24 as Tue
) record

我想过每天使用一个复杂的case语句,但是有一个更简单的方法吗?

CASE Left(DATENAME(dw, @startdate), 3)
WHEN 'Wed' THEN
(
    (SELECT IsNull(Wed, 0) * Value FROM LocationValue lv WHERE lv.LocationId = record.LocationId AND @startdate BETWEEN lv.StartDate and IsNull(lv.EndDate, '2050-12-31')) +
    (SELECT IsNull(Thu, 0) * Value FROM LocationValue lv WHERE lv.LocationId = record.LocationId AND DateAdd(dd, 1, @startdate) BETWEEN lv.StartDate and IsNull(lv.EndDate, '2050-12-31')) +
    (SELECT IsNull(Fri, 0) * Value FROM LocationValue lv WHERE lv.LocationId = record.LocationId AND DateAdd(dd, 2, @startdate) BETWEEN lv.StartDate and IsNull(lv.EndDate, '2050-12-31')) +
    (SELECT IsNull(Sat, 0) * Value FROM LocationValue lv WHERE lv.LocationId = record.LocationId AND DateAdd(dd, 3, @startdate) BETWEEN lv.StartDate and IsNull(lv.EndDate, '2050-12-31')) +
    (SELECT IsNull(Sun, 0) * Value FROM LocationValue lv WHERE lv.LocationId = record.LocationId AND DateAdd(dd, 4, @startdate) BETWEEN lv.StartDate and IsNull(lv.EndDate, '2050-12-31')) +
    (SELECT IsNull(Mon, 0) * Value FROM LocationValue lv WHERE lv.LocationId = record.LocationId AND DateAdd(dd, 5, @startdate) BETWEEN lv.StartDate and IsNull(lv.EndDate, '2050-12-31')) +
    (SELECT IsNull(Tue, 0) * Value FROM LocationValue lv WHERE lv.LocationId = record.LocationId AND DateAdd(dd, 6, @startdate) BETWEEN lv.StartDate and IsNull(lv.EndDate, '2050-12-31'))
)

正如你所看到的,这个案例陈述相当笨拙。

1 个答案:

答案 0 :(得分:1)

我设法使用PIVOT和UNPIVOT的组合以及生成日期范围的查询来解决这个问题。

DECLARE @startDate DATETIME = '2014-02-01'
DECLARE @endDate DATETIME = @startDate + 7

SELECT
  LocationId,
  Sum(Wed) Wed,
  Sum(Thu) Thu,
  Sum(Fri) Fri,
  Sum(Sat) Sat,
  Sum(Sun) Sun,
  Sum(Mon) Mon,
  Sum(Tue) Tue,
  Sum(Fee) Fee
FROM
    (
      SELECT
        af.LocationId,
        Calendar.Day,
        Date,
        Sales,
        IsNumeric(Sales) * Value AS Fee
      FROM
        (
          SELECT
            Left(DateName(DW, datetable.Date), 3) Day,
            Convert(DATE, datetable.Date)         Date
          FROM (
                 SELECT DATEADD(DAY, -(a.a + (10 * b.a) + (100 * c.a)), getdate()) AS Date
                 FROM (SELECT 0 AS a
                       UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
                       UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
                       UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a
                   CROSS JOIN (SELECT 0 AS a
                       UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
                       UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
                       UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b
                   CROSS JOIN (SELECT 0 AS a
                       UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
                       UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
                       UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS c
               ) datetable
          WHERE datetable.Date BETWEEN @startDate AND @endDate
        ) Calendar LEFT JOIN
        (
          SELECT
            LocationId,
            Day,
            Sales
          FROM dbo.f_FakeReport(@startDate) AS Report
          UNPIVOT
          (
              Sales
          FOR Day IN (Wed, Thu, Fri, Sat, Sun, Mon, Tue)
          ) U) AS Report
          ON Calendar.Day = Report.Day
        LEFT JOIN AppearanceFee af
          ON af.LocationId = Report.LocationId
             AND date BETWEEN af.StartDate AND IsNull(af.EndDate, '2099-12-21')
    ) data
  PIVOT
  (
    Sum(Sales)
  FOR Day IN (Wed, Thu, Fri, Sat, Sun, Mon, Tue)
  ) pvt
WHERE LocationId IS NOT NULL
GROUP BY LocationId

http://sqlfiddle.com/#!3/98759/52