【问题标题】:Performance tune tSQL Query count(*) & subqueries性能调整 tSQL 查询计数(*)和子查询
【发布时间】:2016-06-14 01:24:14
【问题描述】:

我知道有更好的方法来完成我想要完成的工作。尽管查询有效,但我担心它的性能会随着数据集的增长而受到影响。

我什至不需要有人重写我所拥有的,如果他们愿意为我指出我应该研究的主题的方向,我将不胜感激。

我试图通过此查询返回的是处于或高于某个状态的记录数的计数。

提前感谢您的帮助!

SELECT ( SELECT count(*) FROM TABLE1 c1 WHERE ( c1.U_KEY3 NOT LIKE 'z%' AND (c1.U_KEY1 = '' or c1.U_KEY1 IS NULL) ) ) AS 'STATUS is EMPTY' , ( SELECT count(*) FROM TABLE1 c1 WHERE ( c1.U_KEY3 NOT LIKE 'z%' AND LEFT(c1.U_KEY1,2) >= '70' ) ) AS 'STATUS > 70' , ( SELECT count(*) FROM TABLE1 c1 WHERE ( c1.U_KEY3 NOT LIKE 'z%' AND LEFT(c1.U_KEY1,2) >= '50' ) ) AS 'STATUS > 50' , ( SELECT count(*) FROM TABLE1 c1 WHERE ( c1.U_KEY3 NOT LIKE 'z%' AND LEFT(c1.U_KEY1,2) >= '30' ) ) AS 'STATUS > 30' , ( SELECT count(*) FROM TABLE1 c1 WHERE ( c1.U_KEY3 NOT LIKE 'z%' AND LEFT(c1.U_KEY1,2) >= '10' ) ) AS 'STATUS > 10'

【问题讨论】:

  • TABLE1 上有索引吗?查询的运行速度可能比您想象的要快。
  • 表格有多少行?你试过用 JOIN 代替子查询吗?
  • @Tim U_KEY 字段被索引。表中现在只有 1200 行,运行良好,但有可能增长到 100,000 行。
  • @FLICKER 没有尝试加入。你是说加入表本身吗?
  • 我准备在这里犯错,但我认为改进的空间不大。 COUNT 查询必须扫描在 WHERE 子句之后保留的所有记录,而您实际上是在 5 个不同的查询中执行此操作。但是对于 10 万条记录,您真的关心性能吗?您希望多久在生产环境中运行一次此查询?

标签: tsql count subquery


【解决方案1】:

您可以使用CASE 语句将所有子查询合并到一个查询中:

SELECT
    SUM(CASE WHEN c1.U_KEY1 = '' OR c1.U_KEY1 IS NULL THEN 1 ELSE 0 END) AS 'STATUS IS EMPTY',
    SUM(CASE WHEN LEFT(c1.U_KEY1,2) >= '70'           THEN 1 ELSE 0 END) AS 'STATUS > 70',
    SUM(CASE WHEN LEFT(c1.U_KEY1,2) >= '50'           THEN 1 ELSE 0 END) AS 'STATUS > 50',
    SUM(CASE WHEN LEFT(c1.U_KEY1,2) >= '30'           THEN 1 ELSE 0 END) AS 'STATUS > 30',
    SUM(CASE WHEN LEFT(c1.U_KEY1,2) >= '10'           THEN 1 ELSE 0 END) AS 'STATUS > 10'
FROM TABLE1 c1
WHERE c1.U_KEY3 NOT LIKE 'z%'

但这可能没有单个子查询运行得那么快。

【讨论】:

  • 这应该更快,因为只有一个表/索引扫描。如果有一个带有 U_KEY1 和 U_KEY3 的索引,那将是带有一些流聚合的单个索引扫描。这并不可怕,具体取决于这两列的大小。
  • @siride 我知道这将是一次单表扫描,但CASE 语句对我来说看起来非常令人发指。感谢您的评论。
  • 它们是,但它们只是可以很快计算的表达式。该查询也可能是可并行的。
【解决方案2】:

我会这样转问题:

DECLARE @t TABLE (Id INT, U_Key1 VARCHAR(4) null);

INSERT INTO @t (id,U_Key1)
VALUES
(1,null),
(2,'902'),
(3,'452'),
(4,'401'),
(5,'103'),
(6,'359'),
(7,'335'),
(8,'772'),
(9,'143'),
(10,'222'),
(11,'664'),
(12,'992'),
(13,'122'),
(14,'332'),
(15,'421'),
(16,'622'),
(17,'982'),
(18,'1234'),
(19,null),
(20,'012');

WITH A AS (
    SELECT CAST(LEFT(U_Key1,2) AS INT) val FROM @t
), limits AS (
    SELECT  10 limitval, 'Status >= 10' limittext
    UNION ALL
    SELECT  30 , 'Status >= 30'
    UNION ALL
    SELECT  50 , 'Status >= 50'
    UNION ALL
    SELECT  70 , 'Status >= 70'

), Counts AS (
    SELECT 'Status is empty' Limittext, COUNT(id) Count FROM @t 
    WHERE U_Key1 IS null
    UNION ALL
    SELECT l.limittext, COUNT( A.val) Count FROM A
    CROSS JOIN limits l
    WHERE A.val >= l.limitval
    GROUP BY l.limittext
    )
    SELECT * FROM Counts

产生结果:

Status is empty 2
Status >= 10    17
Status >= 30    12
Status >= 50    6
Status >= 70    4

【讨论】:

  • 不错!谢谢@PeterO
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-03
  • 1970-01-01
相关资源
最近更新 更多