【问题标题】:SQL : Spread Count() by conditions in multiple columnsSQL:通过多列中的条件传播 Count()
【发布时间】:2016-04-19 14:21:12
【问题描述】:

我有一张手提袋的桌子,上面有一个名为 [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

我正在尝试计算每个车站有多少手提箱 (Destination=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])

你认为这个请求可以更优化吗?

【问题讨论】:

    标签: tsql count query-optimization


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      除非您在 MezzanineStation 中有重复项,否则您不应该需要 distinct
      如果您要实现,则使用带有索引的#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]
      

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

      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]
      

      【讨论】:

      • 我删除了 DISTINCT,因为 [MezzanineStation] 中确实没有重复项。
      • 嗯,实际上,对于每条记录,其中一列应该始终为空,因为手提袋不能同时“去”和“进入”车站……起初我是这样设计的处理没有一个是空的奇怪情况,但你是对的,我会像你说的那样简化它。
      • [LastUpdated] 上已经有一个索引 ^_^ 对于另一个客户,我尝试通过触发器更新这种列,但它在性能方面非常糟糕,所以现在我只需使用其他数据...
      【解决方案3】:

      所以,最后一个版本运行良好,完全符合 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
      

      【讨论】:

      • 为什么我被否决了?我认为给出正确而整洁的答案会很好。
      猜你喜欢
      • 2017-04-23
      • 1970-01-01
      • 1970-01-01
      • 2020-11-23
      • 1970-01-01
      • 1970-01-01
      • 2021-09-10
      • 2021-03-29
      • 1970-01-01
      相关资源
      最近更新 更多