【问题标题】:Is it possible to make a subquery over a group in t-sql?是否可以对 t-sql 中的组进行子查询?
【发布时间】:2023-03-23 06:50:01
【问题描述】:

我的目标是编写一个查询,该查询将返回具有 3 个标志字段的所有类别,如下所示:

ID |恩 |茹 |神父

语言标志必须根据是否有任何与该类别关联的课程记录打开或关闭。

我现在的陈述是这样的:

SELECT c.ID,
    (CASE WHEN c.ID IN (SELECT c.ID FROM Lessons AS l WHERE l.Category_Id = c.ID AND l.Language_Id = 1) THEN 1 ELSE 0 END) AS En,
    (CASE WHEN c.ID IN (SELECT c.ID FROM Lessons AS l WHERE l.Category_Id = c.ID AND l.Language_Id = 2) THEN 1 ELSE 0 END) AS Ru,
    (CASE WHEN c.ID IN (SELECT c.ID FROM Lessons AS l WHERE l.Category_Id = c.ID AND l.Language_Id = 3) THEN 1 ELSE 0 END) AS Fr
FROM LessonCategories AS c

问题是这个查询非常慢,因为 Lessons 表有超过 60,000 条记录,而且我运行了 3 次。

我正在寻找一种方法来提高此查询的效率。 我曾想过在类别和课程之间使用分组连接,但我不知道具体如何以及是否可能。

更快速查询的伪代码是:

SELECT c.[ID], 
    COUNT(l.Language_Id = 1) > 0 AS En
    COUNT(l.Language_Id = 2) > 0 AS Ru
    COUNT(l.Language_Id = 3) > 0 AS Fr
FROM CategoryTreeView AS c
INNER JOIN Lessons AS l ON l.Category_Id = c.ID
GROUP BY c.[ID]

可以使用有效的 t-sql 来表达吗? 或者有什么更好的方法来处理这种查询?

附:如果它有帮助,我不在乎得到一个按位标志字段而不是 3 个语言字段。

谢谢。

【问题讨论】:

    标签: tsql group-by subquery


    【解决方案1】:

    好吧,您只需将两个查询结合起来;)

    SELECT c.ID, 
           SUM(case when l.Language_Id = 1 then 1 else 0) AS En
           SUM(case when l.Language_Id = 2 then 1 else 0) AS Ru
           SUM(case when l.Language_Id = 3 then 1 else 0) AS Fr
    FROM CategoryTreeView AS c
    INNER JOIN Lessons AS l ON l.Category_Id = c.ID
    GROUP BY c.ID
    

    【讨论】:

    • +1,你可以省略 CategoryTreeView 表,因为它的字段都没有被使用
    【解决方案2】:

    您可以在第二个示例中使用计数:

    COUNT(case when l.Language_Id = 1 then 1 else null end) as EnCount
    COUNT(case when l.Language_Id = 2 then 1 else null end) as RuCount
    

    但实际上,您想在第一个示例中使用exists

    case when exists(SELECT 0 FROM Lessons AS l WHERE l.Category_Id = c.ID AND l.Language_Id = 1) then 1 else 0 end as En
    

    如果索引正确,三个existss 会快得多。

    【讨论】:

    • 每行一次索引查找比单个表扫描慢得多、多得多。更不用说每行 3 次搜索了!
    • @Andomar 让 SQL Server 决定。如果扫描结果更便宜,它将选择执行exists 的扫描。不过还是exists,比count便宜。
    【解决方案3】:

    试试这个

    SELECT c.ID,
        (CASE WHEN l.Language_Id = 1 THEN 1 ELSE 0 END) AS En,
        (CASE WHEN l.Language_Id = 2 THEN 1 ELSE 0 END) AS Ru,
        (CASE WHEN l.Language_Id = 3 THEN 1 ELSE 0 END) AS Fr
    FROM LessonCategories AS c
    INNER JOIN Lessons l ON c.ID = l.CategoryId
    

    顺便说一句, 目前,您正在通过应用CASE 语句在SELECT 子句中进行过滤。您可以通过将过滤语句移动到在SELECT 之前执行的WHERE 子句来显着提高任何查询的性能。

    【讨论】:

      【解决方案4】:

      一个完全不同的方法尝试枢轴。

      DECLARE @ctw TABLE(id int, languageid int) 
      
      insert @ctw values(1,1)
      insert @ctw values(1,1)
      insert @ctw values(1,2)
      insert @ctw values(1,3)
      insert @ctw values(2,1)
      insert @ctw values(2,3)
      
      SELECT * FROM 
      (
      SELECT id, CASE languageid WHEN 1 THEN 'en' WHEN 2 THEN 'ru' WHEN 3 THEN 'fr' END language
      FROM @ctw) a
      PIVOT
      (count(language) 
      FOR language
      in([en],[ru],[fr]) 
      )AS p ORDER BY id
      

      测试在这里https://data.stackexchange.com/stackoverflow/q/107960/

      【讨论】:

        猜你喜欢
        • 2014-06-09
        • 2010-09-08
        • 2011-07-06
        • 2017-03-04
        • 1970-01-01
        • 2015-01-25
        • 2011-11-17
        • 1970-01-01
        • 2021-10-31
        相关资源
        最近更新 更多