重写查询
Rewriting the query
我有一个包含以下示例数据的 table。 table 实际包含超过 1000 万行。
tableid
Id
type
1
1
su1
2
2
su1
3
2
su2
4
3
su3
5
4
su1
我必须计算所有只有 su1 类型的 ID。如果 id 有 su1 但还有另一种类型,那么它不应该被计算在内。这是我提出的查询。
Select Count(*) From (
Select id
From table t
Where exists (select null from table t1 where t.id = t1.id and t1.type = 'su1')
Group by id
Having Count(*) = 1) a
tableid 是主键。 Id 上有一个非聚集索引。还有其他写这个查询的方法吗?
我不完全确定你为什么要 Having Count(*) = 1
因为它似乎没有反映在要求中。
但是这个查询写成下面这样更好
SELECT COUNT(*)
FROM (
SELECT id
FROM [table] t
GROUP BY id
HAVING COUNT(CASE WHEN t1.type <> 'su1' THEN 1 END) = 0
) t;
为此,您需要以下索引
[table] (id) INCLUDE (type)
也许我遗漏了一些东西,但你不能在过滤掉除带 type='su1'
的项目之外的所有内容后执行 COUNT DISTINCT
。在那种情况下,我们只有:
WITH tbl (tableid, id, type) AS (
select * from values (1,1,'su1'), (2,2,'su1'), (3,2,'su2'), (4,3,'su3'), (5,4,'su1')
)
SELECT COUNT(DISTINCT id) FROM tbl
WHERE id NOT IN (SELECT id FROM tbl WHERE type != 'su1')
-- 2
这是 SQL Fiddle,我删除了 COUNT DISTINCT
这样您就可以看到单独的结果并且更容易检查 here。
鉴于此 table 和示例数据:
CREATE TABLE dbo.[table]
(
tableid int,
Id int,
type char(3),
INDEX IX_table CLUSTERED (Id, type)
);
INSERT dbo.[table](tableid, Id, type) VALUES
(1, 1, 'su1'),
(2, 2, 'su1'),
(3, 2, 'su2'),
(4, 3, 'su3'),
(5, 4, 'su1');
一种方法是:
;WITH agg AS
(
SELECT tableid, Id, type,
mint = MIN(Type) OVER (PARTITION BY Id),
maxt = MAX(Type) OVER (PARTITION BY Id)
FROM dbo.[table]
)
SELECT tableid, Id, type
FROM agg
WHERE mint = maxt AND mint = 'su1';
如果您的聚簇索引在 Id, type
上,这将允许进行单个聚簇索引扫描:
虽然有些我们可能不想要的线轴有点乱。 David 的建议如何(假设您使用的是 SQL Server 2017 或更高版本):
SELECT tableid = MIN(tableid), Id
FROM dbo.[table]
GROUP BY Id
HAVING STRING_AGG(type, ',') = 'su1';
哦,是的,好多了:
- 示例 db<>fiddle
我有一个包含以下示例数据的 table。 table 实际包含超过 1000 万行。
tableid | Id | type |
---|---|---|
1 | 1 | su1 |
2 | 2 | su1 |
3 | 2 | su2 |
4 | 3 | su3 |
5 | 4 | su1 |
我必须计算所有只有 su1 类型的 ID。如果 id 有 su1 但还有另一种类型,那么它不应该被计算在内。这是我提出的查询。
Select Count(*) From (
Select id
From table t
Where exists (select null from table t1 where t.id = t1.id and t1.type = 'su1')
Group by id
Having Count(*) = 1) a
tableid 是主键。 Id 上有一个非聚集索引。还有其他写这个查询的方法吗?
我不完全确定你为什么要 Having Count(*) = 1
因为它似乎没有反映在要求中。
但是这个查询写成下面这样更好
SELECT COUNT(*)
FROM (
SELECT id
FROM [table] t
GROUP BY id
HAVING COUNT(CASE WHEN t1.type <> 'su1' THEN 1 END) = 0
) t;
为此,您需要以下索引
[table] (id) INCLUDE (type)
也许我遗漏了一些东西,但你不能在过滤掉除带 type='su1'
的项目之外的所有内容后执行 COUNT DISTINCT
。在那种情况下,我们只有:
WITH tbl (tableid, id, type) AS (
select * from values (1,1,'su1'), (2,2,'su1'), (3,2,'su2'), (4,3,'su3'), (5,4,'su1')
)
SELECT COUNT(DISTINCT id) FROM tbl
WHERE id NOT IN (SELECT id FROM tbl WHERE type != 'su1')
-- 2
这是 SQL Fiddle,我删除了 COUNT DISTINCT
这样您就可以看到单独的结果并且更容易检查 here。
鉴于此 table 和示例数据:
CREATE TABLE dbo.[table]
(
tableid int,
Id int,
type char(3),
INDEX IX_table CLUSTERED (Id, type)
);
INSERT dbo.[table](tableid, Id, type) VALUES
(1, 1, 'su1'),
(2, 2, 'su1'),
(3, 2, 'su2'),
(4, 3, 'su3'),
(5, 4, 'su1');
一种方法是:
;WITH agg AS
(
SELECT tableid, Id, type,
mint = MIN(Type) OVER (PARTITION BY Id),
maxt = MAX(Type) OVER (PARTITION BY Id)
FROM dbo.[table]
)
SELECT tableid, Id, type
FROM agg
WHERE mint = maxt AND mint = 'su1';
如果您的聚簇索引在 Id, type
上,这将允许进行单个聚簇索引扫描:
虽然有些我们可能不想要的线轴有点乱。 David 的建议如何(假设您使用的是 SQL Server 2017 或更高版本):
SELECT tableid = MIN(tableid), Id
FROM dbo.[table]
GROUP BY Id
HAVING STRING_AGG(type, ',') = 'su1';
哦,是的,好多了:
- 示例 db<>fiddle