SQL:按多列条件传播Count()

时间:2016-04-19 14:21:13

标签: tsql count query-optimization

我有一张手提箱正在进入或正在进入名为[Tote]的车站,如下所示:

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

我有另一张桌子,其中包含所有名为[MezzanineStation]的现有电台:

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

我正在尝试为每个电台计算到达的电量(目的地= StationName)以及电台有多少电量(LastStationExit = StationName)。

我写了以下请求,它正常工作:

-- 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

它给了我这样的结果:

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

以下是我在[Tote]表上创建的索引:

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

您认为此请求可以更优化吗?

3 个答案:

答案 0 :(得分:0)

您可以尝试(如果您还没有)替换您的" DISTINCT"使用" GROUP BY"。有时,这会产生积极的影响,因为将使用可能影响性能的不同执行计划。

如果您需要提高性能并根据您的要求,您可能希望使用内存优化表变量而不是普通表变量:https://msdn.microsoft.com/en-us/library/dn535766.aspx

答案 1 :(得分:0)

除非您在MezzanineStation中有重复项,否则您不应该需要明确的 如果你要实现,那么使用带有索引的#temp(不是表格)
但我认为你不需要实现 LastUpdated上的索引可能会有所帮助

SELECT MSM.[Name], TD.Count as TDcount, TL.Count as TLcount
  FROM [MezzanineStation] MSM
  LEFT JOIN ( SELECT MS.[Name], count(T.[Destination]) as [Count]
                FROM [Tote] T
                JOIN [MezzanineStation] MS
                      ON MS.[Name] = T.[Destination] 
               WHERE T.[PackageType] = 0
                 AND T.LastUpdated >= DATEADD(HOUR,-12,GETDATE())
               GROUP BY MS.[Name]
            ) as TD
              ON TD.[Name] = MSM.[Name]
  LEFT join ( SELECT MS.[Name], count(T.[LastStationExit]) as count
                FROM [Tote] T
                JOIN [MezzanineStation] MS
                       ON MS.[Name] = T.[LastStationExit])
               WHERE T.[PackageType] = 0
                 AND T.LastUpdated >= DATEADD(HOUR,-12,GETDATE()) 
               GROUP BY MS.[Name] 
             ) as TL
               ON TL.[Name] = MSM.[Name]

如果其中一列始终为null,那么它很简单

SELECT MS.[Name]
     , count(T.[Destination]) as [DestinationCount]
     , count(T.[LastStationExit]) as [LastStationExitCount]
  FROM [MezzanineStation] MS
  LEFT JOIN [Tote] T
         ON MS.[Name] = isnull(T.[Destination], T.[LastStationExit])  
 WHERE T.[PackageType] = 0
   AND T.LastUpdated >= DATEADD(HOUR,-12,GETDATE())
 GROUP BY MS.[Name]

答案 2 :(得分:-1)

所以,最后一个版本工作正常,完全符合KISS要求^ _ ^(感谢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