T-SQL - 条件WHERE子句

时间:2017-04-07 22:12:24

标签: sql sql-server sql-server-2012

CREATE TYPE [dbo].[Param] AS TABLE ( [FlagVal] NVARCHAR(100) )
GO

DECLARE @Param [dbo].[Param];
INSERT  INTO @Param ( [FlagVal] )
VALUES    ( 'Yes' )
        , ( 'NO' )
        , ( 'Maybe_JKL' )
        , ( 'Maybe_XYZ' )
        --, ( 'Maybe_PQR' )
        ;

DECLARE @Sql NVARCHAR(MAX);
SET @Sql = N'
DECLARE @DataT TABLE ( [ID] INT IDENTITY(1,1), [Flag] INT, [FValY] NVARCHAR(45), [FValN] NVARCHAR(45), [FValMB] NVARCHAR(45) );
INSERT  INTO @DataT ( [Flag], [FValY], [FValN], [FValMB] )
VALUES    ( 10, ''XYZ'', NULL, NULL )
        , ( 10, ''ABC'', NULL, NULL )
        , ( 10, ''XYZ'', NULL, NULL )
        , ( 100, NULL, ''MNO'', NULL )
        , ( 100, NULL, ''STU'', NULL )
        , ( 1000, NULL, NULL, ''Maybe_JKL'' )
        , ( 1000, NULL, NULL, ''Maybe_XYZ'' )
        , ( 1000, NULL, NULL, ''Maybe_PQR'' )
        , ( NULL, NULL, NULL, NULL )
        , ( NULL, NULL, NULL, NULL );

SELECT  [ID]
      , [Flag]
      , [FValY]
      , [FValN]
      , [FValMB] 
FROM    @DataT DT
WHERE   [ID] < 1000'

IF EXISTS (SELECT TOP (1) 1 FROM @Param) -- Code within this clause needs improvement
BEGIN
        SET @Sql = CONCAT(@Sql,N' AND (');

        IF EXISTS (SELECT TOP (1) 1 FROM @Param WHERE [FlagVal] = 'Yes')
            BEGIN
                SET @Sql = CONCAT(@Sql,N' [Flag] = 10');
            END
        ELSE
            BEGIN
                SET @Sql = CONCAT(@Sql,N' [Flag] != 10');
            END
        IF EXISTS (SELECT TOP (1) 1 FROM @Param WHERE [FlagVal] = 'NO')
            BEGIN
                SET @Sql = CONCAT(@Sql,N' OR [Flag] = 100');
            END
        ELSE
            BEGIN
                SET @Sql = CONCAT(@Sql,N' AND [Flag] != 100');
            END
        IF EXISTS (SELECT TOP (1) 1 FROM @Param WHERE [FlagVal] NOT IN ('Yes','No'))
            BEGIN
                SET @Sql = CONCAT(@Sql,N' OR ([Flag] = 1000
                                              AND EXISTS (SELECT    TOP (1) 1 
                                                            FROM    @Param P
                                                            WHERE   DT.[FValMB] = P.[FlagVal]))');
            END
        ELSE
            BEGIN
                SET @Sql = CONCAT(@Sql,N' AND ([Flag] != 1000
                                               AND NOT EXISTS (SELECT   TOP (1) 1 
                                                                FROM    @Param P
                                                                WHERE   DT.[FValMB] = P.[FlagVal]))');
            END
        SET @Sql = CONCAT(@Sql,N' )');
END

PRINT @Sql
EXEC sp_executesql
    @Sql,
    N'@Param [dbo].[Param] READONLY',
    @Param = @Param

DROP TYPE [dbo].[Param]
GO

目标

高级别:根据@DataT中选择的内容过滤@Param数据。如果@Param为空,请勿过滤数据。

低级别:

@Param:这是一个过滤器参数,填充后会有“是”,“否”或特定的“Maybe_xxx”值。这用于过滤@DataT表中可用的数据。

@DataT:这是实际的数据表。它有Id列(标识),Flag列(是(10),否(100)和可能(1000)),每个标志类型有一列({{1}填充“是/ 10”标记,[FValY]填充“No / 100”标记,[FValN]填充“Maybe / 1000”标记

[FValMB]过滤器如何工作?此过滤器可以为空或填充“是”和/或“否”和/或特定@Param列的值。它可以是Yes / No / [FValMB] -value的任意组合。

  • 如果[FValMB]仅填充“是”,则只返回前三个记录。

  • 如果@Param仅填充“否”,则只返回第4和第5条记录。

  • 如果@Param仅填充了“Maybe”(@Param值),那么只应返回“Maybe”标记的记录[FValMB]

  • 如果填充“是”和“否”,则只返回前五个记录。

  • 如果填写“是”和“可能”,则只返回前三个以及第6到第8条记录中的任何匹配项......等等

  • 如果@Param value(s) = [FValMB]为空,则不应对此进行过滤。

我的尝试:

我在算术部分遇到问题,我需要检查一个/多个组合等。我需要帮助改进最外层IF语句中的代码。

4 个答案:

答案 0 :(得分:2)

没有足够的积分来为更多问题添加评论。 如果可能的话,通过执行类似的操作来简化代码,因为您似乎知道每个项目的价值。

CREATE TYPE [dbo].[Param] AS TABLE ( [FlagVal] NVARCHAR(100),NumValue int  )
)
GO

DECLARE @Param [dbo].[Param];
INSERT  INTO @Param ( [FlagVal] )
VALUES    ( 'Yes',10 )
        , ( 'NO',100 )
        , ( 'Maybe_JKL',### )
        , ( 'Maybe_XYZ',### )
        --, ( 'Maybe_PQR' )
        ;

添加数值将简化代码。也许你可以从中构建where子句。甚至可以添加missing_Y_and_N并插入值。

答案 1 :(得分:1)

有时你修改表/函数的方式不会影响代码的任何部分。你可以在这里做类似的事情。

甚至替代解决方案也会整洁干净。如果您有大量的记录并且您的真实场景与上面描述的不同,那么您可以考虑动态搜索,但您编写的那个并不符合标记。

    drop type [dbo].[Param]
    CREATE TYPE [dbo].[Param] AS TABLE ( [FlagVal] NVARCHAR(100),FlagID as 
    case when FlagVal='Yes' then 10 when FlagVal='No' then 100 
    when FlagVal like '%Maybe%' then 1000 else null end)
    GO

    DECLARE @Param [dbo].[Param];
    INSERT  INTO @Param ( [FlagVal] )
    VALUES    ( 'Yes' )
            , ( 'NO' )
            , ( 'Maybe_JKL' )
            , ( 'Maybe_XYZ' )
            , ( 'Maybe_PQR' )
            ;

    DECLARE @DataT TABLE ( [ID] INT IDENTITY(1,1), [Flag] INT, [FValY] NVARCHAR(45), 
    [FValN] NVARCHAR(45), [FValMB] NVARCHAR(45) );
    INSERT  INTO @DataT ( [Flag], [FValY], [FValN], [FValMB] )
    VALUES    ( 10, 'XYZ', NULL, NULL )
            , ( 10, 'ABC', NULL, NULL )
            , ( 10, 'XYZ', NULL, NULL )
            , ( 100, NULL, 'MNO', NULL )
            , ( 100, NULL, 'STU', NULL )
            , ( 1000, NULL, NULL, 'Maybe_JKL' )
            , ( 1000, NULL, NULL, 'Maybe_XYZ' )
            , ( 1000, NULL, NULL, 'Maybe_PQR' )
            , ( NULL, NULL, NULL, NULL )
            , ( NULL, NULL, NULL, NULL );

    SELECT * from @DataT
    select * from @Param


    select * from @DataT dt
where exists(select [FlagVal] from @Param p 
where p.FlagID=dt.[Flag] and(( p.FlagVal=dt.FValMB) or (dt.FValMB is null)))


    ------Alternatively declare table variable and use it instead of table type
    DECLARE @TableTypeAlt TABLE ( [FlagVal]  NVARCHAR(100),FlagID int)
    insert into @TableTypeAlt
    select FlagVal, 
    case when FlagVal='Yes' then 10 
    when FlagVal='No' then 100 when FlagVal like '%Maybe%' then 1000 else null end
    from @Param
    select * from @TableTypeAlt

- 或者第三种解决方案,如果你根本不能改变类型 试试这个,

select * from @DataT dt
where exists(
select [FlagVal] from @Param p 
where case when FlagVal='Yes' then 10 when FlagVal='No' then 100 
    when FlagVal like '%Maybe%' then 1000 else null end=dt.[Flag] and(( p.FlagVal=dt.FValMB) or (dt.FValMB is null))
)

答案 2 :(得分:1)

不要为此使用动态SQL,正常声明@DataT表并继续

从@DataT创建一个#temp表(实际数据表)以根据列创建行

if object_id('tempdb..#temp') is not null drop table #temp
select distinct [Flag],case when isnull(FValY,'') <> '' then 'Yes' 
when isnull(fvaln,'')<>'' then 'NO'
when isnull(fvalmb,'') <> '' then 'Maybe_' end As FlagVal into #temp 
from @DataT where isnull(Flag,'') <>''

使用@param和@DataT

加入#temp表
select distinct c.* from @Param a inner join #temp b 
on a.[FlagVal] like b.FlagVal + '%' 
inner join @DataT c on b.Flag = c.Flag

您将获得所需的输出

答案 3 :(得分:0)

我为这个例子制作了永久性的桌子,因为它更简单,更容易将这一部分考虑在内。不应该在候选解决方案的内容上有所作为。像另一张海报一样,有几个要求问题我自己无法回答,但没有要点的问题。具体来说,我注意到您的示例数据没有包含两个或更多FVal *列值的行。因此,我没有测试/适应那个(还)。

/* BEGIN One-time structure setup */
CREATE TYPE [dbo].[Param] AS TABLE ( [FlagVal] NVARCHAR(100) )
GO


CREATE TABLE [dbo].[DataT](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Flag] [int] NULL,
    [FValY] [nvarchar](45) NULL,
    [FValN] [nvarchar](45) NULL,
    [FValMB] [nvarchar](45) NULL,
 CONSTRAINT [PK_DataT] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
/* END One-time structure setup */

/* BEGIN test data setup */
INSERT  INTO DataT ( [Flag], [FValY], [FValN], [FValMB] )
VALUES    ( 10, 'XYZ', NULL, NULL )
        , ( 10, 'ABC', NULL, NULL )
        , ( 10, 'XYZ', NULL, NULL )
        , ( 100, NULL, 'MNO', NULL )
        , ( 100, NULL, 'STU', NULL )
        , ( 1000, NULL, NULL, 'Maybe_JKL' )
        , ( 1000, NULL, NULL, 'Maybe_XYZ' )
        , ( 1000, NULL, NULL, 'Maybe_PQR' )
        , ( NULL, NULL, NULL, NULL )
        , ( NULL, NULL, NULL, NULL )
;
/* END test data setup */

/* BEGIN test param */
DECLARE @Param Param;

INSERT  INTO @Param ( [FlagVal] )
VALUES    ( 'Yes' )
        ,( 'NO' )
        , ( 'Maybe_JKL' )
        , ( 'Maybe_XYZ' )
        , ( 'Maybe_PQR' )
        ;
/* END test param */


/* BEGIN query */
DECLARE @ParamHasYes bit;
SET @ParamHasYes = 0;

DECLARE @ParamHasNo bit;
SET @ParamHasNo = 0;

IF EXISTS (SELECT FlagVal FROM @Param WHERE LOWER(FlagVal) = 'yes') SET @ParamHasYes = 1;
IF EXISTS (SELECT FlagVal FROM @Param WHERE LOWER(FlagVal) = 'no') SET @ParamHasNo = 1;

WITH DataView AS 
(
    SELECT 
        Flag
        ,FValY
        ,FValN
        ,FValMB
        /* IMPORTANT - non-matches for Y and N columns below must be set to NULL (NOT 0!) 
        so that rows are excluded when the param list is empty 
        i.e., @ParamHasYes = 0 and/or @ParamHasNo = 0 */
        ,CASE WHEN FValY IS NOT NULL THEN 1 ELSE NULL END AS RowHasYes 
        ,CASE WHEN FValN IS NOT NULL THEN 1 ELSE NULL END AS RowHasNo
    FROM
        DataT
)
-- Get all the rows that have a value for the yes column
SELECT 
    DataView.*
FROM 
    DataView
WHERE
    RowHasNo = @ParamHasNo
UNION ALL
-- Get all the rows that have a value for the no column
SELECT 
    DataView.*
FROM 
    DataView
WHERE
    RowHasYes = @ParamHasYes
UNION ALL
-- Get all the rows that match the MB list
SELECT 
    DataView.* 
FROM 
    DataView
    INNER JOIN @Param P ON DataView.FValMB = P.FlagVal
ORDER BY
    Flag
    ,FValY
    ,FValN
    ,FValMB
;
/* END query */