如何从有时已经包含 ID 但有时包含另一个 sql 查询的参数中获取 ID 列表

How to get a list of IDs from a parameter which sometimes includes the IDs already, but sometimes include another sql query

我在 SSMS-2017 中开发了一个 SQL 查询,如下所示:

DECLARE @property NVARCHAR(MAX) = @p;
SET @property = REPLACE(@property, '''', '');

DECLARE @propList TABLE (hproperty NUMERIC(18, 0));

IF CHARINDEX('SELECT', @property) > 0 OR CHARINDEX('select', @property) > 0
BEGIN
    INSERT INTO @propList
        EXECUTE sp_executesql @property;
END;
ELSE
BEGIN
    DECLARE @x TABLE (val NUMERIC(18, 0));

    INSERT INTO @x
        SELECT CONVERT(NUMERIC(18, 0), strval)
        FROM dbo.StringSplit(@property, ',');

    INSERT INTO @propList
        SELECT val
        FROM @x;
END;

SELECT ...columns...
FROM ...tables and joins...
WHERE ...filters...
  AND HMY IN (SELECT hproperty FROM @propList)

问题是,参数@p 的值可能是 ID 列表(示例:1,2,3,4)或直接 select 查询(示例:Select 来自 mytable 的 ID where code='A123').

如上所示,代码运行良好。然而,它会导致我们的系统出现问题(因为我们使用 Yardi7-Voyager),我们只需要将 select 语句保留为查询。为了管理它,我计划创建一个函数并在 where 子句中使用它,例如:

WHERE HMY IN (SELECT myFunction(@p))

但是我无法管理它,因为我看到我无法在 SQL 函数中执行动态查询。然后我被堆叠了。在这一点上处理这个问题的任何想法将不胜感激。

其他人指出,解决此问题的最佳方法是更改​​设计,我同意他们的看法。但是,我也想将您的问题视为学术问题并回答它,以防任何未来的读者在设计更改不会 possible/desirable.

的用例中遇到相同的问题

我可以想到两种方法,你可以在一个 select 中完成你正在尝试的事情,只要没有你没有提到的对你可以做的事情的其他限制然而。为了保持简短,我将给你 psuedo-code 可以适应你的情况以及未来读者的情况:

  1. OPENQUERY(或 OPENROWSET)

您可以将上面的代码合并到存储过程而不是函数中,因为存储过程确实允许动态 sql,这与函数不同。那么您应用中的 SELECT 查询将是 SELECT from OPENQUERY(Execute Your Stored Prodedure).

  1. 合并所有可能性。

我大约 99% 确定没有人会想要使用它,但我提到它是我所知道的学术完整。

第二种可能性只有在您的应用程序可能支持的可能查询数量有限且已知的情况下才有效。例如,您只能从 TableA 中获取 Properties,由 column1 过滤,或从 TableB 中,由 Column2 and/or 过滤Column3

可能不止这些可能性,但它必须是有限的、已知的数量,并且可能性越多,代码就会变得越复杂和冗长。

但如果是这种情况,您可以简单地从 UNION ALL 中的所有可能情况中 SELECT,并使 UNION ALL 中只有一个 SELECT 会 return 结果。

例如:

SELECT ... FROM TableA WHERE Column1=fnGetValue(@p, 'Column1')
AND CHARINDEX('SELECT', @property) > 0
AND CHARINDEX('TableA', @property) > 0
AND CHARINDEX('Column1', @property) > 0
AND (Whatever other filters are needed to uniquely identify this case)
UNION ALL 
SELECT
...

请注意 fnGetValue() 不是 built-in 函数。你必须写它。它会解析 @p 中的字符串,找到 'Column1=' 的位置,然后 return 之后的任何值。

在 UNION ALL 的末尾,您需要将最后一个 UNION ALL 添加到查询中,以处理用户传递 comma-separated 字符串而不是查询的情况,但这很容易,因为代码中填充 table 变量的所有步骤都是不必要的。您可以像这样简单地执行最终查询:

WHERE NOT CHARINDEX('SELECT', @p) > 0
AND HMY IN (SELECT strval FROM dbo.StringSplit(@p, ','))

我很确定这种可能性比它的价值要多得多,但它是一个例子,说明一般情况下,动态 SQL 可以用常规 SQL 替换,它只包含您希望动态 sql 能够处理的每个可能选项。