Search code examples
sqlsql-serverstored-procedures

Temporary Information is remaining between executions of a stored procedure


I have the following table:

CREATE TABLE [dbo].[AttendanceRecords]
(
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [SampleDate] [date] NULL,
    [Weekday] [nvarchar](20) NULL,
    [FirstName] [nvarchar](50) NULL,
    [LastName] [nvarchar](50) NULL,
    [Department] [nvarchar](50) NULL,
    [Area] [nvarchar](20) NULL,
    [TotalTime] [int] NULL,
    [FirstInTime] [datetime] NULL,
    [LastOutTime] [datetime] NULL,
    [GrossTotalTime] [int] NULL
) ON [PRIMARY]

and the following stored procedure in SQL Server 2019:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[GetGeneralAttendanceData]
(
    @FirstName nvarchar(50) = null,
    @LastName nvarchar(50) = null,
    @StartDate datetime = null,
    @EndDate datetime = null,
    @Department nvarchar(50) = null,
    @PartialName nvarchar(50) = null
)
AS
BEGIN
    SET NOCOUNT ON;

    CREATE TABLE #finalTable (FirstName NVARCHAR(50), LastName NVARCHAR(50));
    CREATE TABLE #secondaryTable (FirstName NVARCHAR(50), LastName NVARCHAR(50));
    
    SELECT *
    INTO #TimeCards
    FROM AttendanceRecords
    WHERE 1=1 
        AND (@FirstName is NULL or FirstName = @FirstName)
        AND (@LastName is NULL or LastName = @LastName)
        AND (@StartDate is NULL or SampleDate >= @StartDate)
        AND (@EndDate is NULL or SampleDate <= @EndDate)
        AND (@Department is NULL or Department = @Department)
        AND (@PartialName IS NULL OR FirstName LIKE '%' + @PartialName + '%' OR LastName LIKE '%' + @PartialName + '%')
    ORDER BY SampleDate;

    DECLARE @cols NVARCHAR(MAX) = null;
    DECLARE @colsWithoutWeekdays NVARCHAR(MAX) = null;
    DECLARE @colsAndTypes NVARCHAR(MAX) = null;
    DECLARE @query NVARCHAR(MAX) = null;
    DECLARE @SQL NVARCHAR(MAX) = null;
    
    SELECT @cols = STUFF((SELECT ',' + QUOTENAME(d.SampleDate)
                        FROM
                        (
                            SELECT convert(nvarchar(25), SampleDate,25) as SampleDate
                            FROM #TimeCards
                        ) d
                        GROUP BY SampleDate
                        ORDER BY SampleDate
                        FOR XML PATH(''), TYPE
                        ).value('.', 'NVARCHAR(MAX)')
                    ,1,1,'');

    SELECT @colsAndTypes = STUFF((SELECT ',' + QUOTENAME(d.SampleDate) + ' NVARCHAR(50)'
                        FROM
                        (
                            SELECT convert(nvarchar(25), SampleDate,25)+' - '+ [WeekDay] as SampleDate
                            FROM #TimeCards
                        ) d
                        GROUP BY SampleDate
                        ORDER BY SampleDate
                        FOR XML PATH(''), TYPE
                        ).value('.', 'NVARCHAR(MAX)')
                    ,1,1,'');

    SET @SQL = 'ALTER TABLE #finalTable ADD ' + @colsAndTypes;
    EXECUTE(@SQL);

    SET @SQL = 'ALTER TABLE #secondaryTable ADD ' + @colsWithoutWeekdays;
    EXECUTE(@SQL);

    SET @query = 'SELECT FirstName, LastName, ' + @cols + '
                  FROM
                  (SELECT FirstName, LastName, SampleDate, TotalTime FROM #TimeCards) x
                  pivot
                  (sum(TotalTime) for SampleDate in (' + @cols + ')) p
                  order by LastName, FirstName';
    
    SET @SQL = 'INSERT INTO #finalTable ' + @query;
    EXECUTE(@SQL);

    SET @SQL = 'SELECT * FROM #finalTable';
    EXECUTE(@SQL);

    SET @SQL = 'DROP TABLE #finalTable';
    EXECUTE(@SQL);
    SET @SQL = 'DROP TABLE #secondaryTable';
    EXECUTE(@SQL);
    SET @SQL = 'DROP TABLE #TimeCards';
    EXECUTE(@SQL);

    IF OBJECT_ID('tempdb..#finalTable') IS NOT NULL DROP TABLE #finalTable;
    IF OBJECT_ID('tempdb..#secondaryTable') IS NOT NULL DROP TABLE #secondaryTable;
    IF OBJECT_ID('tempdb..#TimeCards') IS NOT NULL DROP TABLE #TimeCards;
END

I've got to be missing something because it appears as though the data keeps staying resident between executions. I found that since I'm altering the temp tables using the Execute command (can you tell me what this is called, transaction?) so I had to do the same to drop the temp table. This took me a while to figure out because I thought all temp tables were stored in the same place. I really don't know where these are though, I searched and cannot find them.

My problem now is that when I search based upon @StartDate and @EndDate, the results will shrink, but will not expand after enlarging the gap between the two dates.

Plus, when I reduce the gap, I get the error:

Msg 0, Level 11, State 0, Line 2
A severe error occurred on the current command. The results, if any, should be discarded.

But I just press F5 again and it executes correctly.

So, for example, I'll do the following:

DECLARE @return_value int

EXEC    @return_value = [dbo].[GetGeneralAttendanceData]
        @StartDate = N'08/26/2024',
        @EndDate = N'09/29/2024'

SELECT  'Return Value' = @return_value
GO

This will work with no issue when ran just after the stored procedure has been altered.

Then I'll change @EndDate to 08/29/2024 and at first I get the error, then I'll run it a second time and it works correctly.

Finally, I will change the date back to 09/29/2024 and it will continue to return the results from the 08/29/2024 query. I've worked on this for a few hours now and I cannot find where I've gone wrong.

Please help and thank you in advance.


Solution

  • I don't know what your issue was with the temp tables, but they should drop automatically when you close the connection. Best practice would be to DROP IF EXISTS before you start.

    But you don't actually need temp tables here. You can create dynamic SQL with the base query, then use that to first get the columns, then create the pivot. It will also be more efficient than using IS NULL OR conditions.

    CREATE OR ALTER PROCEDURE [dbo].[GetGeneralAttendanceData]
    (
        @FirstName nvarchar(50) = null,
        @LastName nvarchar(50) = null,
        @StartDate datetime = null,
        @EndDate datetime = null,
        @Department nvarchar(50) = null,
        @PartialName nvarchar(50) = null
    )
    AS
    
    SET NOCOUNT ON;
    
    DECLARE @baseQuery nvarchar(max) = '
        FROM AttendanceRecords ac
        WHERE 1=1
    ';
    
    IF @FirstName IS NOT NULL
      SET @baseQuery += '
          AND FirstName = @FirstName';
    
    IF @LastName IS NOT NULL
      SET @baseQuery += '
          AND LastName = @LastName';
    
    IF @StartDate IS NOT NULL
      SET @baseQuery += '
          AND SampleDate >= @StartDate';
    
    IF @EndDate IS NOT NULL
      SET @baseQuery += '
          AND SampleDate <= @EndDate';
    
    IF @Department IS NOT NULL
      SET @baseQuery += '
          AND Department = @Department';
    
    IF @PartialName IS NOT NULL
      SET @baseQuery += '
          AND (FirstName LIKE ''%'' + @PartialName + ''%'' OR LastName LIKE ''%'' + @PartialName + ''%''';
    
    DECLARE @cols NVARCHAR(MAX);
    DECLARE @sql NVARCHAR(MAX) = '
    SELECT @cols = STRING_AGG(QUOTENAME(d.SampleDate), '','') WITHIN GROUP (ORDER BY SampleDate)
    FROM
    (
            SELECT convert(nvarchar(25), SampleDate,25) as SampleDate
            ' + @baseQuery + '
            GROUP BY SampleDate
    ) d;
    ';
    
    PRINT @sql;  -- your friend
    
    EXEC sp_executesql @sql,
      N'@cols nvarchar(max) OUT,
        @FirstName nvarchar(50),
        @LastName nvarchar(50),
        @StartDate datetime,
        @EndDate datetime,
        @Department nvarchar(50),
        @PartialName nvarchar(50)',
      @cols OUT,
      @FirstName,
      @LastName,
      @StartDate,
      @EndDate,
      @Department,
      @PartialName;
    
    SET @sql = '
    SELECT FirstName, LastName, ' + @cols + '
    FROM (
        SELECT FirstName, LastName, TotalTime,
           convert(nvarchar(25), SampleDate,25) as SampleDate
        ' + @baseQuery + '
    ) x
    pivot (
      sum(TotalTime) for SampleDate in (
        ' + @cols + '
      )
    ) p
    order by LastName, FirstName;
    ';
        
    
    PRINT @sql;  -- your friend
    
    EXEC sp_executesql @sql,
      N'@FirstName nvarchar(50),
        @LastName nvarchar(50),
        @StartDate datetime,
        @EndDate datetime,
        @Department nvarchar(50),
        @PartialName nvarchar(50)',
      @FirstName,
      @LastName,
      @StartDate,
      @EndDate,
      @Department,
      @PartialName;
    

    db<>fiddle

    To be honest I would strongly recommend you use your application to do dynamic pivots, as SQL is not really designed for this.