sqlsql-servert-sql

Invoking a stored procedure with all combinations of values stored in multiple single-column tables


Here are the tables I am working with and a presentation of the problem I am trying to solve in T-SQL (SQL Server 2022):

-- Create single-column tables to hold values.
CREATE TABLE [dbo].[Attr_1_Values]
(
   [Attr_1_Value] VARCHAR(30) NOT NULL PRIMARY KEY
);

CREATE TABLE [dbo].[Attr_2_Values]
(
   [Attr_2_Value] VARCHAR(30) NOT NULL PRIMARY KEY
);

CREATE TABLE [dbo].[Attr_3_Values]
(
   [Attr_3_Value] VARCHAR(30) NOT NULL PRIMARY KEY
);

-- Insert rows (individual values in this case) into the just-created tables.
INSERT INTO [dbo].[Attr_1_Values] ([Attr_1_Value]) VALUES ('Attr_1_val_1');
INSERT INTO [dbo].[Attr_1_Values] ([Attr_1_Value]) VALUES ('Attr_1_val_2');
INSERT INTO [dbo].[Attr_1_Values] ([Attr_1_Value]) VALUES ('Attr_1_val_3');
INSERT INTO [dbo].[Attr_1_Values] ([Attr_1_Value]) VALUES ('Attr_1_val_4');

INSERT INTO [dbo].[Attr_2_Values] ([Attr_2_Value]) VALUES ('Attr_2_val_1');
INSERT INTO [dbo].[Attr_2_Values] ([Attr_2_Value]) VALUES ('Attr_2_val_2');

INSERT INTO [dbo].[Attr_3_Values] ([Attr_3_Value]) VALUES ('Attr_3_val_1');
INSERT INTO [dbo].[Attr_3_Values] ([Attr_3_Value]) VALUES ('Attr_4_val_2');

-- Create a stored procedure that will take as parameters values from the tables above.
CREATE PROCEDURE [dbo].[My_Stored_Procedure]
   @Attr_1_p AS VARCHAR(30),
   @Attr_2_p AS VARCHAR(30),
   @Attr_3_p AS VARCHAR(30)
AS
BEGIN
    -- Stored procedure logic goes here.
END;

-- Generate all combinations of values from the [Attr_1_Values], [Attr_2_Values], and [Attr_3_Values] tables.
-- Note: From this point on, pseudocode and proper T-SQL code are mixed to best try to convey my intent.
Attr_Combinations = SELECT [Attr_1_Value], [Attr_2_Value], [Attr_3_Value] FROM
                       [dbo].[Attr_1_Values] CROSS JOIN
                       [dbo].[Attr_2_Values] CROSS JOIN
                       [dbo].[Attr_3_Values];

-- Somehow, invoke [dbo].[My_Stored_Procedure] with each individual combination.
-- In this example, that would be 4*2*2 = 16 invocations of [dbo].[My_Stored-Procedure].

LOOP OVER Attr_Combinations
BEGIN
    EXEC [dbo].[My_Stored_Procedure]
               @Attr_1_p = One_Attr_Combination.Attr_1_Value,
               @Attr_2_p = One_Attr_Combination.Attr_2_Value,
               @Attr_3_p = One_Attr_Combination.Attr_3_Value;
END;

How may I accomplish the desired invocations of [dbo].[My_Stored_Procedure] with each invocation passing one combination of values as described above?

Edit for Clarity: Set-Based Approach vs. Iteration

In my actual application, [dbo].[My_Stored_Procedure] will be invoked on as as-needed basis with one specific set of values for @Attr_1_p, @Attr_2_p, and @Attr_3_p. It is in my test harness for this stored procedure that I desire, for robustness of testing, to make an invocation for each possible combination of @Attr_1_p, @Attr_2_p, and @Attr_3_p.

Given how the stored procedure will be used in the actual application, it is desirable for it to have the signature it currently has. I could change its signature to take a table (and then operate on that table in a set-based manner), but that would, to some degree, complicate the client code in that rather than just passing the @Attr_1, @Attr_2, and @Attr_3 values it already has on hand, it would have to first construct a one-row table from these values before invoking the stored procedure.

I understand and appreciate the suggestions to take a set-based approach and would prefer that myself if not for the desire to keep the stored procedure signature as it is to keep invocation as natural as possible for client code. Since Yitzhak Khabinsky's approach allows me to do this, and since I have been able to successfully implement his approach, I have accepted his answer. Thank you to all who commented!


Solution

  • Please check the following conceptual example.

    Just insert result of your SELECT with CROSS JOINs into a table variable, and repeat the logic.

    DECLARE @tbl TABLE (...);
    
    INSERT INTO @tbl (...)
    SELECT [Attr_1_Value], [Attr_2_Value], [Attr_3_Value] FROM
                           [dbo].[Attr_1_Values] CROSS JOIN
                           [dbo].[Attr_2_Values] CROSS JOIN
                           [dbo].[Attr_3_Values];
    

    SQL

    DECLARE @tbl TABLE (ID INT PRIMARY KEY, [Name] VARCHAR(50));
    INSERT @tbl (ID, [Name]) VALUES
    (1, 'Employee'),
    (2, 'Department'),
    (3, 'Class');
    
    DECLARE @IDVar INT, @NameVar VARCHAR(50);
    DECLARE @RowCount INT = (SELECT COUNT(*) FROM @tbl);
    
    WHILE @RowCount > 0 BEGIN
       SELECT @IDVar=ID, @NameVar=[Name] 
       FROM @tbl 
       ORDER BY ID DESC OFFSET @RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;
    
       SELECT @IDVar, @NameVar -- just to see
       
       -- do whatever needed, apply any logic, call stored procedures, etc.
       /*
       Exec usp_Employee @name = @NameVar
       Exec usp_Department @ID = @IDVar,@name = @NameVar
       */
    
       SET @RowCount -= 1;
    END