在 SQL 中替代 'Except' 的表现
Alternate to 'Except' in SQL with performance
我有一个 table 表 A,如 MS-Sql
TrId Status
2345 3
567 3
567 0
2345 0
99 3
778 0
很少有 TrId 的状态为 3 和 0,有些只有 3,有些只有 0。我需要找到状态仅为 3 的 TrId。
其中一种方法是:
Select TrnId From TableA Where flgStatus = 3
EXCEPT
Select TrnId From Tablea Where flgStatus = 0
有超过 1 亿条记录,我没有足够的时间 window 除了,如果有任何替代方案,我们将不胜感激。
EXCEPT
或 MINUS
在这里是正确的。然而,在非常大的 table 上,它并不是最优的。
另一种选择是这样
SELECT *
FROM TableA
WHERE flgStatus = 3
AND TrnId NOT IN
(SELECT TrnId From TableA Where flgStatus = 0)
或者更好,使用 LEFT JOIN
和 IS NULL
来避免性能杀手 NOT
:
SELECT *
FROM TableA T3
LEFT JOIN TableA T0 ON T3.TrnId = T0.TrnId AND T0.flgStatus = 0
WHERE T3.flgStatus = 3
AND T0.TrnId IS NULL
编辑:NOT EXISTS
Igor 的解决方案也是一个很好的方法
您可以使用NOT EXISTS
SELECT *
FROM TableA a
WHERE flgStatus = 3
AND NOT EXISTS
(SELECT TrnId From TableA b Where flgStatus = 0 AND a.TrnId = b.TrnId)
这通常比 NOT IN
具有更好的性能。一个好的替代方法是加入,请参阅@ThomasG 的回答。
组合(TrnId,flgStatus)
是否独特?
然后你可能会切换到 EXCEPT ALL
,类似于 UNION ALL
,这可能比 UNION
更有效,因为它避免了 DISTINCT 操作。
另一种只访问基础 table 一次的解决方案:
Select TrnId
From TableA Where flgStatus in (0,3)
group by TrnId
having MIN(flgStatus) = 3
我会使用一个简单的 group by
:
select trnid
from tablea
group by trnid
having min(status) = max(status) and min(status) = 3;
这是否更快取决于几件事。 . .特别是您是否要删除重复项以及您对数据有哪些索引。 NOT EXISTS
如果您不关心重复项,可能会更快,但消除重复项需要工作。
对于像您这样的大型数据集,使用以下查询可能会以合理的性能提供您想要的结果 -
SELECT ta1.TrId AS TrId
FROM dbo.TableA AS ta1
LEFT JOIN dbo.TableA AS ta2 ON (ta2.TrId = ta1.TrId AND ta2.[Status] != 3)
WHERE ta2.TrId IS NULL;
首先,自联接通过将所有状态(3 或 0、1 等)排列在同一行中来创建 table。过滤器
ta2.[Status] != 3
如果状态为 3,则在连接子句中为 ta2.TrId(或 ta2.*)设置 NULL。
+------+--------+------+--------+
| TrId | Status | TrId | Status |
+------+--------+------+--------+
| 2345 | 3 | 2345 | 0 |
| 567 | 3 | 567 | 0 |
| 567 | 0 | 567 | 0 |
| 2345 | 0 | 2345 | 0 |
| 99 | 3 | NULL | NULL |
| 778 | 0 | 778 | 0 |
+------+--------+------+--------+
然后将以下过滤器用于 select 出现 NULL 的行。
WHERE ta2.TrId IS NULL
因为它是自 LEFT JOIN,左侧 table 具有所有行,但右侧 table 值不满足连接条件的 NULL。
我有一个 table 表 A,如 MS-Sql
TrId Status
2345 3
567 3
567 0
2345 0
99 3
778 0
很少有 TrId 的状态为 3 和 0,有些只有 3,有些只有 0。我需要找到状态仅为 3 的 TrId。
其中一种方法是:
Select TrnId From TableA Where flgStatus = 3
EXCEPT
Select TrnId From Tablea Where flgStatus = 0
有超过 1 亿条记录,我没有足够的时间 window 除了,如果有任何替代方案,我们将不胜感激。
EXCEPT
或 MINUS
在这里是正确的。然而,在非常大的 table 上,它并不是最优的。
另一种选择是这样
SELECT *
FROM TableA
WHERE flgStatus = 3
AND TrnId NOT IN
(SELECT TrnId From TableA Where flgStatus = 0)
或者更好,使用 LEFT JOIN
和 IS NULL
来避免性能杀手 NOT
:
SELECT *
FROM TableA T3
LEFT JOIN TableA T0 ON T3.TrnId = T0.TrnId AND T0.flgStatus = 0
WHERE T3.flgStatus = 3
AND T0.TrnId IS NULL
编辑:NOT EXISTS
Igor 的解决方案也是一个很好的方法
您可以使用NOT EXISTS
SELECT *
FROM TableA a
WHERE flgStatus = 3
AND NOT EXISTS
(SELECT TrnId From TableA b Where flgStatus = 0 AND a.TrnId = b.TrnId)
这通常比 NOT IN
具有更好的性能。一个好的替代方法是加入,请参阅@ThomasG 的回答。
组合(TrnId,flgStatus)
是否独特?
然后你可能会切换到 EXCEPT ALL
,类似于 UNION ALL
,这可能比 UNION
更有效,因为它避免了 DISTINCT 操作。
另一种只访问基础 table 一次的解决方案:
Select TrnId
From TableA Where flgStatus in (0,3)
group by TrnId
having MIN(flgStatus) = 3
我会使用一个简单的 group by
:
select trnid
from tablea
group by trnid
having min(status) = max(status) and min(status) = 3;
这是否更快取决于几件事。 . .特别是您是否要删除重复项以及您对数据有哪些索引。 NOT EXISTS
如果您不关心重复项,可能会更快,但消除重复项需要工作。
对于像您这样的大型数据集,使用以下查询可能会以合理的性能提供您想要的结果 -
SELECT ta1.TrId AS TrId
FROM dbo.TableA AS ta1
LEFT JOIN dbo.TableA AS ta2 ON (ta2.TrId = ta1.TrId AND ta2.[Status] != 3)
WHERE ta2.TrId IS NULL;
首先,自联接通过将所有状态(3 或 0、1 等)排列在同一行中来创建 table。过滤器
ta2.[Status] != 3
如果状态为 3,则在连接子句中为 ta2.TrId(或 ta2.*)设置 NULL。
+------+--------+------+--------+
| TrId | Status | TrId | Status |
+------+--------+------+--------+
| 2345 | 3 | 2345 | 0 |
| 567 | 3 | 567 | 0 |
| 567 | 0 | 567 | 0 |
| 2345 | 0 | 2345 | 0 |
| 99 | 3 | NULL | NULL |
| 778 | 0 | 778 | 0 |
+------+--------+------+--------+
然后将以下过滤器用于 select 出现 NULL 的行。
WHERE ta2.TrId IS NULL
因为它是自 LEFT JOIN,左侧 table 具有所有行,但右侧 table 值不满足连接条件的 NULL。