棘手的数据输入验证 SQL 查询

Tricky data entry validation SQL query

我正在尝试开发一个查询来验证用户数据输入,但我被卡住了。基本上,我们正在处理有关每天 24 小时复合水样的信息。用户将输入 "COLDATE",这是复合的结尾,并以 DATETIME 格式存储,如“2015-03-02 04:00:00.000”。然后他们将输入 "Compstartdate",它是 varchar(8),看起来像“03/02/15”。最后,他们将输入 "Compstarttime",它是 varchar(5),看起来像“04:01”。

别怪我,我没有这样设置,让我们假设修复数据类型不是一个选项。

我处理的规则是一天的 "Compstart(date/time)" 需要与前一天的 "COLDATE" 匹配。到目前为止,我只能弄清楚如何查看 "COLDATE(day) - 1 day" 是否等于 "Compstartdate(day)"。换句话说,我可以轻松地在一条记录内进行逻辑比较,但我不知道如何比较两条记录。

此外,我们只讨论 2000 条记录,因此性能方面的考虑并不重要,我使用 case 语句就证明了这一点。我的意思是,涉及游标或 while 循环的解决方案对我来说是完全可以接受的。

这是我目前的情况:

SELECT S.[SAMPNO]
      ,S.[LOCCODE]
      ,S.[COLDATE]
      ,U.[Compstartdate]
      ,U.[Compstarttime]

  FROM [dbo].[SAMPLE] as S
  JOIN [dbo].[SUSERFLDS] as U
  on S.SAMPNO = U.SAMPNO

  Where 
    Case
        When DATEPART(DAY, Convert(VARCHAR(10),U.Compstartdate,101)) != 
             DATEPART(DAY, DATEADD(DAY, -1, S.COLDATE))

        Then 'Yes' 

        ELSE 'NO'

    END ='YES' 

编辑:让我试着更好地解释这个问题。如果我今天采集了一个24小时的复合样本,并将样本的信息输入数据库,我将输入我采集样本的date/time(复合结束)和date/time复合开始了。因为它是 24 小时合成,所以今天样本的开始 date/time 应该等于昨天样本的结束时间 (COLDATE)。所以我需要取两个具有相同 LOCCODE 但 COLDATE 相隔一天的样本。然后查看较早样本的 COLDATE 是否等于较晚样本的 Compstartdate/time。

编辑 #2:这是一些示例数据。

create table [SAMPLE] (
  SAMPNO   int,
  LOCCODE  char(7),
  COLDATE  datetime
);

create table SUSERFLDS (
 SAMPNO        int,
 Compstartdate char(8),
 Compstarttime char(5)
);

SET DATEFORMAT mdy;


insert into [SAMPLE] values (11,'Sample1','2015-03-02 04:00:00.000');
insert into [SAMPLE] values (12,'Sample1','2015-03-03 04:00:00.000');
insert into [SAMPLE] values (13,'Sample1','2015-03-04 04:00:00.000');
insert into [SAMPLE] values (14,'Sample1','2015-03-05 04:00:00.000');

insert into SUSERFLDS values (11, '03/01/15', '04:00');
insert into SUSERFLDS values (12, '03/02/15', '04:00');
insert into SUSERFLDS values (13, '03/03/15', '05:00');
insert into SUSERFLDS values (14, '03/04/15', '04:00');
--Compstartdate/time for SAMPNO 12
--does match COLDATE for SAMPNO 11
--Compstartdate/time for SAMPNO 13 
--should match COLDATE for SAMPNO 12

我认为您感到困惑 - 没有必要遍历 table - 这实际上就是连接的作用。

遗憾的是,SQLFiddle 目前似乎遇到了困难。这就是我要设置的示例:

create table SAMPLE (
  SAMPNO   int,
  LOCCODE  char(1),
  LOCDESCR char(1),
  LOGBATCH char(1),
  LOGUSER  char(1),
  COLDATE  datetime
);

create table SUSERFLDS (
 SAMPNO        int,
 Compstartdate char(8),
 Compstarttime char(5)
);

SET DATEFORMAT mdy;

insert into SAMPLE values (1, 'x','x','x','x','2015-03-01 04:00:00.000');
insert into SAMPLE values (2, 'x','x','x','x','2015-03-02 04:00:00.000');
insert into SAMPLE values (3, 'x','x','x','x','2015-03-03 04:00:00.000');
insert into SAMPLE values (4, 'x','x','x','x','2015-03-04 04:00:00.000');
insert into SAMPLE values (5, 'x','x','x','x','2015-03-05 04:00:00.000');

insert into SUSERFLDS values (2, '03/02/15', '04:00');
insert into SUSERFLDS values (3, '03/04/15', '04:00');
insert into SUSERFLDS values (4, '03/05/15', '04:00');
insert into SUSERFLDS values (5, '03/06/15', '05:00');

set dateformat mdy;

with example as (
select CAST( compstartdate +' '+ compstarttime as datetime) as compdatetime
from superflds)
select *
from sample
where  1 = (
  select count(*)
  from example
  where DATEPART(dy, compdatetime) = DATEPART(dy, coldate) + 1
  and   DATEPART(hh, compdatetime) = DATEPART(hh, coldate) 
  and   DATEPART(mi, compdatetime) = DATEPART(mi, coldate) 
  )

请在评论中提出任何问题或添加说明。

您只是说 "previous day" 的日期和时间必须匹配。但是随后您匹配 SAMPNO 值。我猜 SUSERFLDS 输入的是第二天采样的时间。

您的查询开始正常,只是您的参考 table 似乎是 SUSERFLDS,所以它应该是列出的第一个 table。然后您想匹配来自 SAMPLE 的具有相同样本编号和相同日期和时间的记录。

要进行比较,您可以将 SAMPNO 日期时间转换为与日期字符串 (mm/dd/yy) 格式相同的字符串,然后提取日期部分和时间部分以与来自SUSERFLDS,或将日期和时间字符串转换为日期时间值并与 SAMPNO 日期时间进行比较。

使用后一个选项,这里是查询:

select  U.[SAMPNO],
        U.[Compstartdate],
        U.[Compstarttime],
        S.[LOCCODE],
        S.[LOCDESCR],
        S.[LOGBATCH],
        S.[LOGUSER],
        S.[COLDATE]
from    SUSERFLDS u
left join SAMPLE  s
    on  s.SampNo  = u.SampNo
    and s.ColDate = Cast( u.CompStartdDate + ' ' + u.CompStartTime as datetime );

注意我使用了外连接。这样一来,您仍然会看到所有 SUSERFLDS 条目,但带有 NULL,其中样本数据应该是那些日期时间不匹配的样本。这会立即指示日期和时间不匹配的地方。您可以添加

where  s.SampNo is null

您将 获得示例日期时间不匹配的 SUSERFLDS 条目。在这种情况下,您可以省略 select 列表中的所有 S.XXX 字段,因为它们将始终只显示 NULL。

但是,这并不能真正告诉您太多信息。如果您能看到实际的样本日期,但只看到不匹配的日期,岂不是更好?

在这种情况下,删除 "left" 以执行内部联接并将联接条件更改为 "not equals"。像这样:

select  U.[SAMPNO],
        U.[Compstartdate],
        U.[Compstarttime],
        S.[LOCCODE],
        S.[LOCDESCR],
        S.[LOGBATCH],
        S.[LOGUSER],
        S.[COLDATE]
from    SUSERFLDS u
join    SAMPLE    s
    on  s.SampNo  = u.SampNo
    and s.ColDate <> Cast( u.CompStartdDate + ' ' + u.CompStartTime as datetime );

现在您可以看到 SUSERFLDS 日期和时间值以及 SAMPLE 日期时间值,但仅限于那些不匹配的条目。这让你看到问题是什么。

终于想通了。这是为我提供所需内容的查询:

set dateformat mdy;
With CTE1 as 
(
Select  S1.SAMPNO as SAMPNO1
        ,S1.COLDATE as COLDATE1
        ,S1.LOCCODE as LOCCODE1
        ,CAST( U1.Compstartdate +' '+ U1.Compstarttime as datetime) as Compstart1
From [SAMPLE] as S1 join
    [SUSERFLDS] as U1 on S1.SAMPNO = U1.SAMPNO
),
CTE2 as 
(
Select  S2.SAMPNO as SAMPNO2
        ,S2.COLDATE as COLDATE2
        ,S2.LOCCODE as LOCCODE2
        ,CAST( U2.Compstartdate +' '+ U2.Compstarttime as datetime) as Compstart2
From [SAMPLE] as S2 join
    [SUSERFLDS] as U2 on S2.SAMPNO = U2.SAMPNO
)
SELECT LOCCODE1
      ,SAMPNO1
      ,SAMPNO2
      ,COLDATE1
      ,COLDATE2
      ,Compstart1
From CTE1 join
        CTE2 on LOCCODE1 = LOCCODE2 and 
            COLDATE2 = DATEADD(Day, -1, COLDATE1)
Where Compstart1 != COLDATE2

如果您发现任何致命缺陷,请告诉我。