Search code examples
t-sqlcountquery-optimization

SQL : Spread Count() by conditions in multiple columns


I have a table of totes going or being into stations named [Tote], like this:

Id          Barcode            Checksum    PackageType Sts         Destination LastStationExit LastUpdated
----------- ------------------ ----------- ----------- ----------- ----------- --------------- -----------------------
-2147483645 777000000000000001 586965230   0           1           NULL        MS32            2016-04-19 14:15:32.577
-2147483644 777000000000000002 821846254   0           1           MS01        NULL            2016-04-19 15:08:16.140
-2147483643 777000000000000003 1174167790  0           1           NULL        MS02            2016-04-19 15:08:20.340
-2147483642 777000000000000004 1543266542  0           1           NULL        MS31            2016-04-19 15:08:24.510
-2147483641 777000000000000005 3424831     0           1           NULL        MS01            2016-04-19 15:08:31.060
-2147483640 777000000000000006 573850175   0           1           MS01        NULL            2016-04-19 15:08:34.200

I've got another table which contains all the existing station named [MezzanineStation]:

Name
----
MS01
MS02
MS11
MS12
MS21
MS22
MS31
MS32

I'm trying to count, for every station, how many totes are going to (Destination=StationName) and how many totes are into the station (LastStationExit=StationName).

I wrote the following request which works correctly:

-- Création d'une variable tampon pour isoler les bacs concernés par le calcul
DECLARE @FilteredToteExtract TABLE
(
   Destination Varchar(4),
   LastStationExit Varchar(4)
)
INSERT INTO @FilteredToteExtract([Destination],[LastStationExit])
SELECT [Destination],[LastStationExit]
FROM [Tote] T
WHERE [PackageType]=0
    AND LastUpdated>=DATEADD(HOUR,-12,GETDATE())
    AND EXISTS (SELECT * FROM [MezzanineStation] MS WHERE MS.[Name]=T.[Destination] OR MS.[Name]=T.[LastStationExit])

-- Calcul de l'occupation (prévue et courante) des gares de la mezzanine
SELECT DISTINCT MS.Name,T_Destination.[Count] AS NbTotesOngoing,T_LastStationExit.[Count] AS NbTotesInside
FROM [MezzanineStation] MS
LEFT JOIN
( SELECT
    Destination,
    COUNT(*) AS [Count]
  FROM @FilteredToteExtract
  GROUP BY Destination
) T_Destination
ON MS.Name = T_Destination.Destination
LEFT JOIN
( SELECT
    LastStationExit,
    COUNT(*) AS [Count]
  FROM @FilteredToteExtract
  GROUP BY LastStationExit
) T_LastStationExit
ON MS.Name = T_LastStationExit.LastStationExit

It gives me this kind of result:

Name NbTotesOngoing NbTotesInside
---- -------------- -------------
MS01 2              1
MS02 NULL           1
MS11 NULL           NULL
MS12 NULL           NULL
MS21 NULL           NULL
MS22 NULL           NULL
MS31 NULL           1
MS32 NULL           NULL

Here are the index that I created on the [Tote] table:

CREATE INDEX IX_Tote_PackageType ON [Tote]([PackageType])
CREATE INDEX IX_Tote_LastStationExit ON [Tote]([LastStationExit])
CREATE INDEX IX_Tote_LastUpdated ON [Tote]([LastUpdated])

Do you think this request can be more optimized?


Solution

  • So, the last revision which works fine and completely meets the KISS requirements ^_^ (thanks to Paparazzi!)

    CREATE FUNCTION dbo.GetMezzanineStationOccupancyForTotes()
    RETURNS @StationOccupancy TABLE
    (
       [Level]           Int            NOT NULL,   -- Niveau de la gare
       [Priority]        Int            NOT NULL,   -- Ordre de priorité spécifié dans la supervision (0 = Priorité la + élevée)
       [Name] T_MEZZANINE_STATION_NAME  NOT NULL,   -- Nom de la gare
       [Open]            Bit NOT NULL,  -- Ouverture/Fermeture de la gare (TRUE = Gare ouverte)
       [NbTotesOngoing]  Int,           -- Nombre de bacs à destination de la gare
       [NbTotesInside]   Int,           -- Nombre de bacs présents dans la gare
       [StationOccupancy] AS [NbTotesOngoing]+[NbTotesInside]   -- Occupation de la gare
    )
    AS
       BEGIN
       /* Constantes */
       --<CST=ENU_TOTES_PACKAGE_TYPE>
       -- Equivalent de l'énumération ENU_TOTES_PACKAGE_TYPE
       DECLARE @TOTES_PACKAGE_TYPE_TOTE       Int = 0, -- Bac
               @TOTES_PACKAGE_TYPE_RETURN_BOX Int = 1, -- Carton retour
               @MX_TOTES_PACKAGE_TYPE         Int = 2
       --</CST=ENU_TOTES_PACKAGE_TYPE>
    
       /* Variables locales */
       DECLARE @OldestIdleTime DateTime -- Date de dernière mise à jour la plus ancienne à prendre en compte
    
       SELECT @OldestIdleTime=DATEADD(HOUR,-[NbHoursForgetTote],GETDATE())
       FROM [Parameter] (NOLOCK)
    
       -- Création d'une variable tampon pour isoler les bacs concernés par le calcul
       DECLARE @FilteredToteExtract TABLE
       (
          Destination Varchar(4),
          LastStationExit Varchar(4)
       )
       INSERT INTO @FilteredToteExtract([Destination],[LastStationExit])
       SELECT [Destination],[LastStationExit]
       FROM [Tote] T (NOLOCK)
       WHERE [PackageType]=@TOTES_PACKAGE_TYPE_TOTE
           AND LastUpdated>=@OldestIdleTime
           AND EXISTS (SELECT * FROM [MezzanineStation] MS WHERE MS.[Name]=T.[Destination] OR MS.[Name]=T.[LastStationExit])
    
       /* Fonction */
       -- Calcul de l'occupation (prévue et courante) des gares de la mezzanine
       INSERT INTO @StationOccupancy
       SELECT
          MS.[Level],
          MS.[Priority],
          MS.[Name],
          MS.[Open],
          ISNULL(COUNT(T.[Destination]),0),
          ISNULL(COUNT(T.[LastStationExit]),0)
       FROM [MezzanineStation] MS (NOLOCK)
       LEFT JOIN @FilteredToteExtract T
          ON MS.[Name] = ISNULL(T.[Destination],T.[LastStationExit])  
       GROUP BY MS.[Name],MS.[Level],MS.[Priority],MS.[Open]
    
       /* Résultats */
       RETURN
       END