user2882307
user2882307

Reputation:

Optional parameters in SQL query

I am new to SQL and I am kind of lost. I have a table that contains products, various fields like productname, category etc.

I want to have a query where I can say something like: select all products in some category that have a specific word in their productname. The complicating factor is that I only want to return a specific range of that subset. So I also want to say return me the 100 to 120 products that fall in that specification.

I googled and found this query:

WITH OrderedRecords AS
(   
    SELECT *, ROW_NUMBER() OVER (ORDER BY PRODUCTNUMMER) AS "RowNumber",
    FROM (
        SELECT * 
        FROM SHOP.dbo.PRODUCT
        WHERE CATEGORY = 'ARDUINO'
        and PRODUCTNAME LIKE '%yellow%'
    )
) 
SELECT * FROM OrderedRecords WHERE RowNumber BETWEEN 100 and 120
Go

The query works to an extent, however it assigns the row number before filtering so I won't get enough records and I don't know how I can handle it if there are no parameters. Ideally I want to be able to not give a category and search word and it will just list all products.

I have no idea how to achieve this though and any help is appreciated!

Upvotes: 3

Views: 985

Answers (3)

esiprogrammer
esiprogrammer

Reputation: 1438

If you just need to to paginate your query and return a specific range of results, you can simply use OFFSET FETCH Clause.

That way there is no need to filter result items by RowNumber. I think this solution is easier:

SELECT * 
FROM SHOP.dbo.PRODUCT
WHERE CATEGORY = 'ARDUINO' AND PRODUCTNAAM LIKE '%yellow%'
ORDER BY PRODUCTNUMMER
OFFSET 100 ROWS          -- start row
FETCH NEXT 20 ROWS ONLY  -- page size

Find out more Pagination with OFFSET / FETCH

Upvotes: 2

TT.
TT.

Reputation: 16137

Building on what esiprogrammer showed in his answer on how to return only rows in a certain range using paging.

Your second question was:

Ideally I want to be able to not give a category and search word and it will just list all products.

You can either have two queries/stored procedures, one for the case where you do lookup with specific parameters, another for lookup without parameters.

Or, if you insist on keeping one query/stored procedure for all cases, there are two options:

  1. Build a Dynamic SQL statement that only has the filters that are present; execute it using EXECUTE (@sql) or EXECUTE sp_executesql @sql
  2. Build a Catch-All Query

Example for option 2:

-- if no category is given, it will be NULL
DECLARE @search_category VARCHAR(128);

-- if no name is given, it will be NULL
DECLARE @search_name VARCHAR(128);

SELECT * 
FROM SHOP.dbo.PRODUCT
WHERE (@search_category IS NULL OR CATEGORY=@search_category) AND
      (@search_name IS NULL OR PRODUCTNAAM LIKE '%'+@search_name+'%')
ORDER BY PRODUCTNUMMER
OFFSET 100 ROWS
FETCH NEXT 20 ROWS ONLY
OPTION(RECOMPILE); -- generate a new plan on each execution that is optimized for that execution’s set of parameters

Upvotes: 4

Migo
Migo

Reputation: 151

What do you mean it assigns the rownumber before filtering? Category and ProductName are part of the sub query... So if the product table has 10k records and only 1k meet your criteria the results from the CTE will be 1k, so RowNumber BETWEEN 100 and 120 works. Test it out, remove your where clauses from both select statement and you'll get rownumber for all of products table. Then add back in the category and productname filter and your RowNumber is for your filter ordered by ProductNumber, so when you then add back in Between 100 and 120, this is the right solution based on what you described.

WITH OrderedRecords AS
(   
        SELECT ROW_NUMBER() OVER (ORDER BY PRODUCTNUMMER) AS "RowNumber"
            , * 
        FROM SHOP.dbo.PRODUCT
        WHERE CATEGORY = 'ARDUINO'
        and PRODUCTNAAM LIKE '%yellow%'
    )
) 
SELECT * 
FROM OrderedRecords 
WHERE RowNumber 
BETWEEN 100 and 120
Go

Upvotes: 0

Related Questions