2

I have two tables - Orders and Products. Each order can have 0 or more products.

--drop table orders
--drop table products

CREATE TABLE Orders (
    OrderId INT PRIMARY KEY,
    OrderDate DATE,
    CustomerName VARCHAR(50)
);

CREATE TABLE Products (
    ProductId INT PRIMARY KEY,
    ProductName VARCHAR(50),
    Price DECIMAL(10, 2),
    OrderId INT,
    FOREIGN KEY (OrderId) REFERENCES Orders(OrderId)
);

INSERT INTO Orders (OrderId, OrderDate, CustomerName)
VALUES (1, '2023-01-01', 'John Doe'),
       (2, '2023-01-02', 'Jane Smith'),
       (3, '2023-01-03', 'Michael Johnson'),
       (4, '2023-01-04', 'Emily Davis'),
       (5, '2023-01-05', 'Robert Wilson'),
       (6, '2023-01-06', 'Olivia Thompson'),
       (7, '2023-01-07', 'William Anderson'),
       (8, '2023-01-08', 'Sophia Martinez'),
       (9, '2023-01-09', 'James Hernandez'),
       (10, '2023-01-10', 'Ava Lee'),
       (11, '2023-01-11', 'Benjamin Walker'),
       (12, '2023-01-12', 'Mia Lewis'),
       (13, '2023-01-13', 'Ethan Hall'),
       (14, '2023-01-14', 'Charlotte White'),
       (15, '2023-01-15', 'Alexander King'),
       (16, '2023-01-16', 'Amelia Scott'),
       (17, '2023-01-17', 'Daniel Green'),
       (18, '2023-01-18', 'Harper Adams'),
       (19, '2023-01-19', 'Matthew Clark'),
       (20, '2023-01-20', 'Luna Rodriguez'),
       (21, '2023-01-21', 'Henry Carter'),
       (22, '2023-01-22', 'Lily Hill'),
       (23, '2023-01-23', 'Sebastian Turner'),
       (24, '2023-01-24', 'Avery Ward'),
       (25, '2023-01-25', 'Scarlett Foster'),
       (26, '2023-01-26', 'Joseph Diaz'),
       (27, '2023-01-27', 'Victoria Morris'),
       (28, '2023-01-28', 'David Sullivan'),
       (29, '2023-01-29', 'Penelope Price'),
       (30, '2023-01-30', 'Jackson Brooks');

INSERT INTO Products (ProductId, ProductName, Price, OrderId)
VALUES 
    -- Products for OrderId 1
    (1, 'Product A', 10.99, 1),
    (2, 'Product B', 19.99, 1),
    
    -- Products for OrderId 2
    (3, 'Product C', 5.99, 2),
    
    -- Products for OrderId 3
    (4, 'Product D', 7.50, 3),
    (5, 'Product E', 14.99, 3),
    (6, 'Product F', 8.99, 3),
    
    -- Products for OrderId 4
    (7, 'Product G', 12.99, 4),
    (8, 'Product H', 9.99, 4),
    (9, 'Product I', 6.49, 4),
    
    -- Products for OrderId 5
    (10, 'Product J', 18.50, 5),
    (11, 'Product K', 11.99, 5),
    
    -- Products for OrderId 6
    (12, 'Product L', 7.99, 6),
    (13, 'Product M', 13.99, 6),
    
    -- Products for OrderId 7
    (14, 'Product N', 9.99, 7),
    
    -- Products for OrderId 8
    (15, 'Product O', 16.99, 8),
    (16, 'Product P', 14.50, 8),
    
    -- Products for OrderId 9
    (17, 'Product Q', 8.99, 9),
    (18, 'Product R', 6.99, 9),
    
    -- Products for OrderId 10
    (19, 'Product S', 12.99, 10),
    
    -- Products for OrderId 11
    (20, 'Product T', 7.50, 11),
    
    -- Products for OrderId 12
    (21, 'Product U', 9.99, 12),
    (22, 'Product V', 11.99, 12),
    
    -- Products for OrderId 13
    (23, 'Product W', 15.99, 13),
    (24, 'Product X', 8.50, 13),
    
    -- Products for OrderId 14
    (25, 'Product Y', 9.99, 14),
    
    -- Products for OrderId 15
    (26, 'Product Z', 12.99, 15),
    (27, 'Product AA', 7.99, 15),
    (28, 'Product BB', 14.99, 15),
    
    -- Products for OrderId 16
    (29, 'Product CC', 11.50, 16),
    (30, 'Product DD', 9.99, 16);

Requirement: Write a query that filters the customerName column from the Orders table or ProductName column from the Products table based on the filter passed in the query and orders it by OrderDate and fetch the number of orders that are requested. Here's the query I wrote but it doesn't accomplish what i'm looking for:

declare @filter varchar(100) = 't'
declare @skiprows int = 5
declare @limitrows int = 5

select *
  from dbo.orders o
  left join dbo.products p
  on o.orderId = p.orderId
  where (customerName like '%'+@filter+'%'
    or productName like '%'+@filter+'%')
  order by orderdate desc
  offset @skiprows rows
  fetch next @limitrows rows only

Result: enter image description here

The parameters @limitrows and @skiprows are used to determine the desired result set. However, in the given query, skipping the first 5 rows and returning the next 5 rows does not provide the expected outcome. The goal is to skip the first 5 distinct Order Ids and then select the next 5 distinct Order Ids. Subsequently, the result set should be joined with the Products table to retrieve all corresponding products. It is important to note that the final result set may contain more than 5 rows since each order can have multiple products. If anyone could provide guidance on how to accomplish this, it would be greatly appreciated. Thank you!

2 Answers 2

4

One way would be to use DENSE_RANK

declare @filter varchar(100) = 't'
declare @skiprows int = 5
declare @limitrows int = 5;

WITH T AS
(
select o.*, 
        p.ProductId,
        p.productName,
        p.Price,
        Rnk = DENSE_RANK() OVER (ORDER BY o.orderdate desc, o.orderId DESC)
  from dbo.orders o
  left join dbo.products p
  on o.orderId = p.orderId
  where (o.customerName like '%'+@filter+'%'
    or p.productName like '%'+@filter+'%')
)
SELECT *
FROM T
WHERE Rnk > @skiprows AND Rnk <= @skiprows + @limitrows
order by Rnk
1
  • 1
    Annoying you can't do OFFSET WITH TIES Jun 28, 2023 at 0:56
3
DECLARE @filter VARCHAR(100) = 't';
DECLARE @skiprows INT = 2;
DECLARE @limitrows INT = 8;

SELECT  *
FROM    (
        SELECT  *
        FROM    orders
        WHERE   (
                CustomerName LIKE '%' + @filter + '%'
                OR
                EXISTS
                (
                SELECT  *
                FROM    products
                WHERE   products.OrderId = orders.OrderId
                        AND ProductName LIKE '%' + @filter + '%'
                ))
        ORDER BY
                orderDate DESC, orderId DESC
        OFFSET  @skiprows ROWS
        FETCH NEXT
                @limitrows ROWS ONLY
        ) orders
OUTER APPLY
        (
        SELECT  *
        FROM    products
        WHERE   products.OrderId = orders.OrderId
        ) products
GO

It's not completely clear what exactly "based on the filter passed in the query" means in your question.

The query above finds all orders that have the letter "t" in either the customer's name or one of the products' names.

Out of this list, it takes the orders from the third last to the tenth last, and returns these orders and all their products (even those that don't have the letter "t" in the name). In your example, though, all the products have a letter "t" in them as a part of the word "product".

If an order doesn't have any products, the query will return a single record for this order with nulls in the product fields (similar to how left join works).

SQLFiddle

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.