SQL Server 中的 IN 结果是否有任何限制?

Unn*_*med 8 sql-server t-sql

IN过滤器可以处理的内容有没有限制?例如:

SELECT Name
FROM People
WHERE Job IN (All the values goes here)
Run Code Online (Sandbox Code Playgroud)

IN 的Microsoft 文档说:

“在括号内显式包含大量值(以逗号分隔的数千个值),在 IN 子句中会消耗资源并返回错误 8623 或 8632。要解决此问题,请将 IN 列表中的项目存储在一个表,并在 IN 子句中使用 SELECT 子查询。”

但是否有任何确切或近似的数字

数以千计的值

Aar*_*and 12

这取决于。严重地。这就是为什么文档不能具体说明您会注意到环境退化的地方。

解决方案是停止这样做(并担心它)并使用表值参数

如果您在 C# 中有这样的值,您可以将逗号分隔的列表构建为字符串以在查询中连接在一起,那么您在 C# 中的值是这样的,您可以将它们填充到 DataTable 或其他结构(也许这就是它们最初来自的地方),并将该结构作为参数传递给存储过程。

  • 数据类型、实际值的大小、整体文本的大小、网络速度、并发性,......“唯一可能的修复”是什么意思 - 修复什么? (3认同)
  • 如果您有 C# 中的值,您可以将它们放入数据表中,并将其作为参数传递给存储过程。 (3认同)
  • @Joshua 我觉得如果你达到了那种限制,可能有更好的方法。用户不是选择 30,000 个单独的项目,他们是选择整个类别,还是经理的所有下属,或制造过程中的所有零件,或者只是在大量下拉菜单中选择“全选”?或者所有东西*除了*少数物品?似乎您可以以不同的方式处理这些情况,而不是将 30,000 个项目*以任何形式*扔到数据库中。 (3认同)

Joe*_*ish 8

严格来说,您可以保证查询失败并显示 65536 个值。话虽如此,我认为在实践中将 32768 视为上限是相当安全的,但这并不是最小的上限。最小上限取决于查询中发生的其他事情和其他本地因素。你可以通过一个简单的例子看到这一点。首先将 100k 行放入堆中:

DROP TABLE IF EXISTS dbo.Q228695;

CREATE TABLE dbo.Q228695 (ID BIGINT);

INSERT INTO dbo.Q228695 WITH (TABLOCK)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);
Run Code Online (Sandbox Code Playgroud)

假设我想在 IN 子句中放入 32768 个值以进行以下形式的查询:SELECT COUNT(*) FROM dbo.Q228695 WHERE ID IN ();这在 SQL Server 2017 中很容易做到STRING_AGG

DECLARE @nou VARCHAR(MAX);

SELECT @nou = 'SELECT COUNT(*) FROM dbo.Q228695 WHERE ID IN (' + STRING_AGG(CAST(RN AS VARCHAR(MAX)), ',') + ')'
FROM
(
    SELECT TOP (32768) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
    FROM master..spt_values t1
    CROSS JOIN master..spt_values t2
) q
OPTION (MAXDOP 1);

EXEC (@nou);
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

消息 8632,级别 17,状态 2,第 1 行

内部错误:已达到表达式服务限制。请在您的查询中寻找潜在的复杂表达式,并尝试简化它们。

如果我从IN子句中删除一个值,查询将在 24 秒后成功。对于这个非常简单的查询,32767 是仍允许执行查询的最大值数。请注意,有关错误 8632 的 Microsoft 文档说明如下:

出现此问题的原因是 SQL Server 限制了可以包含在查询的单个表达式中的标识符和常量的数量。此限制为 65,535。

32767 有效,32768 无效。我不认为 65535/2 = 32767.5 是巧合。无论出于何种原因,观察到的行为是IN子句计为两个,达到极限。

我确实认为这是一个错误的问题。如果我将这些相同的值放入临时表中,则查询将在 0 秒内执行:

DROP TABLE IF EXISTS #t;

CREATE TABLE #t (ID BIGINT);

INSERT INTO #t WITH (TABLOCK)
SELECT TOP (32768) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);

SELECT COUNT(*)
FROM dbo.Q228695
WHERE ID IN (
    SELECT t.ID
    FROM #t t
);
Run Code Online (Sandbox Code Playgroud)

即使您的查询没有抛出错误,一旦将太多值放入IN子句中,您也会付出沉重的性能代价。