【问题标题】:how to correctly use case in where clause如何在where子句中正确使用case
【发布时间】:2012-03-20 19:56:07
【问题描述】:

所以我有一个程序,我目前正在调试过程中,我已将其范围缩小到这个 select 语句。

注意:to_date(''), 3300, 5220 表示来自参数的内容。

现在我们要做的就是获取时间戳参数并减去偏移值

偏移量是自一周开始以来经过的分钟数,其中周日午夜 = 0。(因此,如果是周一午夜,则偏移量 = 1440)。

当从参数中减去偏移量时,您就得到了一周的开始。然后,您从已经预先确定的表中获取偏移值,并将该值添加到一周的开始以获取时间戳。

这样做是为了获取轮班的开始日期和结束日期。

我的原始代码在下面没有问题,但是它缺少周六进入周日的边界条件。

SELECT SHIFT_ID_PK, SHIFT_NAME_FK,
         SHIFT_START_DAY, SHIFT_START_TIME,
         SHIFT_END_DAY, SHIFT_END_TIME, 
         SITE_ID_FK, SHIFT_DAY_ID,
         STARTOFFSET, ENDOFFSET,
         TO_TIMESTAMP_TZ(TO_CHAR((PSTARTTIMESTAMP - (VSTARTOFFSET  / 24 / 60)) + (STARTOFFSET / 24 / 60), 'YYYY-MM-DD HH:MI:SS AM'),  'YYYY-MM-DD HH:MI:SS AM TZH:TZM') as SHIFT_START_DATE,
         TO_TIMESTAMP_TZ(TO_CHAR((PENDTIMESTAMP -  (VENDOFFSET / 24 / 60)) + (ENDOFFSET   / 24 / 60), 'YYYY-MM-DD HH:MI:SS AM') ,'YYYY-MM-DD HH:MI:SS AM TZH:TZM') as SHIFT_END_DATE
  from   shift_tbl
  WHERE
         ENDOFFSET >= VSTARTOFFSET
  and    STARTOFFSET < VENDOFFSET 
  order by shift_start_date asc;

现在我想出处理这个边界条件的方法是低于我在脚本中测试的。

   declare
  VSTARTOFFSET integer;
  VENDOFFSET integer;
  SHIFTOFFSET integer;
  PSTARTTIMESTAMP timestamp;
  PENDTIMESTAMP timestamp;
  begin 
    VSTARTOFFSET := 10020;
    VENDOFFSET := 420; 
    PSTARTTIMESTAMP := TO_DATE('3/17/2012 23:00', 'mm/dd/yyyy hh24:mi');
    PENDTIMESTAMP :=   TO_DATE('3/18/2012 7:00', 'mm/dd/yyyy hh24:mi');

  SELECT SHIFT_ID_PK, SHIFT_NAME_FK,
         SHIFT_START_DAY, SHIFT_START_TIME,
         SHIFT_END_DAY, SHIFT_END_TIME, 
         SITE_ID_FK, SHIFT_DAY_ID,
         STARTOFFSET, ENDOFFSET,
         TO_TIMESTAMP_TZ(TO_CHAR((PSTARTTIMESTAMP - (VSTARTOFFSET / 24 / 60)) + (STARTOFFSET / 24 / 60), 'YYYY-MM-DD HH:MI:SS AM'),'YYYY-MM-DD HH:MI:SS AM TZH:TZM') as SHIFT_START_DATE,  
         TO_TIMESTAMP_TZ(TO_CHAR((PENDTIMESTAMP- (VENDOFFSET / 24 / 60)) + (ENDOFFSET   / 24 / 60), 'YYYY-MM-DD HH:MI:SS AM'),'YYYY-MM-DD HH:MI:SS AM TZH:TZM') AS SHIFT_END_DATE
  from   SHIFT_TBL
  where
    case 
      when SHIFT_START_DAY = 7 and  SHIFT_END_DAY = 1 then          
                 SHIFTOFFSET:= ENDOFFSET + 10080; 
          and    VENDOFFSET := VENDOFFSET + 10080;
      else
                 SHIFTOFFSET := ENDOFFSET;
    end
                 SHIFTOFFSET >= VSTARTOFFSET
          and    STARTOFFSET < VENDOFFSET 
    order by SHIFT_START_DATE asc; 
  end;

如您所见,我不确定如何处理 where 子句中的 case 语句。基本上我想做的是,如果开始日是星期六,结束日是星期日,然后将 10080(一周)添加到结束偏移量/销售偏移量,如果它不满足该条件,则使用原始值。

基本上我的问题相当简单......我相信但我很难获得解决方案。所以我想知道的是如何在 where 子句中正确使用 case 语句。如果我不打算在 where 子句中使用这种形式的 case 语句,我将如何设置这个 select 语句。

非常感谢任何帮助或建议。 谢谢。

【问题讨论】:

  • 这似乎不必要地复杂,但也许我错过了一些东西。基本上,您正在尝试计算开始和结束时间戳,我假设此数据表中没有存储时间戳?我看到您的日期字段是一个整数,从 1 代表星期日到 7 代表星期六。您的轮班时间字段中存储了哪些类型的数据?
  • 不能在 SQL WHERE 子句中执行 PL/SQL 赋值语句。

标签: sql oracle plsql


【解决方案1】:

你不需要在 WHERE 子句中设置任何变量,实际上你也做不到。 您要做的是编写正确的逻辑谓词(即返回 true 或 false 的表达式)来描述您想要获取的行。

这里有 2 个示例,我将尝试如何定义它(据我了解您的要求):

  1. 没有案例:

    WHERE
    ( SHIFT_START_DAY = 7 and  SHIFT_END_DAY = 1  AND ENDOFFSET + 10080 >= VSTARTOFFSET and STARTOFFSET < VENDOFFSET + 10080) OR
    ( NOT (SHIFT_START_DAY = 7 and  SHIFT_END_DAY = 1) AND SHIFTOFFSET >= VSTARTOFFSET and STARTOFFSET < VENDOFFSET )
    
  2. 案例:

    WHERE
    (CASE WHEN SHIFT_START_DAY = 7 and  SHIFT_END_DAY = 1 THEN ENDOFFSET + 10080 ELSE ENDOFFSET END) >= VSTARTOFFSET
    AND    STARTOFFSET < (CASE WHEN SHIFT_START_DAY = 7 and  SHIFT_END_DAY = 1 THEN VENDOFFSET + 10080 ELSE VENDOFFSET END)
    

我没有调试这个表达式,所以不要指望它们能工作 ;),但我希望你已经明白了。

【讨论】:

  • 这正是我想要的!我想我只是有一个严重的大脑放屁没有抓住这个。谢谢!
【解决方案2】:

我建议不要尝试在 WHERE 子句中设置变量,而是将基本查询放入游标并使用它来驱动循环中的主查询。这将允许您为每次迭代设置查询之外的变量。它看起来像这样:

declare
--variables
cursor c_shifts is
select SHIFT_ID_PK, SHIFT_START_DAY, SHIFT_END_DAY
from SHIFT_TBL;
begin
for r_result in c_shifts
loop
if r_result.SHIFT_START_DAY = 1 and r_result.SHIFT_END_DAY = 7 then
  --set variables to values for special case shifts
else
  --set variables for all other cases
end if;
   --run your query for the particular result in this loop iteration based upon r_result.SHIFT_ID_PK, using the variables you set above
   --save results to a staging table, directly dbms_output from the block, etc., as needed
end loop;
   --commit results if saving to a staging table, etc., as needed once the loop completes
end;

【讨论】:

    【解决方案3】:

    不要计算,查询:)

    建议:考虑使用auxiliary calendar table

    为什么要考虑使用辅助日历表?

    日历表可以更轻松地开发解决方案 任何涉及日期的商业模式。最后我检查了,这个 对某些人来说,几乎涵盖了您能想到的任何商业模式 程度。最终需要冗长的持续问题, 复杂而低效的方法包括以下问题:

    • x 和 y 之间有多少个工作日?

    • 3 月的第二个星期二和 4 月的第一个星期五之间的所有日期是什么时候?

    • ...

    ...也许是a column for julianized business days

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-14
      • 1970-01-01
      • 2021-03-14
      相关资源
      最近更新 更多