【问题标题】:SQL Server 2008 - Converting decimal(4,0) to datetimeSQL Server 2008 - 将十进制(4,0)转换为日期时间
【发布时间】:2015-01-28 21:08:08
【问题描述】:

我花了最后 40 分钟在这里和其他网站上寻找答案。问题背景:我的 ERP 系统记录了人工预订的日期和时间。它将预订日期记录为日期时间,将预订时间记录为decimal(4)。我正在尝试转换时间(十进制到日期时间),因此日期和时间是一个值。

样本数据:

Job Op  Date            Time
----------------------------------
1   10  06/01/2015  254
1   20  06/01/2015  254
1   20  06/01/2015  542
1   20  06/01/2015  1347
1   20  07/01/2015  1340
1   30  07/01/2015  1408
1   30  07/01/2015  1340
1   30  08/01/2015  1037
1   40  06/01/2015  543
1   40  06/01/2015  1348
1   40  08/01/2015  1038
1   50  07/01/2015  1219
1   50  08/01/2015  1039
1   60  07/01/2015  1220
1   60  10/01/2015  1054
1   60  12/01/2015  859

同一操作一天可以有多个预订。我的最终目标是找到作业号上的操作的最早预订和最新预订。

Job Op  Start       End         StartTime  FinishTime
-------------------------------------------------------------
1   10  06/01/2015  06/01/2015      254       254
1   20  06/01/2015  07/01/2015      254      1340
1   30  07/01/2015  08/01/2015     1340      1037
1   40  06/01/2015  08/01/2015      543      1038
1   50  07/01/2015  08/01/2015     1219      1039
1   60  07/01/2015  12/01/2015     1220       859

【问题讨论】:

  • 您使用的是什么数据库?请适当地标记问题。

标签: sql sql-server sql-server-2008


【解决方案1】:

您可以使用row_number() 和条件聚合来做到这一点:

select s.job, s.op,
       max(case when seqnum = 1 then date end) as start,
       max(case when seqnum = cnt then date end) as end,
       max(case when seqnum = 1 then time end) as start_time,
       max(case when seqnum = cnt then time end) as end_time
from (select s.*,
             row_number() over (partition by job, op order by date, time) as seqnum,
             count(*) over (partition by job, op) as cnt
      from sample s
     ) s
group by job, op;

请注意:您的问题标题似乎与您要完成的任务无关。

【讨论】:

    【解决方案2】:

    你从十进制(4, 0)到时间的转换是:

    DATEADD(MINUTE, (FLOOR([Time] / 100) * 60) + ([Time] % 100), '00:00')
    

    这只是将您的总分钟数添加到 00:00 以获得时间,您的总分钟数计算为 (hours * 60) + minutes where

    Hours = FLOOR([Time] / 100)
    Minutes = ([Time] % 100)
    

    但是如果为时不晚,您应该完全放弃这种方法。

    在 SQL Server 中,很少需要单独存储日期和时间列,最好的方法是使用单个 DATETIME2 列,如果出于任何原因需要单独的列(例如索引),请使用计算列,例如

    ALTER TABLE T ADD StartDate AS CAST(StartDateTime AS DATE);
    

    如果您必须至少将两列分开存储,您应该使用TIME 数据类型来存储时间,而不是DECIMAL(4, 0)。解决您的情况的最佳方法是解决实际问题,即不正确的数据类型,而不是您提出的问题。如果你这样做,你会得到如下数据:

    Job Op  JobDateTime
    ----------------------------------
    1   10  06/01/2015 02:54
    1   20  06/01/2015 02:54
    1   20  06/01/2015 05:42
    1   20  06/01/2015 13:47
    1   20  07/01/2015 13:40
    1   30  07/01/2015 14:08
    1   30  07/01/2015 13:40
    1   30  08/01/2015 10:37
    1   40  06/01/2015 05:43
    

    等等

    那么你的查询就这么简单

    SELECT  JOb, Op, FirstJob = MIN(JobDateTime), LastJob = MAX(JobDateTime)
    FROM    T
    GROUP BY JOb, Op;
    

    进行此更正的代码是:

    ALTER TABLE [YourTable] ADD JobDateTime DATETIME2 NOT NULL;
    
    UPDATE  [YourTable]
    SET     JobDateTime = DATEADD(MINUTE, (FLOOR([Time] / 100) * 60) + ([Time] % 100), CAST([Date] AS DATETIME)));
    
    ALTER TABLE [YourTable] DROP COLUMN [Date];
    ALTER TABLE [YourTable] DROP COLUMN [Time];
    

    如果您已经有很多使用这些列的现有代码,您可以使用计算列为您进行计算:

    ALTER TABLE YourTable
    ADD JobDateTime AS DATEADD(MINUTE, (FLOOR([Time] / 100) * 60) + ([Time] % 100), CAST([Date] AS DATETIME));
    

    例如

    CREATE TABLE #T (Job INT, Op INT, [Date] DATE, [Time] DECIMAL(4, 0));
    INSERT #T (Job, Op, [Date], [Time])
    VALUES
        (1, 10, '2015-06-01', 254),
        (1, 20, '2015-06-01', 254),
        (1, 20, '2015-06-01', 542),
        (1, 20, '2015-06-01', 1347),
        (1, 20, '2015-07-01', 1340),
        (1, 30, '2015-07-01', 1408),
        (1, 30, '2015-07-01', 1340),
        (1, 30, '2015-08-01', 1037),
        (1, 40, '2015-06-01', 543),
        (1, 40, '2015-06-01', 1348),
        (1, 40, '2015-08-01', 1038),
        (1, 50, '2015-07-01', 1219),
        (1, 50, '2015-08-01', 1039),
        (1, 60, '2015-07-01', 1220),
        (1, 60, '2015-10-01', 1054),
        (1, 60, '2015-12-01', 859);
    
    ALTER TABLE #T
    ADD JobDateTime AS DATEADD(MINUTE, (FLOOR([Time] / 100) * 60) + ([Time] % 100), CAST([Date] AS DATETIME));
    
    SELECT  Job, 
            Op, 
            FirstJob = MIN(JobDateTime), 
            LastJob = MAX(JobDateTime)
    FROM    #T
    GROUP BY Job, Op;
    

    如上所述,我建议只将日期和时间作为单列返回,但如果您确实希望它们作为单独的列,那么您至少应该使用 TIME 类型:

    无论哪种方式,我都建议返回完整的日期时间,或者至少使用日期和时间列,例如

    SELECT  Job, 
            Op, 
            [Start] = CAST(MIN(JobDateTime) AS DATE), 
            [End] = CAST(MAX(JobDateTime) AS DATE), 
            StartTime = CAST(MIN(JobDateTime) AS TIME), 
            EndTime = CAST(MAX(JobDateTime) AS TIME)
    FROM    #T
    GROUP BY Job, Op;
    

    如果您无法进行任何架构更改,您可以在实际查询中包含公式(在您使用十进制存储时间之后打孔您的 dba):

    SELECT  Job, 
            Op, 
            FirstJob = MIN(DATEADD(MINUTE, (FLOOR([Time] / 100) * 60) + ([Time] % 100), CAST([Date] AS DATETIME))),
            LastJob = MAX(DATEADD(MINUTE, (FLOOR([Time] / 100) * 60) + ([Time] % 100), CAST([Date] AS DATETIME)))
    FROM    #T
    GROUP BY Job, Op;
    

    【讨论】:

      猜你喜欢
      • 2021-01-04
      • 2021-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 2022-09-29
      • 2011-08-29
      • 1970-01-01
      相关资源
      最近更新 更多