SQL Server - 查找不包括年份

时间:2016-06-14 07:42:53

标签: sql sql-server

我有一个包含员工信息的表EMPLOYEE,如下所述:

ID     NAME     DOB
1      ABC      1974-01-01
2      BDS      1984-12-31
3      QWE      1959-05-27

and so on

我想列出DOB在给定范围内的所有员工。

select * from EMPLOYEE where DOB BETWEEN '1970-01-01' AND '1980-02-27'

我有一个过滤条件'包括年度比较',当选择'否'时,员工DOB日和DOB月 只应考虑进行比较。 而不是年份。

例如:如果我输入日期范围为'1970-01-01'和'1980-02-27'并且过滤器被选为'否'则它应该只搜索那些员工 其DOB大于等于JAN-01且小于等于FEB-27。

选择'是'时,它只是上述查询中提到的日期范围。

这是我到目前为止所尝试的内容:

select * from EMPLOYEE where DOB BETWEEN '1970-01-01' AND '1980-02-27'
AND MONTH(DOB) >= CASE WHEN 'NO'='NO' THEN MONTH('1970-01-01')
ELSE MONTH(DOB) END
AND MONTH(DOB) <= CASE WHEN 'NO'='NO' THEN MONTH('1980-02-27')
ELSE MONTH(DOB) END
AND DAY(DOB) >= CASE WHEN 'NO'='NO' THEN DAY('1970-01-01')
ELSE DAY(DOB) END
AND DAY(DOB) <= CASE WHEN 'NO'='NO' THEN DAY('1980-02-27')
ELSE DAY(DOB) END

当我通过FROM日期的月份数小于TO日期月份的日期范围时,它会起作用。

例如: 当我将日期范围作为'1970-12-01'传递给'1980-01-31'时,它不起作用。 它应列出DOB在DEC和JAN月份的员工。

请帮助。

8 个答案:

答案 0 :(得分:2)

DECLARE @includeYear bit = 0, -- if 0 - we don't include year, 1 - include 
        @dateFrom date ='1970-12-01',
        @dateTo date ='1980-05-30'

IF @includeYear = 1 
BEGIN
    SELECT e.*
    FROM EMPLOYEE e
    INNER JOIN (SELECT @dateFrom as dF, @dateTo as dT) d
        ON e.DOB BETWEEN dF AND dT
END
ELSE 
BEGIN
    SELECT e.*
    FROM EMPLOYEE e
    INNER JOIN (SELECT @dateFrom as dF, @dateTo as dT) d
        ON e.DOB BETWEEN 
        (CASE WHEN MONTH(dF) > MONTH(dT) 
                THEN  DATEADD(year,YEAR(e.DOB)-YEAR(d.dF)-1,dF)
                ELSE  DATEADD(year,YEAR(e.DOB)-YEAR(d.dF),dF) END)
                AND DATEADD(year,YEAR(e.DOB)-YEAR(d.dT),dT)
        OR e.DOB BETWEEN DATEADD(year,YEAR(e.DOB)-YEAR(d.dF),dF) AND
        (CASE WHEN MONTH(dF) > MONTH(dT) 
                THEN  DATEADD(year,YEAR(e.DOB)-YEAR(d.dT)+1,dT)
                ELSE  DATEADD(year,YEAR(e.DOB)-YEAR(d.dT),dT) END)
END

对于

dateFrom    dateTo
1970-12-01  1980-01-30

输出:

ID  NAME    DOB
1   ABC     1974-01-01
2   BDS     1984-12-31

对于

dateFrom    dateTo
1970-05-01  1980-06-30

输出:

ID  NAME    DOB
3   QWE     1959-05-27

有关

dateFrom    dateTo
1970-05-01  1980-05-30

输出:

ID  NAME    DOB
3   QWE     1959-05-27

答案 1 :(得分:2)

样本数据;

DECLARE @Date_From date; SET @Date_From = '1970-12-01'
DECLARE @Date_To date; SET @Date_To = '1974-01-31'
DECLARE @IncludeYear bit; SET @IncludeYear = 0

CREATE TABLE #Employee (ID int, Name varchar(10), DOB date)
INSERT INTO #Employee (ID, Name, DOB)
VALUES
(1,'ABC','1974-01-01')
,(2,'BDS','1984-12-31')
,(3,'QWE','1959-05-27')

这是我所提出的问题。试图弥补每一种可能性。

SELECT
e.ID
,e.Name
,e.DOB
FROM #Employee e
WHERE
    (
    @IncludeYear = 1
    AND
    DOB BETWEEN @Date_From AND @Date_To
    )
OR
(
@IncludeYear = 0
AND
(
    (
    DATEPART(DAYOFYEAR, @Date_From) = DATEPART(DAYOFYEAR, @Date_To)
    AND 
    DATEPART(DAYOFYEAR, DOB) = DATEPART(DAYOFYEAR, @Date_To)
    )
OR
    (
    DATEPART(DAYOFYEAR, @Date_From) < DATEPART(DAYOFYEAR, @Date_To)
    AND
    DATEPART(DAYOFYEAR, DOB) BETWEEN DATEPART(DAYOFYEAR, @Date_From) AND DATEPART(DAYOFYEAR, @Date_To)
    )
OR
    (
    DATEPART(DAYOFYEAR, @Date_From) > DATEPART(DAYOFYEAR, @Date_To)
    AND
        (
        DATEPART(DAYOFYEAR, DOB) > DATEPART(DAYOFYEAR, @Date_From)
        OR
        DATEPART(DAYOFYEAR, DOB) < DATEPART(DAYOFYEAR, @Date_To)
        )
    )
)
)
  • where子句的第一部分检查@date_from和@date_to 是相同的日期,然后只返回这些。
  • 第二部分检查@date_from的日期是否在之前 @date_to。如果确实如此,则在这些日子之间返回所有内容 年。
  • 最后部分检查@date_to的日期是否到来 @date_from然后它将获得一年中的所有日期 @date_from或之前@date_to

这个的结果就是这样;

ID  Name    DOB
1   ABC     1974-01-01
2   BDS     1984-12-31

答案 2 :(得分:0)

尝试功能{{1}}
如果第一个日期的年份小于第二个日期的日期,那么DOB的年份应该在指定的年份之间。
否则,DOB的年度应小于第二个日期的日期或大于第一个日期的日期。

我希望自己表达得很好。

答案 3 :(得分:0)

我没有逐案处理,拆解和重新组装部分日期,而是试图让生活更轻松:

declare @t table (ID int not null, Name varchar(17) not null, DOB date not null)
insert into @t(ID,NAME,DOB) values
(1,'ABC','19740101'),
(2,'BDS','19841231'),
(3,'QWE','19590527')

declare @Start date
declare @End date
declare @IncludeYear bit

select @Start='19701201',@End='19800131',@IncludeYear=0

;With Normalized as (
    select
        ID,
        Name,
        CASE WHEN @IncludeYear=1 THEN DOB
            ELSE DATEADD(year,DATEDIFF(year,DOB,'20000101'),DOB)
        END as DOB,
        CASE WHEN @IncludeYear=1 THEN @Start
            ELSE DATEADD(year,DATEDIFF(year,@Start,'20000101'),@Start)
        END as StartRange,
        CASE WHEN @IncludeYear=1 THEN @End
            ELSE DATEADD(year,DATEDIFF(year,@End,'20000101'),@End)
        END as EndRange
    from
        @t
)
select * from Normalized
where
    DOB between StartRange and EndRange or
    (
        @IncludeYear=0 and StartRange>EndRange and
        (
            DOB < EndRange or DOB > StartRange
        )
    )

我们创建Normalized CTE,如果@IncludeYear为1,则不执行任何操作,如果为零,则会重置所有日期,以便它们在2000年(任意选择)发生。

然后,我们根据CTE进行简单的查询。它不能正确匹配的一种情况是,当您在年终过渡期间确定了您的范围并且我们不关心多年 - 我们可以专门检查并在最后期限内满足WHERE条款。

结果:

ID          Name              DOB        StartRange EndRange
----------- ----------------- ---------- ---------- ----------
1           ABC               2000-01-01 2000-12-01 2000-01-31
2           BDS               2000-12-31 2000-12-01 2000-01-31

@Start='19700101',@End='19800227',@IncludeYear=1的结果:

ID          Name              DOB        StartRange EndRange
----------- ----------------- ---------- ---------- ----------
1           ABC               1974-01-01 1970-01-01 1980-02-27

答案 4 :(得分:0)

试试这个。

declare @flag varchar(3) ='NO';
declare @sd date ='1980-02-27';
declare @ed date ='1970-01-01';

select tt.*
from (select sd = month(@sd)*100 + day(@sd),
      ed = month(@ed)*100 + day(@ed)
     ) prm      
cross join
    -- test data, place real table here
    ( 
        values 
        (1,'ABC', cast('1974-01-05' as date)),
        (2,'BDS','1984-12-31'),
        (3,'QWE','1959-05-27')
    ) tt(ID,NAME, DOB)
cross apply (select md = month(DOB)*100 + day(DOB)) tx  
where @flag ='YES' and DOB between @sd and @ed
    or @flag ='NO' and (prm.sd<=prm.ed and tx.md  between prm.sd and prm.ed
                        or prm.sd>prm.ed and (tx.md <= prm.ed or tx.md >= prm.sd));

答案 5 :(得分:0)

这是另一种解决方案

var client = new kafka.Client(ZOO_KEEPER_URL, PORTAL_CLIENT_ID);
var topics = [
      { "topic" : MY_TOPIC_NAME,
        partition: 0
      },
      { "topic" : MY_TOPIC_NAME,
        partition: 1      }
    ];
var options = {
      autoCommit: false,
      groupId: GROUP_ID,
      fromOffset : false
    };
var consumer = new Consumer(client, topics, options);
    consumer.on('message', function (message) {
       console.log("New Message; offset: "+ message.offset + " Partition:   "+message.partition);           
    });

正如您所看到的,如果您不想包含年份,我们只是将查询的年份作为常量。这个查询似乎有点慢,但是如果你在表中添加另一个计算列,在那里用一年的常量保存日期,那么你只需要在那个特定列上放置标准。

答案 6 :(得分:0)

  • 始终在谓词中使用SARG。任何未能做到这一点的答案只会导致性能下降和DBA感到不安。

您想要的是根据过程的答案运行两个不同的查询。由于这是一个可能会运行很多的过程,因此将答案存储到PROC中的变量中并从那里运行任何调整代码。这不仅可以通过预先清除错误来使代码更加健壮,而且SQL Server更有可能猜测要与索引一起使用的变量。

以下PROC将有效。随意使用部分或全部:

CREATE TABLE #table_E (ID INT, Name VARCHAR(3), DOB DATE)
INSERT INTO #table_E (ID , Name, DOB)
VALUES (1, 'ABC', '1997-01-02' )
     , (2, 'BDS', '1984-12-31' )
     , (3, 'QWE', '1993-03-22' )

GO

CREATE PROC USP_EmpCompare (@Date_1 DATE, @Date_2 DATE, @Compare_Year VARCHAR(3))
AS
BEGIN
    DECLARE @MONTH_1 INT
          , @Month_2 INT
          , @Day_1   INT
          , @Day_2   INT
          , @Date1  DATE
          , @Date2  DATE
    SET @Date1  = CASE WHEN @Date_1 > @Date_2 THEN @Date_2 ELSE @Date_1 END
    SET @Date2  = CASE WHEN @Date_1 > @Date_2 THEN @Date_1 ELSE @Date_2 END
    SET @Month_1 = CASE WHEN DATEPART(MM, @Date2) > DATEPART(MM, @Date1) THEN DATEPART(MM, @Date1) ELSE DATEPART(MM, @Date2) END
    SET @Month_2 = CASE WHEN DATEPART(MM, @Date1) > DATEPART(MM, @Date2) THEN DATEPART(MM, @Date1) ELSE DATEPART(MM, @Date2) END
    SET @Day_1   = CASE WHEN DATEPART(DD, @Date2) > DATEPART(DD, @Date1) THEN DATEPART(DD, @Date1) ELSE DATEPART(DD, @Date2) END
    SET @Day_2   = CASE WHEN DATEPART(DD, @Date1) > DATEPART(DD, @Date2) THEN DATEPART(DD, @Date1) ELSE DATEPART(DD, @Date2) END
    -- SELECT @Date1, @Date2
    IF @Compare_Year = 'no'
    BEGIN
    ;WITH C AS (SELECT ID
                    , Name
                    , DATEPART(DD, DOB) AS Day
                    , DATEPART(MM, DOB) AS Month
    FROM #table_E)
    SELECT  ID, Name, @Date1, @Date2
    FROM    C
    WHERE   C.Month >= @MONTH_1
        AND C.Month <= @Month_2
        AND C.Day   >= @Day_1
        AND C.DAy   <= @Day_2       
    END
    IF @Compare_Year = 'yes'
    BEGIN

        SELECT ID, Name, DOB
        FROM #table_E
        WHERE DOB <= @Date2
          AND DOB >= @Date1
    END
    ELSE 
        PRINT WHAT! FOLLOW THE RULES YOU FOOL!!!

END 

JK。关于傻瓜的最后一部分可能不包括在你的最终草案中。 ;)

答案 7 :(得分:0)

我会让它变得非常简单,而不是那么复杂。

SELECT * FROM EMPLOYEE WHERE DOB >= '1970-01-01' AND DOB <= '1980-02-27'

这会过滤这两个日期之间的所有日期