School Item Crashes with SqlException

Issue #48 closed
Ghislain Hachey created an issue

When I go to Schools -> List and filter to see some school I can see schools in a table. School ID have a clickable link changing to state site.schools.list.item but this fails and throws the error shown in the image on the backend. school-item-error.PNG

Comments (10)

  1. Brian Lewis repo owner

    Nasty.

    This occurs in circumstances where

    • the collation sequence of the database differs from the default collation of the server; and

    • a stored procedure uses a temporary table, and tries to join it to a database table on a string field.

    The temp table and database table will have different collation sequences, giving rise to this error.

    SQl server unfortunately has used different values for the default collation over the years, but I'm surprised you ended up with

    SQL_Latin1_General_CP1_CI_AS in this day and age.

    Anyway, I can pretty quickly see where this has come from. For the record, the usual fix is to replace the temporary table ( which gets created in the temp db, and therefore has the collation of the temp db = server collation) with a table variable.

    I'll email the fix.

  2. Ghislain Hachey reporter

    On 4/12/16 10:30, Brian Lewis wrote:

    Well, this was a default installation of MS SQL Server Express.

    Should we document the SQL fix in this ticket? If it happened to me on a fresh basic install, it is bound to happen again :)

  3. Brian Lewis repo owner

    It seems from a quick look around that the default collation sequence may depend on the regional settings of the target machine... In any case , for a brand new installation we should go with Latin1_General_CP1_CI_AS

    However, there will be circumstances where you can;t control the server collation so we need to fix any potential references to temp tables in stored procs as indicated above. I did at one stage spend some time doing this - so it probably won't appear often.

    Need to review any stored proc containing a CREATE TABLE statement...

  4. Brian Lewis repo owner

    Propogating changes to the database is a bigger issue, and the approach we are using is to store in the git \database folder a schema file created by SQL DELTA. This represents the schema of all tables, views, stored procs etc... any database artefact. Sql Delta can use this snapshot to generate the necessary sql script to upgrade the target database to match that snapshot.

  5. Ghislain Hachey reporter

    Well, does the stored procedure fix the problem or one must run the Stored Procedure when the problem arises? If the later then I think it is not resolved. But minor and low priority since this does not necessarily happen often or at all. Maybe we document the steps to take and attach the Stored Procedure and call it CLOSED as oppose to RESOLVED.

  6. Brian Lewis repo owner

    the script below redefines the stored procedure - ie changes the code - which fixes the error.

    USE [SIEMIS]
    GO
    
    /****** Object:  StoredProcedure [pEnrolmentRead].[SchoolAnnualSummary]    Script Date: 12/04/2016 12:19:36 PM ******/
    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    
    -- =============================================
    -- Author:          Brian Lewis
    -- Create date: 1 10 2014
    -- Description:     summary of key survey statistics by year
    -- =============================================
    ALTER PROCEDURE [pEnrolmentRead].[SchoolAnnualSummary]
           @SchoolNo nvarchar(50) = null
           , @SurveyYear int = null
    WITH EXECUTE AS 'Pineapples'
    -- we'll let you read the teacher counts here, even if you do not have teacher read
    -- hence the need for the execute as
    AS
    BEGIN
           -- SET NOCOUNT ON added to prevent extra result sets from
           -- interfering with SELECT statements.
           SET NOCOUNT ON;
    
    -- strategy is temp table with each required field, records are added in sets
    -- then summed to bring all the fields together
           DECLARE @tmpAS TABLE
           (
                 schNo nvarchar(50)
                 , svyYear int
                 , minClass nvarchar(10)
                 , maxClass nvarchar(10)
                 , TeacherSurveyCount int
                 , ClassroomCount int
           )
    
    -- to begin make sure we have every life year for the school in the table
           INSERT INTO @TmpAS
           (schNo, svyYear)
           Select schNo, svyYear
           FROM SchoolLifeYears 
           WHERE
                 (
                        (@SchoolNo is null or @SchoolNo = SchNo)
                        AND (@SurveyYear is null or @SurveyYEar = svyYEar)
                 )
    
    -- add the teacher survey count records        
           INSERT INTO @tmpAS
                 (SchNo, svyYear, TeacherSurveyCount)
           SELECT 
                 schNo, svyYear, count(tchsID)
           FROM
                 SchoolSurvey SS
                 INNER JOIN TeacherSurvey TS
                 ON Ss.ssID = TS.ssID
           WHERE
                 (
                        (@SchoolNo is null or @SchoolNo = SchNo)
                        AND (@SurveyYear is null or @SurveyYEar = svyYEar)
                 )
           group by schNo, svyYear
    
    
    -- add the set of classroom records
           INSERT INTO @tmpAS
                 (SchNo, svyYear, ClassroomCount)
           SELECT 
                 schNo, svyYear, count(rmID)
           FROM
                 SchoolSurvey SS
                 INNER JOIN Rooms 
                 ON Ss.ssID = Rooms.ssID
           where
                 (
                        (@SchoolNo is null or @SchoolNo = SchNo)
                        AND (@SurveyYear is null or @SurveyYEar = svyYEar)
                 )
                 and rmType = 'CLASS'
           group by schNo, svyYear
    
    -- calculate the minimum and maximum levels for enrol
    ------
    ------Select SchNo
    ------       , SvyYear
    ------       , MinYear
    ------       , MaxYear
    ------       , min(case when L.lvlYear = MinYear then L.codeCode else null end) MinLevelCode
    ------       , min(case when L.lvlYear = MinYear then L.codeDescription else null end) MinLevel
    ------       , min(case when L.lvlYear = MaxYear then L.codeCode else null end) MaxLevelCode
    ------       , min(case when L.lvlYear = MAxYear then L.codeDescription else null end) MaxLevel
    ------
    ------
    ------FROM 
    ------ Enrollments E
    ------ 
    ------ INNER JOIN TRLevels L
    ------ ON E.enLevel = L.codeCode
    ------ INNER JOIN
    ------ (
    ------       Select S.SchNo
    ------              , S.svyYEar
    ------              , S.ssID
    ------              ,min(lvlYear) MinYear
    ------              , max(lvlYear) MaxYear
    ------       from   SchoolSurvey S
    ------              INNER JOIN   Enrollments E
    ------              ON S.ssID = E.ssID
    ------              INNER JOIN lkpLevels L
    ------              ON E.enLevel = L.codeCode
    ------       GROUP BY S.SchNo, S.svyYEar, S.ssID
    ------ ) MiniMax
    ------ ON MiniMax.ssID = E.ssID
    ------GROUP BY SchNo, svyYEar, minYEar, maxYear
    
           Select 
                 TMP.schNo
                 , TMP.svyYear
                 , SS.ssSchType
                 , SS.ssAuth
                 , SS.ssEnrol
                 , SS.ssEnrolM
                 , SS.ssEnrolF
                 , SS.ssID
                 , SS.ssPrinFirstName
                 , SS.ssPrinSurname
                 , SS.ssNote
                 , LMin.CodeDescription minLevel
                 , LMax.CodeDescription maxLevel
                 , ELS.MaxLevelEnrol
                 , LMaxEnrol.codeDescription  maxEnrolLevel
                 , TeacherSurveyCount
                 , ClassroomCount
                 , case when TeacherSurveyCount = 0  then null else cast(SS.ssEnrol as float)/TeacherSurveyCount end PTR
    
           FROM 
                 (
                 Select
    
                        schNo 
                        , svyYear 
                        , min(minClass) minClass
                        , max(maxClass) maxClass
                        , max(TeacherSurveyCount) TeacherSurveyCount
                        , max(ClassroomCount) ClassroomCount
                 FROM
                        @tmpAS
                 GROUP BY
                        schNo, svyYear
                 ) TMP
                 LEFT JOIN SchoolSurvey SS
                        ON ss.schNo = Tmp.SchNo
                        AND SS.svyYear = TMP.svyYear
                 LEFT JOIN pEnrolmentRead.ssIDEnrolmentLevelSummary ELS
                        ON SS.ssID = ELS.ssID
                 LEFT JOIN TRLevels LMin
                        On ELS.LowestLevel = LMin.codeCode
                 LEFT JOIN TRLevels LMax
                        ON ELS.HighestLevel = LMAx.CodeCode
                 LEFT JOIN TRLevels LMaxEnrol
                        ON ELS.LEvelOfMaxEnrol = LMaxEnrol.CodeCode
    
    
    END
    
    
    GO
    
  7. Ghislain Hachey reporter

    This is properly documented in case the problem arise. If it arises regularly it should be re-opened and looked into more thoroughly to identify the root cause.

  8. Log in to comment