0
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

Goal:

High level: filter @DataT data by what's selected in @Param. If @Param is empty, don't filter the data.

Low level:

@Param: this is a filter param that, when populated, will have either "Yes", "No", or a specific "Maybe_xxx" values. This is used to filter the data available in @DataT table.

@DataT: this is the actual data table. It has an Id column (Identity), a Flag column (Yes (10), No (100), and Maybe (1000)), and one column for each flag type ([FValY] is populated for "Yes/10" flag, [FValN] is populated for "No/100" flag, and [FValMB] is populated for "Maybe/1000" flag)

How does the @Param filter work? This filter can be empty or populated with either Yes and/or No and/or specific [FValMB] column's value(s). It can have any combinations of Yes/No/[FValMB]-value(s).

  • If @Param is only populated with "Yes", only the first three records should be returned.

  • If @Param is only populated with "No", only the 4th and 5th records should be returned.

  • If @Param is only populated with "Maybe" ([FValMB] values), then only the records for "Maybe" flags should be returned where that @Param value(s) = [FValMB].

  • If "Yes" and "No" are populated, then only the first five records should be returned.

  • If "Yes" and "Maybe" are populated, then only the first three and whatever matches from 6th to 8th records should returned ... and so on

  • If @Param is empty, no filter should apply for this.

My attempt:

I'm having trouble with the arithmetic part where I need to check for the one/more combinations and such. I need help with improving the code within the most outer IF statement.

3
  • 1
    IMHO,if it was some other example then dynamic Sql approach was good.but in this example I don't think there will be much performance gain.Just becasue the way table and type are populated.Also more or less your dynamic sql is ok.There is no way you can reduce if block except going my way.If you go my way then No use in trying dynamic sql. Commented Apr 11, 2017 at 4:59
  • Thanks. The reason I used dyn-sql is because there're more optional filters I need to apply in this example, which I kept out of this post. Commented Apr 11, 2017 at 14:22
  • would love to see complete script and understand whole requirement Commented Apr 11, 2017 at 14:50

4 Answers 4

2

Dont have enough points to add a comment for more questions. If possible simplify your code by doing something like this as you seem to know what the value is for each item.

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' )
        ;

Adding the numeric value will simplify the code. And them maybe you can just build the where clause from it. Could even add missing_Y_and_N and insert the value.

Sign up to request clarification or add additional context in comments.

1 Comment

sry, but I've to use that udt w/o modifying it. :(
1
+50

Sometime you modify table/function in such a manner that it don't affect any part of code.you can do similar thing here.

Or even alternate solution will be neat clean .In case you have lot million of records and your real scenario is different than one describe above then you can think of dynamic search,but the one you have written is not upto the mark.

    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

-- Alternatively 3rd solution,if you cannot change type at all then try this,

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))
)

7 Comments

IF I comment out a "Maybe_xxx" value from @Param, that record is still being returned in the output. i.e. commented out " , ( 'Maybe_PQR' )" and exec the query and the ID = 8 record is still being returned. Should I be doing something else too?
@007,change the value of flag in type and table both.then it will work.
I'm not sure what you mean, But the values in udt and table reflect the data I expect in the real table, can't really change that.
007,sorry I got your point.see for yes/no you hv different value 10/100 but for all maybe you hv only 1000 ,hence the confusion.i thought it was wrong ?Can't you change "Maybe " value to be very specific.like MayBe_JKL=1000,Maybe_XYZ=2000 and so on.Explain more then then i correct my script.
No worries. Unfortunately, I can't or rather, won't, have different Flag values for each MAYBE_xxx values. :(
|
1

Don't use dynamic SQL for this,Declare your @DataT table normally and proceed

Create a #temp table from @DataT(the actual data table) to make rows based on columns

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,'') <>''

Join the #temp table with @param and @DataT

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

You will get the desired output

1 Comment

Same as other sln, if I comment out a "Maybe_xxx" value from @Param, that record is still being returned in the output. i.e. commented out " , ( 'Maybe_PQR' )" and exec the query and the ID = 8 record is still being returned. Should I be doing something else too?
0

I made your table permanent for this example just because it was simpler and easier to factor that piece out. Shouldn't make a difference in the guts of the candidate solution. Like another poster, there are several requirements questions that I couldn't quite answer for myself, but don't have the points to inquire about. Specifically, I notice that your sample data has no rows with values in two or more of the FVal* columns. Therefore I did not test for / accommodate that (yet).

/* 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 */

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.