【问题标题】:Difference Max and Min from Different Dates不同日期的最大和最小差异
【发布时间】:2015-06-30 06:25:20
【问题描述】:

我将尽力解释这一点。

下面的代码执行以下操作:

  • 从 ServiceLocation 表中查找服务地址。
  • 查找服务类型(电或水)。
  • 查找过去多少天提取数据。

一旦有了这个,它就会通过从一天的最小抄表量中减去一天的最大抄表量来计算“每日使用量”。

        (MAX(mr.Reading) - MIN(mr.Reading)) AS 'DaytimeUsage'

但是,我缺少的是前一天的最大读数和当天的最小读数。从数学上讲,这应该是这样的:

  • MAX(PriorDayReading) - MIN(ReadDateReading)

基本上,如果它回到 5 天,它应该踢出一个如下所示的表:

服务地点 |阅读日期 |用法 |

主街 123 号 | 2015 年 4 月 20 日 | 12 |
主街 123 号 | 2015 年 4 月 19 日 | 8 |
主街 123 号 | 2015 年 4 月 18 日 | 6 |
主街 123 号 | 2015 年 4 月 17 日 | 10 |
主街 123 号 | 2015 年 4 月 16 日 | 11 |

“Usage”是我缺少的“DaytimeUsage”+ 用法(以及上面的问题)。例如,4/18/15 将是下面查询中的“DaytimeUsage”加上从 4/17/15 读取的 MAX 与 4/18/15 读取的 MIN 之间的差异。

我不确定如何完成此操作或是否可能。

SELECT 
    A.ServiceAddress AS 'Service Address', 
    convert(VARCHAR(10),A.ReadDate,101) AS 'Date', 
    SUM(A.[DaytimeUsage]) AS 'Usage' 

FROM 
(
    SELECT
        sl.location_addr AS 'ServiceAddress', 
        convert(VARCHAR(10),mr.read_date,101) AS 'ReadDate', 
        (MAX(mr.Reading) - MIN(mr.Reading)) AS 'DaytimeUsage'
    FROM
        DimServiceLocation AS sl
        INNER JOIN FactBill AS fb ON fb.ServiceLocationKey = sl.ServiceLocationKey
        INNER JOIN FactMeterRead as mr ON mr.ServiceLocationKey = sl.ServiceLocationKey
        INNER JOIN DimCustomer AS c ON c.CustomerKey = fb.CustomerKey
    WHERE 
        c.class_name = 'Tenant'
        AND sl.ServiceLocationKey = @ServiceLocation
        AND mr.meter_type = @ServiceType
    GROUP BY 
        sl.location_addr, 
        convert(VARCHAR(10),
        mr.read_date,101)
) A

WHERE A.ReadDate >= GETDATE()-@Days 
GROUP BY A.ServiceAddress, convert(VARCHAR(10),A.ReadDate,101)
ORDER BY convert(VARCHAR(10),A.ReadDate,101) DESC

【问题讨论】:

  • 我只知道你想获得昨天的最大阅读量和今天的最小阅读量。这可以通过函数之间的日期来实现
  • 是的。但是,它需要对日期范围内的每个日期执行此操作。因此,4/16 的最大读取和 4/17 的最小读取之间的差异然后是 4/17 的最大读取和 4/16 的最小读取之间的差异......等等。将这些总和添加到现有的 MIN和查询中每个读取日期的最大读取差异。

标签: sql sql-server datetime max min


【解决方案1】:

您似乎可以通过计算昨天和今天的 MAX 之间的差异来解决这个问题,但是这就是我的处理方式。相对于任何给定日期的前一天再次加入同一个表,并在您的内部查询中为此选择 Max/Min。此外,如果您将日期放在内部查询 where 子句中,您返回的数据集将更快更小。

SELECT 
A.ServiceAddress AS 'Service Address', 
  convert(VARCHAR(10),A.ReadDate,101) AS 'Date', 
  SUM(A.[TodayMax]) - SUM(A.[TodayMin]) AS 'Usage',
  SUM(A.[TodayMax]) - SUM(A.[YesterdayMax]) AS 'Usage with extra bit you want' 
FROM 
(
SELECT
    sl.location_addr AS 'ServiceAddress', 
    convert(VARCHAR(10),mr.read_date,101) AS 'ReadDate', 
    MAX(mrT.Reading) AS 'TodayMax',
    MIN(mrT.Reading) AS 'TodayMin',
    MAX(mrY.Reading) AS 'YesterdayMax',
    MIN(mrY.Reading) AS 'YesterdayMin',        
FROM
    DimServiceLocation AS sl
    INNER JOIN FactBill AS fb ON fb.ServiceLocationKey = sl.ServiceLocationKey
    INNER JOIN FactMeterRead as mrT ON mrT.ServiceLocationKey = sl.ServiceLocationKey
    INNER JOIN FactMeterRead as mrY ON mrY.ServiceLocationKey = s1.ServiceLocationKey
                                    AND mrY.read_date = mrT.read_date -1)
    INNER JOIN DimCustomer AS c ON c.CustomerKey = fb.CustomerKey
WHERE 
    c.class_name = 'Tenant'
    AND sl.ServiceLocationKey = @ServiceLocation
    AND mr.meter_type = @ServiceType
    AND convert(VARCHAR(10), mrT.read_date,101) >= GETDATE()-@Days 
GROUP BY 
    sl.location_addr, 
    convert(VARCHAR(10),
    mr.read_date,101)

) A
GROUP BY A.ServiceAddress, convert(VARCHAR(10),A.ReadDate,101)
ORDER BY convert(VARCHAR(10),A.ReadDate,101) DESC

【讨论】:

  • 在实际数据集上运行此命令时,每个 read_date(4/20/15、4/19/15、4/18/15 等)对每一列都有相同的用法(用法和使用额外的位)。它们似乎都是最近的一天,而不是准确到阅读日期。想法?
  • 您能否运行内部查询并查看给定日期在今天的最小值和昨天的最大值之间是否存在差异?也许没有丢失的数据。
  • 是的,我将其扩展回 30 天,并检查了实际读取数据。不幸的是,它为每一行返回相同的结果,而不是根据该行的读取日期计算每一行。
  • 每行是否在所有 30 天内都有相同的结果,或者您的意思是今天和昨天的值在任何一天都相同?
【解决方案2】:

如果您在 sql server 2005 以上,您可以使用 APPLY 运算符。这是文档的链接。 https://technet.microsoft.com/en-us/library/ms175156(v=sql.105).aspx APPLY 操作有两种形式 OUTER APPLY 和 CROSS APPLY - OUTER 像左连接一样工作,而 CROSS 像内连接一样工作。它们允许您为返回的每一行运行一次查询。我设置了自己的示例,以了解您正在尝试做的事情,就在这里,希望对您有所帮助。

http://sqlfiddle.com/#!6/fdb3f/1

CREATE TABLE SequencedValues (
  Location varchar(50) NOT NULL,
  CalendarDate datetime NOT NULL,
  Reading int
  )

INSERT INTO SequencedValues (
  Location,
  CalendarDate,
  Reading
  )
 SELECT
   'Address1',
   '4/20/2015',
   10
UNION SELECT
   'Address1',
   '4/19/2015',
   9
UNION SELECT
   'Address1',
   '4/19/2015',
   20
UNION SELECT
   'Address1',
   '4/19/2015',
   25
UNION SELECT
   'Address1',
   '4/18/2015',
   8
UNION SELECT
   'Address1',
   '4/17/2015',
   7
UNION SELECT
   'Address2',
   '4/20/2015',
   100
UNION SELECT
   'Address2',
   '4/20/2015',
   111
UNION SELECT
   'Address2',
   '4/19/2015',
   50
UNION SELECT
   'Address2',
   '4/19/2015',
   65


SELECT DISTINCT 
    sv.Location,
    sv.CalendarDate,
    sv_dayof.MINDayOfReading,
    sv_daybefore.MAXDayBeforeReading
FROM SequencedValues sv
OUTER APPLY (
    SELECT MIN(sv_dayof_inside.Reading) AS MINDayOfReading
    FROM SequencedValues sv_dayof_inside
    WHERE sv.Location = sv_dayof_inside.Location
    AND sv.CalendarDate = sv_dayof_inside.CalendarDate
    ) sv_dayof
OUTER APPLY (
    SELECT MAX(sv_daybefore_max.Reading) AS MAXDayBeforeReading
    FROM SequencedValues sv_daybefore_max
    WHERE sv.Location = sv_daybefore_max.Location
    AND sv_daybefore_max.CalendarDate IN (
        SELECT TOP 1 sv_daybefore_inside.CalendarDate
        FROM SequencedValues sv_daybefore_inside
        WHERE sv.Location = sv_daybefore_inside.Location
        AND sv.CalendarDate > sv_daybefore_inside.CalendarDate
        ORDER BY sv_daybefore_inside.CalendarDate DESC
        )
    ) sv_daybefore
ORDER BY
    sv.Location,
    sv.CalendarDate DESC

【讨论】:

  • 通过一些调整来解释每天的多次读取,这就像一个魅力!谢谢!我仍然需要验证它是否占没有任何读取的天数,如果您对如何处理有任何建议,请告诉我。基本上,回到最后一个可用的最大读取(如果它发生在前一天以外的任何一天)。如果我是正确的,SELECT TOP 1 应该处理这个,不是吗?
  • @johnwonderbread 你是对的。如果你想说回到前一天,不管它是否是前一天,由于 TOP 1 和 ORDER BY,这将正常工作。但是,如果您只想要前一天,则需要执行 DATEADD -1 或创建一个名为 Calendar 的单独表,其中每个日期都有前一个日期、前一个业务日期,以及您需要的任何其他元信息。
【解决方案3】:

我不确定我是否完全理解您的数据库结构,但我可能有一个解决方案,因此请随时编辑我的答案以适应或更正任何错误。

这个想法是为表 FactMeterRead 使用两个别名。 mrY(Y 代表昨天)和 mrT(T 代表今天)。并通过 read_date 限制来区分它们。 但是,我对您的表的理解不够,无法编写功能齐全的查询。不管怎样,我希望你能通过这个例子得到这个想法。

SELECT
        sl.location_addr AS 'ServiceAddress', 
        convert(VARCHAR(10),mrT.read_date,101) AS 'ReadDate', 
        (MAX(mrY.Reading) - MIN(mrT.Reading)) AS 'DaytimeUsage'
    FROM
        DimServiceLocation AS sl
          INNER JOIN FactMeterRead as mrY ON mrY.ServiceLocationKey = sl.ServiceLocationKey
          INNER JOIN FactMeterRead as mrT ON mrT.ServiceLocationKey = sl.ServiceLocationKey
    WHERE mrY.read_date=DATE_SUB(mrT.read_date,1 DAY)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-12
    • 1970-01-01
    • 2012-07-16
    相关资源
    最近更新 更多