1

我正在使用一个 postgres SQL 数据库,该数据库将军事时间存储为字符(不要问...),我需要更新一个列,该列将时间添加/减去时间本身的列。一些列仅存储小时和分钟(HHMM 或 1005),而一些列将小时、分钟和秒存储为时间戳......所以我只剩下解析和提取数据了。

例如; 列TaxiTime仅存储小时和分钟,如下所示: 10050050将等于第一个条目的 10 小时 5 分钟和第二个条目的 0 小时 50 分钟。

我是 postgresql 的新手,但不是 SQL 的新手,所以我已经能够鼓起这个查询:

UPDATE "myTable".tgtplsel ts
SET etd = subquery.new_etd
FROM((SELECT 
        replace(((((interval '1 hours' * substring(starttime, 1, 2)::integer) +
        (interval '1 minutes' * substring(starttime, 3, 2)::integer)) + 

        ((interval '1 hours' * ((SELECT zuluoffset FROM "myTable".airports WHERE name = 'KABC'))) +
        (interval '1 minutes' * 0) +
        (interval '1 seconds' * 0))) +
        ((interval '1 hours' * substring(ts.taxidelay, 1, 2)::integer) +
        (interval '1 minutes' * substring(ts.taxidelay, 3, 2)::integer)) +

        ((interval '1 hours' * substring(ts.etd, 1, 2)::integer) +
        (interval '1 minutes' * substring(ts.etd, 3, 2)::integer))::time)::char(5), ':', '') as new_etd, ts.exerciseid

      FROM "myTable".tgtplsel as ts
      INNER JOIN "myTable".exerparm as ep ON ts.exerciseid = ep.exerciseid
      WHERE ts.exerciseid = 11
      AND ts.taxidelay is not null
      AND length(ts.taxidelay) > 0)) as subquery
WHERE ts.exerciseid = subquery.exerciseid 
AND ts.exerciseid = 11
AND ts.taxidelay is not null
AND length(ts.taxidelay) > 0

我认为这会更新所有符合WHEREandAND子句标准的行(当前为 16 行),但我错了。这将更新所有满足此条件的记录,但仅使用子查询的 FIRST 值。为什么?

子查询本身返回 16 行,计算正确,例如:

0245
1050
0920
0345
1210
etc.

但是当我把它变成一个UPDATE语句时,它会更新所有 16 行0245

为什么?更重要的是,我怎样才能让它用正确的值更新每一行?

更新

首先,我很抱歉没有提供所有信息....我在下班的路上把它放在一起,很着急...现在....回答一些问题...
PostgreSQL 版本是8.3 .3
“myTable” 不是真正的模式名称,但我不得不用其他名称替换原来的名称。

关于我要完成的工作,这里有一点点破败:我正在尝试使用从其他几个字段计算的值来更新表的ETD列。tgtplsel我们用来生成这个新值的公式是:

ETD = ((starttime - zuluoffset) + taxidelay + ETD)

所以如果我的 ETD 是 00:30,starttime 是 23:00,zuluoffset 是 -5,taxidelay 是 0005,那么新的 ETD 应该是 ((23:00 - (-5)) + 00:05 + 00:30 ) 或 18:35。将这些转换为timeorinterval类型的原因是因为开始时间可能是 02:00 或凌晨 2 点,如果 zuluoffset 是 -5,那么我需要正确计算到 21:00,而不是 -03:00。同样,其中一些列是类型numeric,而另一些则character......完全不在我的手中,无法做任何事情来修复它,所以我正在尽我最大的努力解决它。

我能够创建一个 SQLFiddle(感谢那些建议它的人)并输入一些数据....虽然我的一些 ETD 值应该是0000,如果我SELECT在小提琴中做一个,它们会以0. 无论如何,这是SQL Fiddle

4

3 回答 3

1

基本查询

抛开复杂的计算不谈,您可以在很大程度上简化UPDATE. 没有子查询需要她:

UPDATE "myTable".tgtplsel ts
SET    etd = <complex calculation>
FROM   "myTable".exerparm ep
WHERE  ep.exerciseid = ts.exerciseid 
AND    ts.exerciseid = 11
AND    ts.taxidelay is not null
AND    length(ts.taxidelay) > 0

认真的吗?“myTable”是您的架构名称吗?

计算

你可怕的表情(又名<complex calculation>):

TL;DR

可以改写为:

 to_char( to_timestamp(ep.starttime, 'HH24mi')::time
          + (SELECT interval '1h' * zuluoffset
             FROM "myTable".airports WHERE name = 'KABC')
          + to_timestamp(ts.taxidelay, 'HH24mi')::time::interval      
          + to_timestamp(ts.etd, 'HH24mi')::time::interval
         , 'HH24mi')
  • 你真的应该使用timestamp,timeinterval开始。那才是正道。但你似乎知道这一点。
    既然你被你不幸的设置困住了,我也会帮助你解决黑暗的一面。我很灵活。:) (自引用!)

  • to_timestamp()可以将 atime作为输入。它预先设置了year 1,这在这里无关紧要,因为我们time立即转换为 (这反过来又可以转换为interval)。

  • 使用该函数to_char()将 a 转换intervaltext.

功能

为了让你在黑暗面的生活更明亮一点,创建一些这些转换函数,它们可以在有或没有秒的情况下工作:

将“军事时间”转换为时间:

CREATE FUNCTION f_mt2time(text)
  RETURNS time AS
$func$
SELECT CASE WHEN length($1) = 4
        THEN to_timestamp($1, 'HH24mi')::time
        ELSE to_timestamp($1, 'HH24miss')::time
       END
$func$ LANGUAGE sql IMMUTABLE;

将时间转换为“军事时间”:

CREATE FUNCTION f_time2mt(time)
  RETURNS text AS
$func$
SELECT CASE WHEN extract(sec FROM $1) = 0
        THEN to_char($1, 'HH24MI')
        ELSE to_char($1, 'HH24MISS')
       END
$func$ LANGUAGE sql IMMUTABLE;

->函数的SQLfiddle演示

把它们放在一起

应用辅助。zuluoffset函数并将表达式中的常量子查询提取到FROM列表中。更清晰更快:

UPDATE "myTable".tgtplsel ts
SET    etd = f_time2mt(
                f_mt2time(ep.starttime)
              + z.zulu
              + f_mt2time(ts.taxidelay)::interval      
              + f_mt2time(ts.etd)::interval)
FROM   "myTable".exerparm ep
CROSS   JOIN (
   SELECT interval '1h' * zuluoffset AS zulu
   FROM   "myTable".airports
   WHERE  name = 'KABC'
   ) z
WHERE  ep.exerciseid = ts.exerciseid 
AND    ts.exerciseid = 11
AND    ts.taxidelay is not null
AND    length(ts.taxidelay) > 0;

现在好多了。

于 2013-05-07T00:05:56.843 回答
0

不需要任何字符串修改。使用extractordate_partdate_trunc函数获取部分时间戳。当您要存储部分时间戳(如小时+天+秒)时,请根据需要使用time或数据类型。interval

我没有尝试写一个替换,因为你没有发布你正在操作的数据/表,也没有清楚地解释整个查询应该做什么。不过,我强烈怀疑,当您摆脱字符串操作以支持正确的日期/时间处理功能时,您将获得更好的结果。

于 2013-05-07T00:02:33.720 回答
-1

我建议改变:

AND length(ts.taxidelay) > 0)) as subquery
WHERE ts.exerciseid = subquery.exerciseid 

AND length(ts.taxidelay) > 0)) as subquery
ON ts.exerciseid = subquery.exerciseid 

此外,为了进行故障排除,请将整个查询作为选择而不是更新运行。它将帮助您了解正在发生的事情。

于 2013-05-06T23:36:33.780 回答