2016-04-20 162 views
0

我有一个查询,我需要从表中获取结果,具体取决于用户在vb.net页面上指定的参数。CASE里WHERE子句

声明一个变量将是理想的解决方案,但由于它是一个内联表值函数,我不能这样做,所以我已经阅读了其他问题,这在多表函数中是可能的,但是使用这种功能是不推荐的。

以下查询将是理想的解决方案或我想实现的目标。

USE [AUDIT] 
GO 

ALTER FUNCTION [dbo].[GetCreated_LPA_Audits] 
( 
@fechaInicio nvarchar (max), 
@fechaFin nvarchar (max) 
@Period int, 
@Fecha int 

) 
RETURNS TABLE 
AS 
RETURN 
(

SELECT T1.Plant,T1.Area,T1.Location,T1.IDAudit,T1.Auditor,T1.AuditDate,T1.DueDate,T1.CreationDate 
FROM Header AS T1 inner join Audits AS T2 ON T1.IDChecklist = T2.IDChecklist 
inner join AuditGroups AS T3 ON T2.IDGroup = T3.IDGroup 

    WHERE T3.IDGroup = '2' and CreationDate is not null AND 
    CASE WHEN @Periodo = '0' THEN CreationDate>= @fechaInicio 
     WHEN @Periodo = '1' THEN DATEPART(MONTH,CreationDate)>= @Fecha 
     WHEN @Periodo = '2' THEN 
           CASE WHEN @Fecha = 13 THEN 
                DATEPART(MONTH,CreationDate)>= 1 
           END 
     WHEN @Periodo = '2' THEN 
           CASE WHEN @Fecha = 14 THEN 
                DATEPART(MONTH,CreationDate)>= 7 
           END 
     WHEN @Periodo = '3' THEN 
           CASE WHEN @Fecha = 15 THEN 
                DATEPART(Year,CreationDate)>= 2015 
           END 
    END 
    AND 

CASE WHEN @Periodo = '0' THEN @fechaInicio<=CreationDate 
    WHEN @Periodo = '1' THEN @Fecha<=DATEPART(MONTH,CreationDate) 
    WHEN @Periodo = '2' THEN 
           CASE WHEN @Fecha = 13 THEN 
                6<=DATEPART(MONTH,CreationDate) 
           END 
     WHEN @Periodo = '2' THEN 
           CASE WHEN @Fecha = 14 THEN 
                12<=DATEPART(MONTH,CreationDate) 
           END 
     WHEN @Periodo = '3' THEN 
           CASE WHEN @Fecha = 15 THEN 
                DATEPART(Year,CreationDate)>= 2015 
           END 
    END 

AND 在先前的查询@fechaInicio,@fechaFin,@Periodo和@Fecha是由用户提供的参数。

基本上如果@Periodo = 0我需要得到结果,其中 CreationDate>= @fechaInicio and CreationDate<[email protected]

如果@Periodo = 1我需要在那里 DATEPART(MONTH,CreationDate)>= @Fecha and DATEPART(MONTH,CreationDate)<= @Fecha

等等的结果。 希望我明确表示感谢!

编辑利用@MatBailie伪代码,有些轻视的改变和回答他的问题

IF @periodo = '0' THEN 
    WHERE CreationDate >= @fechaInicio -- Copied from 1st CASE 
     AND @fechaFin <= CreationDate -- Copied from 2nd CASE 

    -- gets results from @fechaInicio to @fechaFin 
    -- i.e. results from 04/05/2016 to 04/16/2016 

IF @periodo = '1' THEN 
    WHERE DATEPART(MONTH,CreationDate) >= @Fecha -- Copied from 1st CASE 
     AND @Fecha <= DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 

    -- In these case both conditions are the same 'cause 
    -- @Fecha is the number of a month (1 - 12) 
    -- i.e. @Fecha = 3 will get all the results of March 
    -- regardless of what it is on @fechaInicio and @fechaFin 

IF @periodo = '2' THEN 
    IF @fetcha = 13 THEN 
     WHERE DATEPART(MONTH,CreationDate)>= 1 -- Copied from 1st CASE 
      AND 6<=DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 
    IF @fetcha = 14 THEN 
     WHERE DATEPART(MONTH,CreationDate)>= 7 -- Copied from 1st CASE 
      AND 12<=DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 

    -- You never use @fetchaInicio? 
    -- You want everything in the first 6 months or last 6 months 
    -- For every year 
    -- Regardless of what is in @fetchaInicio? 

    -- Exactly!!-- 

IF @periodo = '3' THEN 
    IF @fetcha = 15 THEN 
     WHERE DATEPART(Year,CreationDate)>= 2015 -- Copied from 1st CASE 
      AND 2015 <= DATEPART(Year,CreationDate) -- Copied from 2nd CASE 

    -- 

又是怎么回事的情况下@periodo = '2' AND @fetcha NOT IN (13,14)? 那么情况如何@periodo = '3' AND @fetcha NOT IN (15)

这种情况将不存在,其限制在客户端。 如果他们选择@Periodo = '2'那么@Fecha将具有1-12的值,而没有其他值。

同样的,@Periodo = '3'的15或再@Fecha将有值16,既指的2015年或2016年

+1

可能重复的[条件where子句在Sql Server?](http://stackoverflow.com/questions/18629132/conditional-where-clause-in-sql-server) –

+0

和@VPeriodo> = @VFecha和@VPeriodo <= @ VFecha'呵呵..你能更具体地说明你的问题是什么吗? – JamieD77

+0

@ JamieD77我在一个时间范围内获得结果('@ fechaInicio'和'@ fechaFin')但是,如果用户请求(使用'@ Periodo'参数),我需要从该TimeFrame获取结果,但它可能是在'@ VPeriodo'中,我存储了需要比较的时间间隔('DATEPART')in'@ VFecha',它是我需要的时间范围(即如果它的月份是1 - 12) – abichango

回答

0

首先,你是正确的,内联TFV真的更快。如果不是过度复杂。 我最好为SQL Server端的每个@Periodo参数值设置一些iTFV,并在客户端选择正确的代码。

或者你可以做一个单一的iTVF

WHERE 
    @Periodo = '0' AND CreationDate>= @fechaInicio and CreationDate<[email protected] 
    OR @Periodo = '1' and DATEPART(MONTH,CreationDate)>= @Fecha and DATEPART(MONTH,CreationDate)<= @Fecha 
    ... 

但MS SQL众所周知,建立ocassionally不良计划或运营商可以使您的努力,坚持iTVF没用。

+0

和你最后的陈述是问题,它是一个增长的表,有多少OR会是一个问题?或者更多的是关于'OR'语句,而不是有多少'OR'? – abichango

0

每当你要使用的参数如条件的WHERE子句中,你可以按照以下格式:

WHERE 
((@Periodo = '0' AND CreationDate 
OR (@Periodo = '1' AND @VPeriodo = CAST(DATEPART(MONTH,CreationDate) as Nvarchar(10)) 
OR (@Periodo = '2' AND @VPeriodo = CAST(DATEPART(MONTH,CreationDate) as Nvarchar(10)) 
OR (@Periodo = '3' AND @VPeriodo = CAST(DATEPART(Year,CreationDate) as Nvarchar(10))) 

请确保参数评估是第一次,因为它会很快并且为假将停止查询继续处理更多的处理密集部分条件。

+0

这仍然是缓慢的。多个OR条件可以将优化器抛出。将搜索字段嵌入到函数中通常是不可挽回的。两者都产生全表扫描而不是索引范围搜索。 – MatBailie

+0

@MatBailie有人将我的问题标记为可能重复此[问题](http://stackoverflow.com/questions/18629132/conditional-where-clause-in-sql-server)! 答案与此相同,我认为它解决了我的问题,但是由于速度问题,是否有其他方法可以实现此目的? – abichango

+0

@abichango - 这是一个详细的链接,为什么这种类型的查询表现不佳。 http://www.sommarskog.se/dyn-search-2008.html至于具体的选择,我不确定你的意思是在你的评论我的答案。 – MatBailie

1

重新组织WHERE子句会使得过滤的字段位于左侧,而不在任何函数内。

例如...

WHERE 
     CreationDate >= @VPeriodo 
    AND CreationDate < CASE 
         WHEN @Periodo = '0' THEN DATEADD(DAY, 1, @VPeriodo) 
         WHEN @Periodo = '1' THEN DATEADD(MONTH, 1, @VPeriodo) 
         WHEN @Periodo = '2' THEN DATEADD(MONTH, 1, @VPeriodo) 
         WHEN @Periodo = '3' THEN DATEADD(YEAR, 1, @VPeriodo) 
         END 

在这个例子中,右手边是所有标量常量。这意味着您可以在CreationDate字段上进行范围扫描。

此外,@VPeriodo应该是DATEDATETIME而不是VARCHAR(MAX)



编辑:包箍为使用VARCHAR处理


所有日期的需要使用VARCHAR时要在格式YYYYMMDD通过跳跃。这是为了让stirngs的natrual顺序相同日期的自然顺序...
- '20161101'>'20161002'

当使用其他格式,如YYYYDDMM,失败...
- '20160111' < '20160210'问题,这种格式的10月2日来到后11月1日

WHERE 
     CreationDate >= @VPeriodo 
    AND CreationDate < CONVERT(
         NVARCHAR(8), 
         CASE 
          WHEN @Periodo = '0' THEN DATEADD(DAY, 1, CAST(@VPeriodo AS DATE)) 
          WHEN @Periodo = '1' THEN DATEADD(MONTH, 1, CAST(@VPeriodo AS DATE)) 
          WHEN @Periodo = '2' THEN DATEADD(MONTH, 1, CAST(@VPeriodo AS DATE)) 
          WHEN @Periodo = '3' THEN DATEADD(YEAR, 1, CAST(@VPeriodo AS DATE)) 
         END, 
         112 -- Format code for ISO dates, YYYYMMDD 
        ) 



编辑:后的OP提出了意见和修改的问题

这里OP一个问题,所有我做的是重新安排你的代码,使伪代码你已经什么书面...

IF @periodo = '0' THEN 
    WHERE CreationDate >= @fetchaInicio -- Copied from 1st CASE 
     AND @fetchaInicio <= CreationDate -- Copied from 2nd CASE 

    -- These two conditions are direct from your code 
    -- But they're the same as each other 
    -- What do you REALLY want to happen when @Periodo = '0'? 

IF @periodo = '1' THEN 
    WHERE DATEPART(MONTH,CreationDate) >= @Fecha -- Copied from 1st CASE 
     AND @Fecha <= DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 

    -- These two conditions are direct from your code 
    -- But they're the same as each other 
    -- What do you REALLY want to happen when @Periodo = '1'? 

IF @periodo = '2' THEN 
    IF @fetcha = 13 THEN 
     WHERE DATEPART(MONTH,CreationDate)>= 1 -- Copied from 1st CASE 
      AND 6<=DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 
    IF @fetcha = 14 THEN 
     WHERE DATEPART(MONTH,CreationDate)>= 7 -- Copied from 1st CASE 
      AND 12<=DATEPART(MONTH,CreationDate) -- Copied from 2nd CASE 

    -- You never use @fetchaInicio? 
    -- You want everything in the first 6 months or last 6 months 
    -- For every year 
    -- Regardless of what is in @fetchaInicio? 

IF @periodo = '3' THEN 
    IF @fetcha = 15 THEN 
     WHERE DATEPART(Year,CreationDate)>= 2015 -- Copied from 1st CASE 
      AND DATEPART(Year,CreationDate)>= 2015 -- Copied from 2nd CASE 

    -- Both conditions are the same again, why? 
    -- You want everything from 2015 onwards, forever? 
    -- You never use @fetchaInicio? 
    -- It's always 2015? 

那么情况如何@periodo = '2' AND @fetcha NOT IN (13,14)? 那么情况如何@periodo = '3' AND @fetcha NOT IN (15)


请你能陪我上面的伪代码,并给什么你真的想在每种情况下做一些实际的例子吗?

+0

事情是'@ VPeriodo'不是一个实际的参数,我用它“模拟”一个变量,使其达到某个值(显然不是那样工作的)。它也'VARCHAR',因为'CreationDate'它的laso'VARCHAR'(这是怎么设计的表) – abichango

+0

@abichango - 我不明白,你的函数定义有一个名为'@ vperiodo'的变量。但不管它是一个变量,还是来自另一个表*(用于连接谓词)*的字段,该结构仍然成立。你有一个*实际的例子,你无法应用这个结构? – MatBailie

+0

我觉得我还不够清楚(对不起),我有'@ VPeriodo'变量,但它不在任何其他表中,它本身没有值,我试图用它作为局部变量,其中我可以使它的值等于'CreationDate'或'DATEPART(month,CreationDate'),而不是将它与SQL进行比较(如SQL)。 最初的查询没有@VPeriodo,既没有@VFecha也没有@VFecha 2,就像我说过的,我试图用它们作为变量来保存它们的数据,而不是与它们比较数据,这是否合理? 此外,这是我正在使用的实际查询。 – abichango