SQL Server How to avoid local variable and subquery to return child records from child table

bihw5rsg  于 2023-05-21  发布在  其他
关注(0)|答案(2)|浏览(124)

I have the stored procedure in SQL Server. I have two tables a and b. The B table has the foreign key AId to the table A to its primary key Id. I need to return records from parent table A filtered by name and and the same time the records from the child table they are associated by its id record of table A. I implemented as I listed. Is there a way to use the id from the query for the second query? Can I avoid subqueries and left joins?

CREATE PROCEDURE [dbo].[sp_GetByName]
    (@name NVARCHAR(255))
AS  
BEGIN
    SELECT
        [a].[Id],
        [a].[Name]
    FROM 
        [dbo].[a]
    WHERE 
        [a].[Name] = @name;
    
    DECLARE @aId INT = (SELECT TOP (1) [Id] FROM [dbo].[a] 
                        WHERE [Name] = @name);

    SELECT
        [b].[Id],
        [b].[AId],
        [a].[Name]
    FROM 
        [dbo].[b] 
    LEFT JOIN 
        [dbo].[a] ON [b].[AId] = [a].[Id]
    WHERE 
        [b].[AId] = @aId;
END;

Is there a way to use the id from the query for the second query? Can I avoid subqueries and left joins?

kuarbcqp

kuarbcqp1#

If the Name field is a unique key (and consequentially the result of the first query is a single row) you could use scalar variables, like in the following example:

CREATE PROCEDURE [dbo].[sp_GetByName](@name NVARCHAR(255))
AS BEGIN
    DECLARE @Id INT;
    DECLARE @Name NVARCHAR(MAX);
    
    -- Save the result to avoid recalculate it again
    SELECT @Id = [Id], @Name = [Name]
    FROM [dbo].[a]
    WHERE [Name] = @name;
    
    SELECT [Id] = @Id, Name = @Name; 
    
    SELECT
        [b].[Id],
        [b].[AId],
        Name  = @Name
    FROM [dbo].[b] 
    WHERE [b].[AId] = @Id;
END;

If the name is not uinique you could use a table variable or a temporary table (if the returned dataset is big) to store the results and reuse next:

CREATE PROCEDURE [dbo].[sp_GetByName](@name NVARCHAR(255))
AS BEGIN
    DECLARE @mainResult TABLE (Id INT, Name NVARCHAR(MAX));
    
    -- Save the result to avoid recalculate it again
    INSERT INTO @mainResult
    SELECT [Id], [Name]
    FROM [dbo].[a]
    WHERE [Name] = @name;
    
    SELECT *
    FROM @mainResult    
    
    SELECT
        [b].[Id],
        [b].[AId],
        [a].[Name]
    FROM [dbo].[b] 
    JOIN @mainResult [a] ON [b].[AId] = [a].[Id];
END;
af7jpaap

af7jpaap2#

If I understand the question correctly, you can use the solution below.

Using your original SPROC, if you had two records with Name=Bob , your second dataset would only return b records for MAX(Id). The solution offered here would return b records for all Id values with a matching 'Name' value.

NOTE: If your data won't have duplicate Name values, I would suggest returning the value 'Id' as an output parameter. By doing so, someone that is calling your SPROC knows that when you pass in @Name you only get one @Id returned.

CREATE PROCEDURE [dbo].[sp_GetByName](@name NVARCHAR(255))
AS BEGIN
SELECT
    [a].[Id],
    [a].[Name]
FROM [dbo].[a]
WHERE [a].[Name] = @name;

SELECT
    [b].[Id],
    [b].[AId],
    [a].[Name]
FROM [dbo].[b] LEFT JOIN [dbo].[a] ON [b].[AId] = [a].[Id]
WHERE [a].[Name] = @name;
END;

相关问题