【问题标题】:SELECT query with multiple sub-queries for counts带有多个子查询的 SELECT 查询
【发布时间】:2013-03-19 08:01:33
【问题描述】:

我刚刚为报告编写了此查询。但是我最初在每个子查询上都没有使用日期范围过滤器来编写它。但这没有用。所以我将它添加到每个子查询中。这行得通,但我真的不喜欢每次都重复它,有没有语法可以更简单地做同样的事情?

 SELECT Count(r.id)                       AS cnt_total,
   (SELECT Count(r1.entity_id)
    FROM   auto_reminders_members r1
    WHERE  r1.reminder_id = r.reminder_id
           AND r1.date_last_reminder BETWEEN CONVERT(DATETIME, '03/28/2013',
                                             101)
                                             AND
               CONVERT(DATETIME,
               '03/28/2013' + ' 23:59:59.997 ', 101)
           AND r1.action = 'notnow') AS cnt_notnow,
   (SELECT Count(r1.entity_id)
    FROM   auto_reminders_members r1
    WHERE  r1.reminder_id = r.reminder_id
           AND r1.date_last_reminder BETWEEN CONVERT(DATETIME, '03/28/2013',
                                             101)
                                             AND
               CONVERT(DATETIME,
               '03/28/2013' + ' 23:59:59.997 ', 101)
           AND r1.action = 'insert') AS cnt_insert,
   (SELECT Count(r1.entity_id)
    FROM   auto_reminders_members r1
    WHERE  r1.reminder_id = r.reminder_id
           AND r1.date_last_reminder BETWEEN CONVERT(DATETIME, '03/28/2013',
                                             101)
                                             AND
               CONVERT(DATETIME,
               '03/28/2013' + ' 23:59:59.997 ', 101)
           AND r1.action = 'update') AS cnt_update,
   (SELECT Count(r1.entity_id)
    FROM   auto_reminders_members r1
    WHERE  r1.reminder_id = r.reminder_id
           AND r1.date_last_reminder BETWEEN CONVERT(DATETIME, '03/28/2013',
                                             101)
                                             AND
               CONVERT(DATETIME,
               '03/28/2013' + ' 23:59:59.997 ', 101)
           AND r1.action = 'verify') AS cnt_verify
FROM   auto_reminders_members r
WHERE  r.reminder_id = 1
       AND r.date_last_reminder BETWEEN CONVERT(DATETIME, '03/28/2013', 101) AND
                                            CONVERT(DATETIME,
                                            '03/28/2013' + ' 23:59:59.997 ', 101
                                            )
GROUP  BY r.reminder_id  

【问题讨论】:

  • 子查询部分是否总是相同的?
  • 请问什么版本的 SQL Server?

标签: sql-server select count sql-server-2012


【解决方案1】:

这是一种使用GROUPING SETSPIVOT 的方法,但这取决于您使用的版本,您忘记告诉我们了:

DECLARE @reminder_id INT, @date DATE;

SELECT @reminder_id = 1, @date = '20130328';

;WITH x AS 
(
  SELECT [action] = COALESCE([action],'Total'), c2 = COUNT(*)
    FROM dbo.auto_reminders_members
    WHERE reminder_id = @reminder_id
    AND CONVERT(DATE, date_last_reminder) = @date
    GROUP BY GROUPING SETS(([action]), ())
)
SELECT reminder_id = @reminder_id, * FROM x 
PIVOT 
(
  MAX([c2]) FOR [action] IN ([Total],[notnow],[insert],[update],[verify])
) AS p;

如果是 2005,您可以将 GROUPING SETS 行替换为旧式语法:

    GROUP BY [action] WITH ROLLUP

我真的希望人们不要再纵容BETWEEN 和这种神奇的“一天结束”的东西。如果你想要一整天,你的 WHERE 子句应该是:

WHERE CONVERT(DATE, r.date_last_reminder) = '20130328'

或者,如果你是 2005 年:

WHERE r.date_last_reminder >= '20130328'
  AND r.date_last_reminder <  '20130329'

这里有一些重要的点,主要是 23:59:59.997 可能会根据基础数据类型四舍五入或遗漏数据,没有理由您应该将日期时间值转换为字符串来执行查询,以及区域性m/d/y 这样的格式由于各种原因而不好(例如,如果您的查询是针对 03/08/2013 的,我不知道您是在 3 月 8 日之后还是 8 月 3 日之后)。

请阅读这些文章:

您还应该小心为列名使用保留字/关键字,例如 entity_idaction

【讨论】:

  • 这很有趣,我采用第一种方法进行求和,这对这个项目很有效,但是我有一个更复杂的查询,我必须做一堆不同的计数不同的项目,这可能更合适。从来没有见过这样的东西。顺便说一句,我们在 SQL SErver 2012 上
【解决方案2】:
select convert(nvarchar(10),warnt.created_dt,120) 'Date',loc.loc_name 'Location',--   
loctype.LocationTypeDesc,
 (select SUM(stk.sel_price) from tbl_mst_stock as stk 
inner join tbl_gen_warranty as wrn on stk.Stock_ID=wrn.Stock_Id
inner join tbl_mst_model as mdl on mdl.Model_ID=wrn.model_id
where wrn.mobile_type like 'SL')'Selleing Price',
(Select COUNT(stk.stk_code) from tbl_mst_stock as stk 
inner join tbl_gen_warranty as warn on stk.Stock_ID=warn.Stock_Id
inner join tbl_mst_model mdl on mdl.Model_ID=warn.model_id
    where warn.mobile_type like 'SL') 'Stock count'
From tbl_mst_location as loc 
inner join tbl_mst_location_types loctype on loc.LocationTypeID=loctype.LocationTypeID
inner join tbl_gen_warranty as warnt on loc.Location_ID=warnt.Location_id
Where  loctype.LocationTypeID=1 and loctype.group_id=1 
and (CONVERT(nvarchar(10),warnt.created_dt,120) 
Between CONVERT(nvarchar(10),'2013-01-01',120) and convert(nvarchar(10),'2013-07-09'))
Group by loc.loc_name,loctype.LocationTypeDesc,warnt.created_dt,loctype.LocationTypeID

【讨论】:

    【解决方案3】:

    这是一种无需所有子查询的方法

    SELECT  Count(r.id) AS cnt_total,
            sum(case when r.action = 'notnow' then 1 else 0 end) as 'cnt_notnow',
            sum(case when r.action = 'insert' then 1 else 0 end) as 'cnt_insert',
            sum(case when r.action = 'update' then 1 else 0 end) as 'cnt_update',
            sum(case when r.action = 'verify' then 1 else 0 end) as 'cnt_verify'        
    FROM    auto_reminders_members r
    
    WHERE  r.reminder_id = 1
           AND CONVERT(DATE, r.date_last_reminder) = '20130328'
    

    我还稍微清理了查询,删除了 group by,因为这将始终为 1,并更改日期比较以避免使用逻辑之间的混乱(感谢 Aaron 的评论)

    【讨论】:

    • 仅供参考 BETWEEN remains terrible,也没有理由返回 reminder_id 列,更不用说 GROUP BY 它,如果它是一个常量 (1)。
    • 对,我还没有太注意清理查询的那部分
    【解决方案4】:

    找到一种替代解决方案:

     SELECT Sum(1)   AS cnt_total,
       Sum(CASE
             WHEN r.action = 'notnow' THEN 1
             ELSE 0
           END) AS cnt_notnow,
       Sum(CASE
             WHEN r.action = 'insert' THEN 1
             ELSE 0
           END) AS cnt_insert,
       Sum(CASE
             WHEN r.action = 'update' THEN 1
             ELSE 0
           END) AS cnt_update,
       Sum(CASE
             WHEN r.action = 'verify' THEN 1
             ELSE 0
           END) AS cnt_verify
    

    来自 auto_reminders_members r WHERE r.reminder_id = 1 AND r.date_last_reminder BETWEEN CONVERT(DATETIME, '03/28/2013', 101) AND 转换(日期时间, “2013 年 3 月 28 日”+“23:59:59.997”,101 )

    【讨论】:

    • 是的,这是许多人发布的正确解决方案。您可能应该选择其中之一作为答案。
    【解决方案5】:

    试试这个:

    select 
        reminder_id,
        count(1) as Total,
        sum(case when  r1.action = 'notnow' then 1 else 0 end) as cnt_notnow,
        sum(case when  r1.action = 'insert' then 1 else 0 end) as cnt_insert,
        sum(case when  r1.action = 'update' then 1 else 0 end) as cnt_update,
        sum(case when  r1.action = 'verify' then 1 else 0 end) as cnt_verify
    from auto_reminders_members r
    WHERE  r.reminder_id = 1
       AND r.date_last_reminder BETWEEN CONVERT(DATETIME, '03/28/2013', 101) AND
                                            CONVERT(DATETIME,
                                            '03/28/2013' + ' 23:59:59.997 ', 101
                                            )
    GROUP  BY r.reminder_id 
    

    【讨论】:

      【解决方案6】:

      你也许可以做这样的事情

      SELECT Count(r.id) AS cnt_total,
        SUM(CASE WHEN r1.action = 'notnow') THEN 1 ELSE 0 END) AS cnt_notnow,
        SUM(CASE WHEN r1.action = 'insert') THEN 1 ELSE 0 END) AS cnt_insert,
        SUM(CASE WHEN r1.action = 'update') THEN 1 ELSE 0 END) AS cnt_update,
        SUM(CASE WHEN r1.action = 'verify') THEN 1 ELSE 0 END) AS cnt_verify,
      
      FROM   auto_reminders_members r
      WHERE  r.reminder_id = 1
             AND r.date_last_reminder BETWEEN CONVERT(DATETIME, '03/28/2013', 101) AND
                                                  CONVERT(DATETIME,
                                                  '03/28/2013' + ' 23:59:59.997 ', 101
                                                  )
      GROUP  BY r.reminder_id
      

      【讨论】:

        猜你喜欢
        • 2017-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-24
        • 2021-03-14
        • 1970-01-01
        • 2019-01-02
        相关资源
        最近更新 更多